pax_global_header00006660000000000000000000000064146544653050014526gustar00rootroot0000000000000052 comment=3e80feee3ef81d5ab48b7d03828726c5ed0f08f1 backintime-1.5.2/000077500000000000000000000000001465446530500136415ustar00rootroot00000000000000backintime-1.5.2/.codespellrc000066400000000000000000000023441465446530500161440ustar00rootroot00000000000000[codespell] # Folders and files to skip skip = .codespellrc,*.po,Makefile,*.desktop,.git,__pycache__,*.pyc,languages.py,_build,TRANSLATIONS,./doc/manual/html # 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 # 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$ backintime-1.5.2/.github/000077500000000000000000000000001465446530500152015ustar00rootroot00000000000000backintime-1.5.2/.github/ISSUE_TEMPLATE.md000066400000000000000000000011461465446530500177100ustar00rootroot00000000000000To help us diagnose the problem quickly, please provide the output of the console command `backintime --diagnostics`. Additionally, please specify as precisely as you can the package or installation source where you got Back In Time from. Sometimes there are multiple alternatives, like in [for Arch-based distros](https://aur.archlinux.org/packages?K=backintime). As an alternative fell free to use our mailing list for every topic about Back In Time. Visit the subscription page at https://mail.python.org/mailman3/lists/bit-dev.python.org or send an email with subject "Subscribe" to bit-dev-join@python.org. backintime-1.5.2/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000001001465446530500207710ustar00rootroot00000000000000Don't forget the changelog entry! ;) Did you run "codespell"? backintime-1.5.2/.gitignore000066400000000000000000000014061465446530500156320ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # editor backup and temp files *~ *.sic *.swp \#*\# .\#* # packaging *.deb debian/debhelper-build-stamp debian/*.debhelper debian/*.debhelper.log debian/*.substvars debian/files debian/backintime-common debian/backintime-gnome debian/backintime-kde debian/backintime-kde4 debian/backintime-notify debian/backintime-qt # compressed files *.gz # Compiled translations *.mo # Makefile common/Makefile qt/Makefile # coveralls.io status .coverage # 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 # PyCharm IDE project settings .idea # Linter configuration .flake8 backintime-1.5.2/.readthedocs.yaml000066400000000000000000000017271465446530500170770ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # 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 backintime-1.5.2/.travis.yml000066400000000000000000000034561465446530500157620ustar00rootroot00000000000000# TravisCI (https://travis-ci.org) configuration file os: linux # Support End of "Focal" (20.04 LTS) is April 2025 # dist: focal # Support End of "Jammy" (22.04 LTS) is April 2027 dist: jammy language: python arch: - amd64 python: - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" addons: # add localhost to known_hosts to prevent ssh unknown host prompt during unit tests ssh_known_hosts: localhost 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 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 libdbus-1-dev # jobs: # exclude: # - python: "3.9" # - python: "3.10" # - python: "3.11" # - python: "3.12" install: - pip install -U pip - pip install pylint coveralls 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 # start ssh-agent so that we can add private keys - eval `ssh-agent -s` script: # compile all files - ensure that syntax is correct - python -m compileall common common/test common/plugins qt qt/test qt/plugins # run unit tests - ensure that functionality is correct - cd common - ./configure --python=python3 - make unittest-v - cd .. - cd qt - ./configure --python=python3 - make - pytest after_success: - coverage combine - coveralls backintime-1.5.2/AUTHORS000066400000000000000000000003421465446530500147100ustar00rootroot00000000000000Oprea Dan () Bart de Koning () Richard Bailey () Germar Reitze () Taylor Raack () Christian Buhtz Michael Büker Jürgen Altfeld backintime-1.5.2/CHANGES000066400000000000000000001710561465446530500146460ustar00rootroot00000000000000Back In Time Version 1.5.2 (2024-08-06) * Fix: Ensure crontab with ending newline (#781) * Fix(translation): Correct corrupt translated strings in Basque, Islandic and Spanish causing application crashes (#1828) * Build(translation): Language helper script processing syntax checks on po-files Version 1.5.1 (2024-07-27) * Fix: Use correct port to ping SSH Proxy (#1815) Version 1.5.0 (2024-07-26) * Dependency: Migration to PyQt6 * Breaking Change: EncFS deprecation warning (#1735, #1734) * Breaking Change: GUI started with --debug does no longer add --debug to the crontab for scheduled profiles. Use the new "enable logging for debug messages" in the 'Schedule' section of the 'Manage profiles' GUI instead. * Feature: Warn if Cron is not running (#1747) * Feature: Profile and GUI allow to activate debug output for scheduled jobs by adding '--debug' to crontab entry (#1616, contributed by @stcksmsh Kosta Vukicevic) * Feature: Support SSH proxy (jump) host (#1688) (@cgrinham, Christie Grinham) * Feature: Support rsync '--one-file-system' in Expert Options (#1598) * Feature: "*-dev" version strings contain last commit hash (#1637) * Fix: Global flock fallback to single-user mode if insufficient permissions (#1743, #1751) * Fix: Fix Qt segmentation fault with uninstall ExtraMouseButtonEventFilter when closing main window (#1095) * Fix: Names of weekdays and months translated correct (#1729) * Fix: Global flock for multiple users (#1122, #1676) * Fix bug: "Backup folders" list does reflect the selected snapshot (#1585) (@rafaelhdr Rafael Hurpia da Rocha) * Fix: Validation of diff command settings in compare snapshots dialog (#1662) (@stcksmsh Kosta Vukicevic) * Fix bug: Open symlinked folders in file view (#1476) * Fix bug: Respect dark mode using color roles (#1601) * Fix: "Highly recommended" exclusion pattern in "Manage Profile" dialog's "Exclude" tab show missing only (#1620) * Fix bug: `make install` ignored $(DEST) in file migration part (#1630) * Removed: Context menu in LogViewDialog (#1578) * Removed: Field "filesystem_mount" and "snapshot_version" in "info" file (#1684) * Refactor: Replace Config.user() with getpass.getuser() (#1694) * Chore!: Remove "debian" folder (#1548) * Build: Enable several PyLint rules (#1755, #1766) * Build: Add AppStream meta data (#1642) * Build: PyLint unit test is skipped if PyLint isn't installed, but will always run on TravisCI (#1634) * Build: Git commit hash is presevered while "make install" (#1637) * Build: Fix bash-completion symlink creation while installing & adding --diagnostics (#1615) * Build: TravisCI use PyQt (except arch "ppc64le") Version 1.4.3 (2024-01-30) * Feature: Exclude 'SingletonLock' and 'SingletonCookie' (Discord) and 'lock' (Mozilla Firefox) files by default (part of #1555) * Work around: Relax `rsync` exit code 23: Ignore instead of error now (part of #1587) * Feature (experimental): Add new snapshot log filter `rsync transfer failures (experimental)` to find them easier (they are normally not shown as "error"). This feature is experimental because it is based on hard-coded error message strings in the rsync source code and may possibly not find all rsync messages or show false positives. * Fix bug: 'qt5_probing.py' hangs when BiT is run as root and no user is logged into a desktop environment (#1592 and #1580) * Fix bug: Launching BiT GUI (root) hangs on Wayland without showing the GUI (#836) * Improve: Launcher for BiT GUI (root) does not enforce Wayland anymore but uses same settings as for BiT GUI (userland) (#1350) * Fix bug: 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) * 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) * Fix bug: RTE: module 'qttools' has no attribute 'initate_translator' with encFS when prompting the user for a password (#1553). * Fix bug: Schedule dropdown menu used "minutes" instead of "hours". * Fix bug: Unhandled exception "TypeError: 'NoneType' object is not callable" in tools.py function __log_keyring_warning (#820). Logging thread removed and logger module correctly initialized as fix. Is "Heisenbug" so 100 % retesting was not possible. * 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) * Build: Install dbus configuration file to /usr/share not /etc (#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) * Translation: Minor modifications in source strings and updating language files. * Refactor: Solved circular dependency between tools.py and logger.py to fix #820 * Improved: qtsystrayicon.py, qt5_probing.py, usercallbackplugin.py and all parts of app.py do now also use "backintime" as logging namespace in the syslog to ensure complete log output with `journalctl | grep -i backintime` Version 1.4.1 (2023-10-01) * Dependency: Add "qt translations" to GUI runtime dependencies (#1538). * Build: Unit tests do generically ignore all instead of well-known warnings now (#1539). * Build: Warnings about missing Qt translation now are ignored while testing (#1537). * Fix bug: GUI didn't start when "show hidden files" button was on (#1535). Version 1.4.0 (2023-09-14) * Project: Renamed branch "master" to "main" and started "gitflow" branching model. * Refactor: Renamed qt4plugin.py to systrayiconplugin.py (we are using Qt5 for years now ;-) * Refactor: Removed unfinished feature "Full system backup" (#1526) * Fix bug: AttributeError: can't set attribute 'showHiddenFiles' in app.py (#1532) * Fix bug: Check SSH login works on machines with limited commands (#1442) * Fix bug: Missing icon in SSH private key button (#1364) * Fix bug: Master issue for missing or empty system-tray icon (#1306) * Fix bug: System-tray icon missing or empty (GUI and cron) (#1236) * Fix bug: Improve KDE plasma icon compatibility (#1159) * GUI Change: View last (snapshot) log button in GUI uses "document-open-recent" icon now instead of "document-new" (#1386) * Fix bug: Unit test fails on some machines due to warning "Ignoring XDG_SESSION_TYPE=wayland on Gnome..." (#1429) * Fix bug: Generation of config-manpage caused an error with Debian's Lintian (#1398). * Fix bug: Return empty list in smartRemove (#1392, Debian Bug Report 973760) * Fix bug: Taking a snapshot reports `rsync` errors now even if no snapshot was taken (#1491) * Fix bug: takeSnapshot() recognizes errors now by also evaluating the rsync exit code (#489) Fixes related problem: Killing `rsync` was not handled gracefully (by ignoring the rsync exit code) * Fix bug: The error user-callback is now always called if an error happened while taking a snapshot (#1491) * Fix bug: D-Bus serviceHelper error "LimitExceeded: Maximum length of command line reached (100)": Max command length is now 120 instead of 100 (#1027) * Feature: Introduce new error codes for the "error" user callback (as part of #1491): 5: Error while taking a snapshot. 6: New snapshot taken but with errors. * Feature: The `rsync` exit code is now contained in the snapshot log (part of #489). Example: [E] Error: 'rsync' ended with exit code -9 (negative values are signal numbers, see 'kill -l') * Fix bug: Treat rsync exit code 24 as INFO instead of ERROR (#1506) * Breaking change: Minimal Python version 3.8 required (#1358). * Removed: Handling and checking of user group "fuse" (#1472). * Feature: Exclude /swapfile by default (#1053) * Feature: Rearranged menu bar and its entries in the main window (#1487, #1478). * Feature: Configure user interface language via config file and GUI. * Documentation: Removed outdated docbook (#1345). * Build: Introduced .readthedocs.yaml as asked by ReadTheDocs.org (#1443). * Dependency: The oxygen icons should be installed with the BiT Qt GUI since they are used as fallback in case of missing icons * Fix bug: Add support for ChainerBackend class as keyring which iterates over all supported keyring backends (#1410) * Translation: Strings to translate now easier to understand for translators (#1448, #1457, #1462, #1465). * Translation: Improved completeness of translations and additional modifications of source strings (#1454, #1512) * Translation: Plural forms support (#1488). * Removed: Translation in Canadian English, British English and Javanese (#1455). * Added: Translation in Persian and Vietnamese (#1460). * Added: Message to users (after 10 starts of BIT Gui) to motivate them contributing translations (#1473). Version 1.3.3 (2023-01-04) * Feature: New command line argument "--diagnostics" to show helpful info for better issue support (#1100) * GUI change: Remove Exit button from the toolbar (#172) * GUI change: Define accelerator keys for menu bar and tabs, as well as toolbar shortcuts (#1104) * Desktop integration: Update .desktop file to mark Back In Time as a single main window program (#1258) * Feature: Write all log output to stderr; do not pollute stdout with INFO and WARNING messages anymore (#1337) * Fix bug: RTE "reentrant call inside io.BufferedWriter" in logFile.flush() during backup (#1003) * Fix bug: Incompatibility with rsync 3.2.4 or later because of rsync's "new argument protection" (#1247). Deactivate "--old-args" rsync argument earlier recommended to users as a workaround. * Fix bug: DeprecationWarnings about invalid escape sequences. * Fix bug: AttributeError in "Diff Options" dialog (#898) * Fix bug: Settings GUI: "Save password to Keyring" was disabled due to "no appropriate keyring found" (#1321) * Fix bug: Back in Time did not start with D-Bus error "dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NameHasNoOwner: Could not get owner of name 'net.launchpad.backintime.serviceHelper': no such name" (fixes client-side part of #921 - system D-Bus part of the Udev serviceHelper is still under investigation). * Fix bug: Avoid logging errors while waiting for a target drive to be mounted (#1142, #1143, #1328) * Fix bug: [Arch Linux] AUR pkg "backintime-git": Build tests fails and installation is aborted (#1233, fixed with #921) * Fix bug: Wrong systray icon showing in Wayland (#1244) * Documentation update: Correct description of profile.schedule.time in backintime-config manpage (#1270) * Translation update: Brazilian Portuguese (#1267) * Translation update: Italian (#1110, #1123) * Translation update: French (#1077) * Testing: Fix a test fail when dealing with an empty crontab (#1181) * Testing: Fix a test fail when dealing with an empty config file (#1305) * Testing: Skip "test_quiet_mode" (does not work reliably) * Testing: Improve "test_diagnostics_arg" (introduced with #1100) to no longer fail when JSON output was mixed with logging output (part of #921, fixes #1233) * Testing: Numerous fixes and extensions to testing (#1115, #1213, #1279, #1280, #1281, #1285, #1288, #1290, #1293, #1309, #1334) Version 1.3.2 (2022-03-12) * Fix bug: Tests no longer work with Python 3.10 (https://github.com/bit-team/backintime/issues/1175) Version 1.3.1 (2021-07-05) * bump version, forgot to push branch to Github before releasing Version 1.3.0 (2021-07-04) * 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 available (https://github.com/bit-team/backintime/issues/1030) Version 1.2.1 (2019-08-25) * 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 (2019-04-27) * 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) * clarify '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 writable (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; missing 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 numerical 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 ignore 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.24 (2017-11-07) * fix critical bug: CVE-2017-16667: shell injection in notify-send (https://github.com/bit-team/backintime/issues/834) Version 1.1.22 (2017-10-28) * fix bug: stat free space for snapshot folder instead of backintime folder (https://github.com/bit-team/backintime/issues/552733) * backport bug fix: backintime root crontab doesn't run; missing line-feed 0x0A on last line (https://github.com/bit-team/backintime/issues/552781) * backport bug fix: can't open files with spaces in name (https://github.com/bit-team/backintime/issues/552552) Version 1.1.20 (2017-04-09) * backport bug fix: CVE-2017-7572: polkit CheckAuthorization: race condition in privilege authorization Version 1.1.18 (2017-03-29) * Fix bug: manual snapshots from GUI didn't work (https://github.com/bit-team/backintime/issues/728) Version 1.1.16 (2017-03-28) * backport bug fix: start a new ssh-agent instance only if necessary (https://github.com/bit-team/backintime/issues/722) * Fix bug: OSError when running backup-job from systemd (https://github.com/bit-team/backintime/issues/720) Version 1.1.14 (2017-03-05) * backport bug fix: udev schedule not working (https://github.com/bit-team/backintime/issues/605) * backport bug fix: Keyring doesn't work with KDE Plasma5 (https://github.com/bit-team/backintime/issues/545) * backport bug fix: nameError in tools.make_dirs (https://github.com/bit-team/backintime/issues/622) * backport bug fix: use current folder if no file is selected in files view * 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) Version 1.1.12 (2016-01-11) * 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 (2016-01-09) * 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/grayed 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: create 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 (2015-09-28) * 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 gray 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 compatibility 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 (2015-06-27) * 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 (2015-03-22) * 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 unresponsive (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 plugged 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: UnicodeError thrown if filename has broken charset (https://launchpad.net/bugs/1419694) * use 'crontab' instead of 'crontab -' to read from stdin (https://launchpad.net/bugs/1419466) Version 1.1.2 (2015-02-04) * 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 (2015-01-15) * 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 confusing 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 (2014-11-02) * 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 (2014-10-01) * 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 (2014-08-06) * 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 (2013-12-21) * 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 (2013-12-13) * Fix bug: cron scheduled snapshots won't start with 1.0.30 Version 1.0.30 (2013-12-12) * 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; reorganize 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 doesn'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 (2013-10-19) * 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 (2013-09-07) * 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. EXPERIMENTAL! * add 'Local encrypted': mount encfs Version 1.0.24 (2013-05-08) * 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 separate 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 (2013-03-26) * 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 (2012-12-15) * Fix bug: restore remote path with spaces using mode ssh returned error Version 1.0.18 (2012-11-17) * 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 (2012-11-15) * Fix a package dependency problem ... this time for good (https://bugs.launchpad.net/backintime/+bug/1077446) Version 1.0.14 (2012-11-09) * Fix a package dependency problem Version 1.0.12 (2012-11-08) * 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 (2012-03-06) * Add "Restore to ..." in replacement of copy (with or without drag & drop) because copy don't restore user/group/rights Version 1.0.8 (2011-06-18) * 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 (2011-01-02) * 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 (2010-10-28) * 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 (2010-10-16) * reduce log file (no more duplicate "Compare with..." lines) * declare backintime-kde4 packages as a replacement of backintime-kde Version 1.0 (2010-10-16) * 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 information * 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 (2009-05-19) * 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 (2009-05-07) * 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 (2009-04-27) * fix French translation Version 0.9.22 (2009-04-24) * 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 (2009-04-06) * 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 (2009-04-02) * 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 (2009-03-16) * fix a bug/crush for French version Version 0.9.16 (2009-03-13) * 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 (2009-03-05) * 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 (2009-02-28) * 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 (2009-02-24) * 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 (2009-02-20) * 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 multiple 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 (2009-02-09) * 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 (2009-01-30) * 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 (2009-01-16) * 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 (2009-01-09) * 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 (2008-12-22) * 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 (2008-12-17) * 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 (2008-12-11) * add Drag & Drop from MainWindow:FileView/SnapshotsDialog to Nautilus * update German translation (Michael Wiedmann ) Version 0.8.14 (2008-12-07) * 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 (2008-12-01) * add German translation (Michael Wiedmann ) * add SnapshotNameDialog * add Name/Remove snapshot in main toolbar * change the way it detects if the mainwindow is the active window (no dialogs) * toolbars: show icons only * update Spanish translation (Francisco Manuel García Claramonte ) Version 0.8.10 (2008-11-22) * 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 (2008-11-19) * SnapshotsDialog: add diff * update Spanish translation (Francisco Manuel García Claramonte ) Version 0.8.6 (2008-11-17) * fix change backup path crush * add SnapshotsDialog Version 0.8.2 (2008-11-14) * 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 (2008-11-10) * add every 5/10 minutes automatic backup Version 0.8 (2008-11-07) * 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 running 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 (2008-11-03) * if there is already a GUI instance running raise it * add Spanish translation (Francisco Manuel García Claramonte ) Version 0.7.2 (2008-10-28) * better integration with gnome icons (use mime-types) * remember last path * capitalize month in timeline (bug in french translation) Version 0.7 (2008-10-22) * fix cron segfault * fix a crush when launched the very first time (not configured) * multi-lingual support * add French translation Version 0.6.4 (2008-10-20) * remove About & Settings dialogs from the pager * allow only one instance of the application Version 0.6.2 (2008-10-16) * remember window position & size Version 0.6 (2008-10-13) * 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 (2008-10-10) * add size & date columns in files view * changed some texts Version 0.5 (2008-10-03) * This is the first release. backintime-1.5.2/CONTRIBUTING.md000066400000000000000000000364221465446530500161010ustar00rootroot00000000000000# How to contribute to _Back In Time_ 😊 **Thanks for taking the time to contribute!** 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. # Index - [Quick guide](#quick-guide) - [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) - [Build own `deb` file](#build-own-deb-file) - [Testing](#testing) - [SSH](#SSH) - [What happens after you opened a Pull Request (PR)?](#what-happens-after-you-opened-a-pull-request-PR) - [Strategy Outline](#strategy-outline) - [Licensing of contributed material](#licensing-of-contributed-material) # Quick guide > [!IMPORTANT] > 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/de/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 --branch 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 on 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/de/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork). # Best practice and recommendations Please take the following best practices into account if possible. This will reduce the work load of the maintainers and to increase the chance that your pull request is accepted. - Follow [PEP 8](https://peps.python.org/pep-0008/) as a minimal Style Guide for Python Code. - 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"`). - For docstrings follow [Google Style Guide](https://sphinxcontrib-napoleon.readthedocs.org/en/latest/example_google.html) (see our own [HOWTO about doc generation](common/doc-dev/1_doc_maintenance_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. You can run them via `make`-system with `cd common && ./configure && make && make test` or using a regular unittest runner of your choice (e.g. `pytest`). See section [Build and install via `make` system](#build-and-install-via-make-system-recommended) 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 follow the _Classical (aka Detroit) school_ instead of _London (aka mockist) school_. # Resources & Further readings - [Mailing list _bit-dev_](https://mail.python.org/mailman3/lists/bit-dev.python.org/) - [Source code documentation for developers](https://backintime-dev.readthedocs.org) - [Translations](https://translate.codeberg.org/engage/backintime) are done on a separate platform. - [HowTo's and maintenance](common/doc-dev/README.md) - [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 Ubuntu. 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. Be aware that some of the named packages can be replaced with PyPi packages. * Runtime dependencies for the CLI - `python3` (>= 3.8) - `rsync` - `cron-daemon` - `openssh-client` - `sshfs` - `python3-keyring` - `python3-dbus` - `python3-packaging` - Recommended - `encfs` * Runtime dependencies for the GUI - `x11-utils` - `python3-pyqt6` - `python3-dbus.mainloop.pyqt6` - `libnotify-bin` - `policykit-1` - `qttranslations6-l10n` - `qtwayland6` (if Wayland is used as display server instead of X11) - Recommended - For SSH key storage **one** of these packages - `python3-secretstorage` - `python3-keyring-kwallet` - `python3-gnomekeyring` - For diff-like comparing files between backup snapshots **one** of these packages - `kompare` - or `meld` - Optional: Default icons - The `oxygen` icons should be offered as optional dependency since they are used as fallback in case of missing icons (mainly app and system-tray icons) * Build and testing dependencies - All runtime dependencies for CLI and GUI including the recommended - `build-essential` - `gzip` - `gettext` - `python3-pyfakefs` - `pylint` ## Build and install via `make` system (recommended) 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 `make test` 4. `sudo make install` * Qt GUI 1. `cd qt` 2. `./configure && make` 3. Run unit tests via `make test` 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. ## Build own `deb` file 1. Run `./makedeb.sh` in the repositories root directory. 2. Two `deb` files are built and places in the repositories parent directory. 3. Install the packages - `sudo dpkg -i ../backintime-common-.deb` - `sudo dpkg -i ../backintime-qt-.deb` # Testing > [!IMPORTANT] > Remember to **manually** test _Back In Time_ and not rely solely on > the automatic test suite. After [building and installing](#build--install), `make` can be used to run the test suite. Since _Back In Time_ consists of two components, `common` and `qt`, the tests are segregated accordingly. $ cd common $ make test Or $ cd qt $ make test Alternatively use `make test-v` for a more verbose output. The `make` system will use `pytest` as test runner if available otherwise Python's own `unittest` module. ## SSH Some tests require an available SSH server. They get skipped if this is not the case. 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. 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](common/doc-dev/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. # Strategy Outline The following tries to give 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](#code-quality--unit-tests)) - [Issues](#issues) - [Replace encryption library EncFS or remove it](#replace-encryption-library-encfs-or-remove-it) - [Project infrastructure](#project-infrastructure) - [Graphical User Interface (GUI): Redesign and Refactoring](#graphical-user-interface-gui-redesign-and-refactoring) - [Terminal User Interface](#terminal-user-interface) ## 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. 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 encryption library EncFS or remove it 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 replacing it, with [GoCryptFS](https://github.com/rfjakob/gocryptfs) as a potential candidate. 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. Besides replacing EncFS there is also a [discussion](https://mail.python.org/archives/list/bit-dev@python.org/thread/D2GXCCVUAVZ2E5ELBHUZGT7ITUN4ADEP) going on if _Back In Time_ needs an encryption feature or if encryption should be done on file systems level via [LUKS](https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup) or similar solutions. ## Project infrastructure 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)). ## 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. ## Terminal User Interface Various people use _Back In Time_ via the terminal, for example, through an SSH shell on a headless server. There is an idea of creating a terminal user interface (TUI) or to enhance the existing command-line interface (CLI). See [#254](https://github.com/bit-team/backintime/issues/254). The proposal of having a web frontend was rejected ([#209](https://github.com/bit-team/backintime/issues/209)). Separate projects offering a web fronted will be supported of course. # 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 (see [LICENSE](LICENSE)) as the rest of the work. July 2024 backintime-1.5.2/FAQ.md000066400000000000000000001324611465446530500146010ustar00rootroot00000000000000# 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 snapshots to a new hard-drive?](#how-to-move-snapshots-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) - [Backups (snapshots)](#backups-snapshots) * [Does _Back In Time_ create incremental or full backups?](#does-back-in-time-create-incremental-or-full-backups) * [How do snapshots with hard-links work?](#how-do-snapshots-with-hard-links-work) * [How can I check if my snapshots are using hard-links?](#how-can-i-check-if-my-snapshots-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 snapshot logs?](#what-is-the-meaning-of-the-leading-11-characters-eg-cfp-in-my-snapshot-logs) * [Snapshot "WITH ERRORS": [E] 'rsync' ended with exit code 23: See 'man rsync' for more details](#snapshot-with-errors-e-rsync-ended-with-exit-code-23-see-man-rsync-for-more-details) * [What happens when I remove a snapshot?](#what-happens-when-i-remove-a-snapshot) - [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 Snapshots on my new Computer](#back-in-time-doesnt-find-my-old-snapshots-on-my-new-computer) - [Schedule](#schedule) * [How does the 'Repeatedly (anacron)' schedule work?](#how-does-the-repeatedly-anacron-schedule-work) * [Will a scheduled snapshot run as soon as the computer is back on?](#will-a-scheduled-snapshot-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) - [Problems, Errors & Solutions](#problems-errors--solutions) * [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) * [Ubuntu - Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8))](#ubuntu---warning-apt-key-is-deprecated-manage-keyring-files-in-trustedgpgd-instead-see-apt-key8) * [Segmentation fault on Exit](#segmentation-fault-on-exit) * [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) - [user-callback and other PlugIns](#user-callback-and-other-plugins) * [How to backup Debian/Ubuntu Package selection?](#how-to-backup-debianubuntu-package-selection) * [How to restore Debian/Ubuntu Package selection?](#how-to-restore-debianubuntu-package-selection) - [Hardware-specific Setup](#hardware-specific-setup) * [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) * [How to use Western Digital MyBook World Edition with BIT over ssh?](#how-to-use-western-digital-mybook-world-edition-with-bit-over-ssh) - [Uncategorized questions](#uncategorized-questions) * [Which additional features on top of a GUI does BIT provide over a self-configured rsync backup? I saw that it saves the names for uids and gids, so I assume it can restore correctly even if the ids change. Great! :-) Are there additional benefits?](#which-additional-features-on-top-of-a-gui-does-bit-provide-over-a-self-configured-rsync-backup-i-saw-that-it-saves-the-names-for-uids-and-gids-so-i-assume-it-can-restore-correctly-even-if-the-ids-change-great---are-there-additional-benefits) * [Support for specific package formats (deb, rpm, Flatpack, AppImage, Snaps, PPA, …)](#support-for-specific-package-formats-deb-rpm-flatpack-appimage-snaps-ppa-) - [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 _snapshot log_ contains messages specific to a particular snapshot at a given time. It is stored within each snapshot 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 _snapshot_ 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 snapshots 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. Take a look at the [Ubuntu Docu](https://help.ubuntu.com/community/HowtoPartition/ResizingPartition) for more info on that. 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 snapshots. BiT expects this folder, and needs it to import existing snapshots. ## 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/folder in the last snapshot too: 1. Create a new snapshot 2. Move the original folder 3. Manually move the same folder inside BiTs last snapshot in the same way you did with the original folder 4. Create a new snapshot 5. Remove the next to last snapshot (the one where you moved the folder manually) to avoid problems with permissions when you try to restore from that snapshot # Backups (snapshots) ## Does _Back In Time_ create incremental or full backups? Back In Time does use `rsync` and its `--hard-links` feature. Because of that each snapshot 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 snapshots 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. #### snapshot0 | path | inode | counter | |:-------|--------:|----------:| | fileA | 1 | 1 | | fileB | 2 | 1 | | fileC | 3 | 1 | Lets 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. #### snapshot0 | path | inode | counter | |:-------|--------:|----------:| | fileA | 1 | 2 | | fileB | 2 | 1 | | fileC | 3 | 1 | #### snapshot1 | path | inode | counter | |:-------|--------:|----------:| | fileA | 1 | 2 | | fileB | 4 | 1 | | fileD | 5 | 1 | Now change ``fileB`` again and make a new snapshot #### snapshot0 | path | inode | counter | |:-------|--------:|----------:| | fileA | 1 | 3 | | fileB | 2 | 1 | | fileC | 3 | 1 | #### snapshot1 | path | inode | counter | |:-------|--------:|----------:| | fileA | 1 | 3 | | fileB | 4 | 1 | | fileC | 5 | 2 | #### snapshot2 | path | inode | counter | |:-------|--------:|----------:| | fileA | 1 | 3 | | fileB | 6 | 1 | | fileD | 5 | 2 | Finally smart-remove is going to remove **snapshot0**. All that is done by smart-remove is to ``rm -rf`` (force delete everything) the whole directory of **snapshot0**. #### snapshot0 (no longer exist) | path | inode | counter | |:-------|--------:|----------:| | (empty) | 1 | 2 | | (empty) | 2 | 0 | | (empty) | 3 | 0 | #### snapshot1 | path | inode | counter | |:-------|--------:|----------:| | fileA | 1 | 2 | | fileB | 4 | 1 | | fileD | 5 | 2 | #### snapshot2 | 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 snapshots are using hard-links? Please compare the inodes of a file that definitely didn't change between two snapshots. For this open two Terminals and ``cd`` into both snapshot folder. ``ls -lai`` will print a list where the first column is the inode which should be equal for the same file in both snapshots if the file didn't change and the snapshots 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 snapshots it should be 3. Don't be confused on the size of each snapshot. If you right click on preferences for a snapshot in a file manager and look for its size, it will look like they are all full snapshots (not incremental). But that's not (necessary) the case. To get the correct size of each snapshot 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 snapshots 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 snapshots 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 snapshot 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). ## Snapshot "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 snapshot 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 snapshot 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 snapshot 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 snapshot log). ## What happens when I remove a snapshot? Each snapshot is stored in a dated subdirectory of the "full snapshot path" shown in Settings. It contains a ``backup`` directory of all the files as well as a log of the snapshot's creation and some other details. Removing the snapshot removes this whole directory. Each snapshot is independent of the others, so other snapshots are not affected. However, the data of identical files is not stored redundantly by multiple snapshots, so removing a snapshot will only recover the space used by files that are unique to that snapshot. # 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 Snapshots 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 snapshot 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 Snapshots 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 Snapshot 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 Snapshot 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 snapshot. If the snapshot 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 snapshot. No matter how many time elapsed since last successful snapshot. ## Will a scheduled snapshot 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 snapshot. - with ``When drive get connected (udev)`` *Back In Time* will start a snapshot as soon as you connect your drive ;-) - old fashion schedules like ``Every Day`` will use cron. This will only start a new snapshot at the given time. If your computer is off, no snapshot 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. # Problems, Errors & Solutions ## 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)](common/doc-dev/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)](common/doc-dev/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. ## Ubuntu - Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)) In newer Ubuntu-based distros you may get this warning if you install _Back In Time_ from PPA. The reason is that public keys of signed packages shall be stored in a new folder now (for details see https://itsfoss.com/apt-key-deprecated/). A solution is described in [#1338](https://github.com/bit-team/backintime/issues/1338#issuecomment-1454740118) ## Segmentation fault on Exit This problem existed at least since version 1.2.1, and will 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. See also: - [#1768](https://github.com/bit-team/backintime/pull/1768) - [#1095](https://github.com/bit-team/backintime/issues/1095) ## 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 snapshot/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 snapshot will stay in ``new_snapshot`` folder. Depending on which state the process was while killing the next scheduled snapshot 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 snapshot but rsync will fail when there is not enough space. Depending on ``Continue on errors`` setting the failed snapshot will be kept and marked ``With Errors`` or it will be removed. By default, *Back In Time* will finally remove the oldest snapshots until there is more than 1 GiB free space again. # user-callback and other PlugIns ## How to backup Debian/Ubuntu Package selection? There is a [user-callback example](https://github.com/bit-team/user-callback/blob/master/user-callback.apt-backup) which will backup all package selections, sources and repository keys which are necessary to reinstall exactly the same packages again. It will even backup whether a package was installed manually or automatically because of dependencies. Download the script, copy it to ``~/.config/backintime/user-callback`` and make it executable with ``chmod 755 ~/.config/backintime/user-callback`` It will run every time a new snapshot is taken. Make sure to include ``~/.apt-backup``. ## How to restore Debian/Ubuntu Package selection? If you made snapshots including apt-get package selection as described in the FAQ "`How to backup Debian/Ubuntu Package selection?`_" you can easily restore your system after a disaster/on a new machine. 1. install Debian/Ubuntu on your new hard drive as usual 1. install backintime-qt4 from our PPA ```bash sudo add-apt-repository ppa:bit-team/stable sudo apt-get update sudo apt-get install backintime-qt4 ``` 1. connect your external drive with the snapshots 1. Start *Back In Time*. It will ask you if you want to restore your config. Sure you want! *Back In Time* should find your snapshots automatically. Just select the one from which you want to restore the config and click Ok. 1. restore your home 1. recreate your ``/etc/apt/sources.list`` if you had something special in there. If your Debian/Ubuntu version changed don't just copy them from ``~/.apt-backup/sources.list`` 1. copy your repositories with ```bash sudo cp ~/.apt-backup/sources.list.d/* /etc/apt/sources.list.d/ ``` 1. restore apt-keys for your PPAs with ```bash sudo apt-key add ~/.apt-backup/repo.keys ``` 1. install and update `dselect` with ```bash sudo apt-get install dselect sudo dselect update install ``` 1. Make some *housecleaning* in ``~/.apt-backup/package.list``. For example, you don't want to install the old kernel again. So run ```bash sed -e "/^linux-\(image\|headers\)/d" -i ~/.apt-backup/package.list ``` 1. install your old packages again with ```bash sudo apt-get update sudo dpkg --set-selections < ~/.apt-backup/package.list sudo apt-get dselect-upgrade ``` 1. If you used the new script which uses apt-mark to backup package selection proceed with next step. (there should be files ``~/.apt-backup/pkg_auto.list`` and ``~/.apt-backup /pkg_manual.list``). Otherwise, you can stop here. Restore package selection with ```bash sudo apt-mark auto $(cat ~/.apt-backup/pkg_auto.list) sudo apt-mark manual $(cat ~/.apt-backup/pkg_manual.list) ``` # Hardware-specific Setup ## 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 snapshot 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 **HowTo** 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 with a Synology NAS** 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 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 ``` # Uncategorized questions ## Which additional features on top of a GUI does BIT provide over a self-configured rsync backup? I saw that it saves the names for uids and gids, so I assume it can restore correctly even if the ids change. Great! :-) Are there additional benefits? Actually it's the other way around ;) *Back In Time* stores the user and group name which will make it possible to restore permissions correctly even if UID/GID changed. Additionally it will store the current User -> UID and Group -> GID map so if the User/Group doesn't exist on the system during restore it will restore to the old UID/GID. Hard to say which additional features *Back In Time* provides. You can script all of them in your own rsync script, too. But to name some features: - Inhibit suspend/hibernate during take snapshot - Shutdown system after finish - Auto- and Smart-Removal - Plugin- and user-callback support ## 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. # 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). backintime-1.5.2/HISTORY.md000066400000000000000000000154341465446530500153330ustar00rootroot00000000000000# Looking back in time at _Back In Time_ *by Michael Büker, 2024* The history of the _Back In Time_ project, which at the time of this writing already spans nearly 16 years, 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). ## 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 snapshot with the source, in order to check if a new snapshot was necessary. If the answer was yes, it would use `cp` to create a new snapshot. 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 snapshot. 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 snapshot 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. backintime-1.5.2/LICENSE000066400000000000000000000431031465446530500146470ustar00rootroot00000000000000 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. backintime-1.5.2/README.md000066400000000000000000000241461465446530500151270ustar00rootroot00000000000000[![Build Status](https://app.travis-ci.com/bit-team/backintime.svg)](https://app.travis-ci.com/bit-team/backintime) [![Coverage Status](https://coveralls.io/repos/github/bit-team/backintime/badge.svg?branch=main)](https://coveralls.io/github/bit-team/backintime?branch=main) [![Source code documentation Status](https://readthedocs.org/projects/backintime-dev/badge/?version=latest)](https://backintime-dev.readthedocs.io) [![Translation status](https://translate.codeberg.org/widget/backintime/common/svg-badge.svg)](https://translate.codeberg.org/engage/backintime) # Back In Time Copyright (C) 2008-2024 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze, Taylor Raack
Copyright (C) 2022 Christian Buhtz, Michael Büker, Jürgen Altfeld _Back In Time_ is an easy-to-use tool to backup files and folders. It runs on GNU Linux (not on Windows or OS X/macOS) and provides a command line tool `backintime` and a GUI `backintime-qt` both written in Python3. It uses [`rsync`](https://rsync.samba.org/) to take manual or scheduled snapshots and stores them locally or remotely through SSH. Each snapshot is in its own folder with copies of the original files, but unchanged files are hard-linked between snapshots to save storage space. It was inspired by [FlyBack](https://en.wikipedia.org/wiki/FlyBack). ## Maintenance status The project is in active development since the [new team](#the-team) joined in summer 2022. Development is done in spare time so things need to be prioritized. Stick with us, we all ♥️ _Back In Time_. 😁 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). 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). ## The team The current team started in summer of 2022 (with #1232) and constitutes the project's 3rd generation of maintainers. Consisting of three members with diverse backgrounds (@aryoda, @buhtz, @emtiu), the team benefits from the assistance of the former maintainer, @Germar, who contributes from behind the scenes. All team members are engaged in every aspect of the project, including code analysis, documentation, solving issues, and the implementation of new features. This work is carried out voluntarily during their limited spare time. # Index - [Documentation, FAQs, Support](#documentation-faqs-support) - [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) # Documentation, FAQs, Support * [FAQ - Frequently Asked Questions](FAQ.md) * [End user documentation](https://backintime.readthedocs.org/) (not totally up-to-date) * [Mailing list _bit-dev_](https://mail.python.org/mailman3/lists/bit-dev.python.org/) for **every topic**, question and idea about _Back In Time_. Despite its name it is not restricted to development topics only. * Use [Issues](https://github.com/bit-team/backintime/issues) to ask questions and report bugs. * [Source code documentation for developers](https://backintime-dev.readthedocs.org) # 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. ## Alternative installation options Besides the repositories of the official GNU/Linux distributions, there are other alternative installation options provided and maintained by third parties. - [@Germar](https://github.com/germar)'s Personal Package Archive ([PPA](https://launchpad.net/ubuntu/+ppas)) offering [`ppa:bit-team/stable`](https://launchpad.net/~bit-team/+archive/ubuntu/stable) as stable and [`ppa:bit-team/testing`](https://launchpad.net/~bit-team/+archive/ubuntu/testing) as testing PPA. - [@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). # Known Problems and Workarounds 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) In older releases: - Error: "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)) - [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) - [Incompatibility with rsync >= 3.2.4](#incompatibility-with-rsync-324-or-newer) More problems described in [this FAQ section](FAQ.md#problems-errors--solutions). ## Problems in the latest stable release All releases can be found in the [list of releases](https://github.com/bit-team/backintime/releases). ### File permissions handling and therefore possible non-differential backups 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. 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. ### `qt_probing.py` may hang with high CPU usage when running BiT as `root` via `cron` See the related issue [#1592](https://github.com/bit-team/backintime/issues/1592). The only reliable work-around is to delete (or move into another folder) the file `/usr/share/backintime/common/qt_probing.py`: `mv /usr/share/backintime/common/qt_probing.py /usr/share/backintime/` Renaming does *not* work! ## Problems in versions older than the latest stable release ### 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) ### 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. # Contributing and other ways to support the project See [CONTRIBUTING](CONTRIBUTING.md) file for an overview about the projects workflow and strategy. July 2024 backintime-1.5.2/TRANSLATIONS000066400000000000000000000040451465446530500155100ustar00rootroot00000000000000Arabic [ar]: - Maytham Alsudany - Jad Madi (@jadmadi) Basque [eu]: Alexander Gabilondo Bokmål (Norwegian) [nb_NO]: Hans Fredrik Nordhaug Catalan [ca]: Josep Sanchez Chinese (Simplified) [zh_CN,zh_Hans]: Kuntao Zhao Chinese (Traditional) [zh_TW,zh_Hant]: Kuntao Zhao Danish [da]: Adam Sjøgren German [de]: Michael Wiedmann Greek [el]: - Iliana Panagopoulou (hpanago) - Paraskevas Leivadaros Finnish [fi]: - Ellen Rönnholm French [fr]: - Michel Corps (@jej) Indonesian [id]: Andika Triwidada Japanese [ja]: - Ayako Buhtz - AmaseCocoa (甘瀬ここあ) Nynorsk (Norwegian) [nn] - Hans Fredrik Nordhaug - VL Persian (Farsi) [fa]: - Farooq Karimi Zadeh - Parsa Ranjbar Polish [pl]: Paweł Hołuj Portuguese (Brazilian) [pt_BR]: - Scythemare - Andre Desgualdo Pereira - Yuri Musachio Portuguese [pt]: - Yuri Musachio - Filipe Oliveira (@filipeaaoliveira) Romanian [ro]: Florentina Mușat Russian [ru]: Vadim Peretokin Slovak [sk]: Tomáš Vadina Slovenian [sl]: - Vanja Cvelbar - Liam Starič (ravijol1) Spanish [es]: - Francisco Manuel García Claramonte - Mauricio J. Adonis C. Swedish [sv]: Niklas Grahn Turkish [tr]: Onur Esat Karabacak Vietnamese [vi]: Hai Nam Other languages: - Launchpad translators - - - Several mailing lists in Debian (@lists.debian.org) & Ubuntu (@lists.ubuntu.com) especially the user related lists backintime-1.5.2/VERSION000066400000000000000000000000061465446530500147050ustar00rootroot000000000000001.5.2 backintime-1.5.2/common/000077500000000000000000000000001465446530500151315ustar00rootroot00000000000000backintime-1.5.2/common/.coveragerc000066400000000000000000000002531465446530500172520ustar00rootroot00000000000000# default options for coverage from coveralls-python [run] omit = */virtualenv/python3* [report] exclude_lines = pragma: no cover if __name__ == '__main__': backintime-1.5.2/common/applicationinstance.py000066400000000000000000000216421465446530500215400ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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. """ This module holds the ApplicationInstance class, used to handle the one application instance mechanism. """ import os import fcntl 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. Args: pidFile (str): full path of file used to save pid and procname autoExit (bool): automatically call sys.exit if there is an other instance running flock (bool): use file-locks to make sure only one instance is checking at the same time """ def __init__(self, pidFile, autoExit=True, flock=False): self.pidFile = pidFile self.pid = 0 self.procname = '' self.flock = None if flock: self.flockExclusiv() if autoExit: if self.check(True): self.startApplication() def __del__(self): self.flockUnlock() # 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 another instance running Returns: bool: ``True`` if this is the only application instance """ # check if the pidfile exists if not os.path.isfile(self.pidFile): return True self.pid, self.procname = self.readPidFile() # 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 if self.procname and \ self.procname != tools.processName(self.pid) and \ self.procname != tools.processCmdline(self.pid): return True if autoExit: # exit the application print("The application is already running !") # exit raise an exception so don't put it in a try/except block exit(0) return False def busy(self): """ Check if one application with this instance is currently running. Returns: bool: ``True`` if an other instance is currently running. """ return not self.check() def startApplication(self): """ Called when the single instance starts to save its pid """ pid = os.getpid() procname = tools.processName(pid) 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)) # 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): """ Called when the single instance exit (remove pid file) """ try: os.remove(self.pidFile) except: pass def flockExclusiv(self): """ 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(f"Trying to put an advisory lock on the flock file {flock_file_URI}") try: 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)) def flockUnlock(self): """ Remove the exclusive lock. Second instance can now continue but should find it self to be obsolete. """ if self.flock: logger.debug(f"Trying to remove the advisory lock from the 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(...)' pass self.flock = None def readPidFile(self): """ Read the pid and procname from the file Returns: tuple: tuple of (pid(int), procname(str)) """ 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)) except ValueError as 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 appInstance = ApplicationInstance('/tmp/myapp.pid') # do something here print("Start MyApp") time.sleep(5) # sleep 5 seconds print("End MyApp") # remove pid file appInstance.exitApplication() backintime-1.5.2/common/askpass.py000066400000000000000000000031321465446530500171470ustar00rootroot00000000000000# Copyright (C) 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. # # 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 sys import password import password_ipc import tools import config if __name__ == '__main__': """ return password. """ cfg = config.Config() tools.envLoad(cfg.cronEnvFile()) profile_id = os.getenv('ASKPASS_PROFILE_ID', '1') mode = os.getenv('ASKPASS_MODE', 'local') if mode == 'USER': prompt = os.getenv('ASKPASS_PROMPT', None) pw = password.Password(cfg) 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 pw = password.Password(cfg) print(pw.password(None, profile_id, mode)) sys.exit(0) #temp mode fifo = password_ipc.FIFO(temp_file) pw = fifo.read(5) if pw: print(pw) backintime-1.5.2/common/backintime000077500000000000000000000032131465446530500171640ustar00rootroot00000000000000#!/bin/sh # Back In Time # Copyright (C) 2008-2022 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. # 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 # -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 "$@" backintime-1.5.2/common/backintime-askpass000077500000000000000000000022271465446530500206330ustar00rootroot00000000000000#!/bin/sh # Back In Time # Copyright (C) 2008-2022 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. #fixing gray window error #https://launchpad.net/bugs/1493020 export QT_GRAPHICSSYSTEM="native" CUR_PATH="$(dirname $(readlink -m $0))" if [ -f "${CUR_PATH}/askpass.py" ]; then APP_PATH=$CUR_PATH else APP_PATH=$(readlink -m "${CUR_PATH}/../share/backintime/common") fi /usr/bin/python3 -Es $APP_PATH/askpass.py "$@" backintime-1.5.2/common/backintime.desktop000066400000000000000000000003731465446530500206350ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Name=Backintime Password Cache Exec=/bin/sh -c "backintime pw-cache start 2>&1 >/dev/null" Comment=Cache passwords for non-interactive Backintime cronjobs Icon=gtk-save SingleMainWindow=true Terminal=false Type=Application backintime-1.5.2/common/backintime.py000066400000000000000000001260251465446530500176170ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 sys import argparse import atexit import subprocess from datetime import datetime from time import sleep import json import pathlib import tools # Workaround for situations where startApp() is not invoked. # E.g. when using --diagnostics and other argparse.Action tools.initiate_translation(None) import config import logger import snapshots import sshtools import mount import password import encfstools import cli from diagnostics import collect_diagnostics, collect_minimal_diagnostics from exceptions import MountException from applicationinstance import ApplicationInstance from version import __version__ RETURN_OK = 0 RETURN_ERR = 1 RETURN_NO_CFG = 2 parsers = {} def takeSnapshotAsync(cfg, checksum = False): """ Fork a new backintime process with 'backup' command which will take a new snapshot in background. Args: 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()))) 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 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 ' + __version__, help = "show %(prog)s's version number.") parser.add_argument('--license', action = printLicense, nargs = 0, help = "show %(prog)s's license.") parser.add_argument('--diagnostics', action = printDiagnostics, nargs = 0, help = "show helpful info for better support in case of issues (in JSON format)") ####################### ### 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 is running 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 used 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 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 = 'Temporarily disable creation of backup files before changing local files. ' +\ 'This can be switched off permanently in Settings, too.') restoreCP.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.') command = 'shutdown' nargs = 0 description = 'Shut down 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 Removal" 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 snapshot 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 paths 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) logger.openlog() args = argParse(None) # Name, Version, As Root, OS for key, val in collect_minimal_diagnostics().items(): logger.debug(f'{key}: {val}') # 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 " f"{config.Config.APP_NAME}. This will cause some trouble. " f"Please use either 'sudo -i {app_name}' or 'pkexec {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) 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'Used argument(s): {used_args}') logger.debug(f'Unknown argument(s): {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(f'Unknown argument(s): {unknownArgs}') return args def printHeader(): """ Print application name, version and legal notes. """ 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): license_path = pathlib.Path(tools.docPath()) / 'LICENSE' print(license_path.read_text('utf-8')) sys.exit(RETURN_OK) class printDiagnostics(argparse.Action): """ Print information that is helpful for the support team to narrow down problems and bugs. The info is printed using the machine- and human-readable JSON format """ def __init__(self, *args, **kwargs): super(printDiagnostics, self).__init__(*args, **kwargs) def __call__(self, *args, **kwargs): diagnostics = collect_diagnostics() print(json.dumps(diagnostics, indent=4)) 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 paths 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 = '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 lastSnapshot(args): """ Command for printing the very last snapshot in current profile. Args: args (argparse.Namespace): previously parsed arguments 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) else: logger.error("There are no snapshots in '%s'" % cfg.profileName()) _umount(cfg) sys.exit(RETURN_OK) def lastSnapshotPath(args): """ Command for printing the path of the very last snapshot in current profile. Args: args (argparse.Namespace): previously parsed arguments 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) def unmount(args): """ Command for unmounting all filesystems. Args: args (argparse.Namespace): previously parsed arguments Raises: SystemExit: 0 """ setQuiet(args) cfg = getConfig(args) _mount(cfg) _umount(cfg) sys.exit(RETURN_OK) def benchmarkCipher(args): """ Command for transferring a file with scp to remote host with all available ciphers and print its speed and time. 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. Args: args (argparse.Namespace): previously parsed arguments 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): """ Command for removing snapshots without asking before remove (BE CAREFUL!) Args: args (argparse.Namespace): previously parsed arguments Raises: SystemExit: 0 """ remove(args, True) def smartRemove(args): """ Command for running Smart-Removal from Terminal. Args: args (argparse.Namespace): previously parsed arguments Raises: SystemExit: 0 if okay 2 if Smart-Removal is not configured """ setQuiet(args) printHeader() cfg = getConfig(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('Smart Removal will remove {} snapshots'.format(len(del_snapshots))) sn.smartRemove(del_snapshots, log = logger.info) _umount(cfg) sys.exit(RETURN_OK) else: logger.error('Smart Removal is not configured.') sys.exit(RETURN_NO_CFG) def restore(args): """ Command for restoring files from snapshots. Args: args (argparse.Namespace): previously parsed arguments 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) def checkConfig(args): """ Command for checking the config file. Args: args (argparse.Namespace): previously parsed arguments 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() backintime-1.5.2/common/bash-completion/000077500000000000000000000000001465446530500202155ustar00rootroot00000000000000backintime-1.5.2/common/bash-completion/backintime000066400000000000000000000066771465446530500222660ustar00rootroot00000000000000# 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) if [[ ${COMP_WORDS[$c]} != -* ]]; then opts="${opts} ${last} ${COMP_WORDS[$c]}" fi ;; esac last=${COMP_WORDS[$c]} c=$[$c+1] done echo "${opts}" } # return a list of all snapshots _bit_snapshots_list() { backintime$(_bit_extr_opts) --quiet snapshots-list | awk '{print $2}' } _backintime() { local cur prev actions opts pw_cache_commands local cur_action='' pos_action=0 c=0 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 \ --diagnostics" 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" pw_cache_commands="start stop restart reload status" # extract the current action while [[ $c -le $[${COMP_CWORD} - 1] ]]; do case ${actions} in *"${COMP_WORDS[$c]}"*) cur_action="${COMP_WORDS[$c]}" pos_action=${c} break ;; esac c=$[${c}+1] done case "${cur_action}" in restore) if [[ ${cur} != -* ]]; then #which positional argument is $cur? case $[${COMP_CWORD}-${pos_action}] in #first arg is a filename 1) _filedir return 0 ;; #second arg is a dirname 2) _filedir -d return 0 ;; #third arg is snapshot-id 3) COMPREPLY=( $(compgen -W "$(_bit_snapshots_list)" -- ${cur}) ) return 0 ;; esac fi ;; remove|remove-and-do-not-ask-again) if [[ ${cur} != -* ]]; then #snapshot-ids COMPREPLY=( $(compgen -W "$(_bit_snapshots_list)" -- ${cur}) ) return 0 else #other args COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi ;; esac case "${prev}" in --config|decode|restore|--share-path) if [[ ${cur} != -* ]]; then _filedir return 0 fi ;; pw-cache) if [[ ${cur} != -* ]]; then COMPREPLY=( $(compgen -W "${pw_cache_commands}" -- ${cur}) ) return 0 fi ;; *) if [[ -z "${cur_action}" ]]; then opts="${opts} ${actions}" fi COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; esac } complete -F _backintime backintime complete -F _backintime backintime-qt backintime-1.5.2/common/bcolors.py000066400000000000000000000022131465446530500171440ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (C) 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. # # 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 if sys.stdout.isatty(): HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' else: HEADER = '' OKBLUE = '' OKGREEN = '' WARNING = '' FAIL = '' ENDC = '' BOLD = '' UNDERLINE = '' backintime-1.5.2/common/bitbase.py000066400000000000000000000007571465446530500171250ustar00rootroot00000000000000# SPDX-FileCopyrightText: © 2024 Christian BUHTZ # # SPDX-License-Identifier: GPL-2.0 # # This file is part of the program "Back In time" which is released under GNU # General Public License v2 (GPLv2). # See file LICENSE or go to . """Basic constants used in multiple modules.""" # See issue #1734 and #1735 URL_ENCRYPT_TRANSITION = 'https://github.com/bit-team/backintime' \ '/blob/-/doc/ENCRYPT_TRANSITION.md' backintime-1.5.2/common/cli.py000066400000000000000000000155531465446530500162630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Back In Time # Copyright (C) 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. # # 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 sys import tools import snapshots import bcolors 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))) if where is None: where = input('Restore to (empty for original path): ') if where: where = tools.preparePath(os.path.abspath(os.path.expanduser(where))) snapshotsList = snapshots.listSnapshots(cfg) 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): snapshotsList = snapshots.listSnapshots(cfg) if not snapshot_ids: snapshot_ids = (None,) sids = [selectSnapshot(snapshotsList, cfg, sid, 'SnapshotID to remove') for sid in snapshot_ids] if not force: print('Do you really want to remove these snapshots?') [print(sid.displayName) for sid in sids] if not 'yes' == input('(no/yes): '): return s = snapshots.Snapshots(cfg) [s.remove(sid) for sid in sids] def checkConfig(cfg, crontab = True): import mount from exceptions import MountException def announceTest(): print() print(frame(test)) def failed(): print(test + ': ' + bcolors.FAIL + 'failed' + bcolors.ENDC) def okay(): print(test + ': ' + bcolors.OKGREEN + 'done' + bcolors.ENDC) def errorHandler(msg): print(bcolors.WARNING + 'WARNING: ' + bcolors.ENDC + msg) cfg.setErrorHandler(errorHandler) mode = cfg.snapshotsMode() if cfg.SNAPSHOT_MODES[mode][0] is not None: #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 test = 'Mount' announceTest() try: hash_id = mnt.mount(mode = mode, check = False) except MountException as ex: failed() print(str(ex)) return False okay() test = 'Check/prepare snapshot path' announceTest() snapshots_path = cfg.snapshotsPath(mode = mode, tmp_mount = True) if not cfg.setSnapshotsPath(snapshots_path, mode = mode): failed() return False okay() #umount if not cfg.SNAPSHOT_MODES[mode][0] is None: test = 'Unmount' announceTest() try: 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(): failed() return False okay() return True 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 """ 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])) 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 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' ret += ' |' + msg.center(size) + '|\n' ret += ' +' + '-' * size + '+' return ret class RestoreDialog(object): def __init__(self, cfg, sid, what, where, **kwargs): self.config = cfg self.sid = sid self.what = what self.where = where self.kwargs = kwargs self.logFile = self.config.restoreLogFile() if os.path.exists(self.logFile): os.remove(self.logFile) def callback(self, line, *params): if not line: return print(line) with open(self.logFile, 'a') 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) print('\nLog saved to %s' % self.logFile) class BackupJobDaemon(tools.Daemon): def __init__(self, func, args): super(BackupJobDaemon, self).__init__() self.func = func self.args = args def run(self): self.func(self.args, False) backintime-1.5.2/common/config-example-local000066400000000000000000000045051465446530500210460ustar00rootroot00000000000000profile1.snapshots.automatic_backup_day=1 profile1.snapshots.automatic_backup_mode=0 profile1.snapshots.automatic_backup_time=0 profile1.snapshots.automatic_backup_weekday=7 profile1.snapshots.backup_on_restore.enabled=true profile1.snapshots.bwlimit.enabled=false profile1.snapshots.bwlimit.value=3000 profile1.snapshots.continue_on_errors=true profile1.snapshots.copy_links=false profile1.snapshots.copy_unsafe_links=false profile1.snapshots.cron.ionice=true profile1.snapshots.cron.nice=true profile1.snapshots.custom_backup_time=8,12,18,23 profile1.snapshots.dont_remove_named_snapshots=true profile1.snapshots.exclude.1.value=.gvfs profile1.snapshots.exclude.10.value=/proc/* profile1.snapshots.exclude.11.value=/sys/* profile1.snapshots.exclude.12.value=/dev/* profile1.snapshots.exclude.13.value=/run/* profile1.snapshots.exclude.2.value=.cache* profile1.snapshots.exclude.3.value=[Cc]ache* profile1.snapshots.exclude.4.value=.thumbnails* profile1.snapshots.exclude.5.value=[Tt]rash* profile1.snapshots.exclude.6.value=*.backup* profile1.snapshots.exclude.7.value=*~ profile1.snapshots.exclude.8.value=/home/USER/Ubuntu One profile1.snapshots.exclude.9.value=.dropbox* profile1.snapshots.exclude.size=13 profile1.snapshots.include.1.type=0 profile1.snapshots.include.1.value=/home/USER profile1.snapshots.include.size=1 profile1.snapshots.local_encfs.path= profile1.snapshots.log_level=3 profile1.snapshots.min_free_space.enabled=true profile1.snapshots.min_free_space.unit=20 profile1.snapshots.min_free_space.value=1 profile1.snapshots.mode=local profile1.snapshots.no_on_battery=false profile1.snapshots.notify.enabled=true profile1.snapshots.path=/mnt/backup profile1.snapshots.path.auto=true profile1.snapshots.path.host=HOST profile1.snapshots.path.profile=1 profile1.snapshots.path.user=USER profile1.snapshots.preserve_acl=false profile1.snapshots.preserve_xattr=false profile1.snapshots.remove_old_snapshots.enabled=true profile1.snapshots.remove_old_snapshots.unit=80 profile1.snapshots.remove_old_snapshots.value=10 profile1.snapshots.smart_remove=false profile1.snapshots.smart_remove.keep_all=2 profile1.snapshots.smart_remove.keep_one_per_day=7 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 profiles.version=1 backintime-1.5.2/common/config-example-ssh000066400000000000000000000051741465446530500205540ustar00rootroot00000000000000profile1.snapshots.automatic_backup_day=1 profile1.snapshots.automatic_backup_mode=0 profile1.snapshots.automatic_backup_time=0 profile1.snapshots.automatic_backup_weekday=7 profile1.snapshots.backup_on_restore.enabled=true profile1.snapshots.bwlimit.enabled=false profile1.snapshots.bwlimit.value=3000 profile1.snapshots.continue_on_errors=true profile1.snapshots.copy_links=false profile1.snapshots.copy_unsafe_links=false profile1.snapshots.cron.ionice=true profile1.snapshots.cron.nice=true profile1.snapshots.custom_backup_time=8,12,18,23 profile1.snapshots.dont_remove_named_snapshots=true profile1.snapshots.exclude.1.value=.gvfs profile1.snapshots.exclude.10.value=/proc/* profile1.snapshots.exclude.11.value=/sys/* profile1.snapshots.exclude.12.value=/dev/* profile1.snapshots.exclude.13.value=/run/* profile1.snapshots.exclude.2.value=.cache* profile1.snapshots.exclude.3.value=[Cc]ache* profile1.snapshots.exclude.4.value=.thumbnails* profile1.snapshots.exclude.5.value=[Tt]rash* profile1.snapshots.exclude.6.value=*.backup* profile1.snapshots.exclude.7.value=*~ profile1.snapshots.exclude.8.value=/home/USER/Ubuntu One profile1.snapshots.exclude.9.value=.dropbox* profile1.snapshots.exclude.size=13 profile1.snapshots.include.1.type=0 profile1.snapshots.include.1.value=/home/USER profile1.snapshots.include.size=1 profile1.snapshots.local_encfs.path= profile1.snapshots.log_level=3 profile1.snapshots.min_free_space.enabled=true profile1.snapshots.min_free_space.unit=20 profile1.snapshots.min_free_space.value=1 profile1.snapshots.mode=ssh profile1.snapshots.no_on_battery=false profile1.snapshots.notify.enabled=true profile1.snapshots.path= profile1.snapshots.path.auto=true profile1.snapshots.path.host=HOST profile1.snapshots.path.profile=1 profile1.snapshots.path.user=USER profile1.snapshots.preserve_acl=false profile1.snapshots.preserve_xattr=false profile1.snapshots.remove_old_snapshots.enabled=true profile1.snapshots.remove_old_snapshots.unit=80 profile1.snapshots.remove_old_snapshots.value=10 profile1.snapshots.smart_remove=false profile1.snapshots.smart_remove.keep_all=2 profile1.snapshots.smart_remove.keep_one_per_day=7 profile1.snapshots.smart_remove.keep_one_per_month=24 profile1.snapshots.smart_remove.keep_one_per_week=4 profile1.snapshots.ssh.cipher=default profile1.snapshots.ssh.host=REMOTE_HOST profile1.snapshots.ssh.password.save=false profile1.snapshots.ssh.password.use_cache=true profile1.snapshots.ssh.path= profile1.snapshots.ssh.port=22 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 profiles.version=1 backintime-1.5.2/common/config.py000066400000000000000000002266771465446530500167740ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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. """Configuration 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 socket import random import textwrap 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: _('Warning') except NameError: _ = lambda val: val import tools import configfile import logger import sshtools import encfstools import password import pluginmanager import schedule from exceptions import PermissionDeniedByPolicy, \ InvalidChar, \ InvalidCmd, \ LimitExceeded import version class Config(configfile.ConfigFileWithProfiles): APP_NAME = 'Back In Time' COPYRIGHT = 'Copyright (C) 2008-2024 Oprea Dan, Bart de Koning, ' \ 'Richard Bailey, Germar Reitze, Christian Buhtz, Michael Büker, Jürgen Altfeld et al.' CONFIG_VERSION = 6 """Latest or highest possible version of Back in Time's config file.""" 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 # Used when new snapshot profile is created. 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', '/swapfile', # Discord files # See also: https://github.com/bit-team/backintime/issues/1555#issuecomment-1787230708 'SingletonLock', 'SingletonCookie', # Mozilla files # See also: https://github.com/bit-team/backintime/issues/1555#issuecomment-1787111063 'lock' ] 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_REMOTE = False DEFAULT_SSH_PREFIX = 'PATH=/opt/bin:/opt/sbin:\\$PATH' DEFAULT_REDIRECT_STDOUT_IN_CRON = True DEFAULT_REDIRECT_STDERR_IN_CRON = False ENCODE = encfstools.Bounce() PLUGIN_MANAGER = pluginmanager.PluginManager() 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._GLOBAL_CONFIG_PATH = '/etc/backintime/config' HOME_FOLDER = os.path.expanduser('~') DATA_FOLDER = '.local/share' CONFIG_FOLDER = '.config' BIT_FOLDER = 'backintime' self._DEFAULT_LOCAL_DATA_FOLDER = os.path.join(HOME_FOLDER, DATA_FOLDER, BIT_FOLDER) self._LOCAL_CONFIG_FOLDER = os.path.join(HOME_FOLDER, CONFIG_FOLDER, BIT_FOLDER) self._MOUNT_ROOT = os.path.join(DATA_FOLDER, BIT_FOLDER, 'mnt') if data_path: self.DATA_FOLDER_ROOT = data_path self._LOCAL_DATA_FOLDER = os.path.join(data_path, DATA_FOLDER, BIT_FOLDER) self._LOCAL_MOUNT_ROOT = os.path.join(data_path, self._MOUNT_ROOT) else: self.DATA_FOLDER_ROOT = HOME_FOLDER self._LOCAL_DATA_FOLDER = self._DEFAULT_LOCAL_DATA_FOLDER self._LOCAL_MOUNT_ROOT = os.path.join(HOME_FOLDER, self._MOUNT_ROOT) tools.makeDirs(self._LOCAL_CONFIG_FOLDER) tools.makeDirs(self._LOCAL_DATA_FOLDER) 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) # (buhtz) Introduced in 2009 via commit 5b26575be4. # Ready to remove after 15 years. # 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) # 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: # 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(version.__version__)) #TODO: add popup warning sys.exit(2) 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) if currentConfigVersion < 6: logger.info('Update to config version 6', self) # remap some keys for profile in self.profiles(): # make a 'schedule' domain for everything relating schedules self.remapProfileKey('snapshots.automatic_backup_anacron_period', 'schedule.repeatedly.period', profile) self.remapProfileKey('snapshots.automatic_backup_anacron_unit', 'schedule.repeatedly.unit', profile) self.remapProfileKey('snapshots.automatic_backup_day', 'schedule.day', profile) self.remapProfileKey('snapshots.automatic_backup_mode', 'schedule.mode', profile) self.remapProfileKey('snapshots.automatic_backup_time', 'schedule.time', profile) self.remapProfileKey('snapshots.automatic_backup_weekday', 'schedule.weekday', profile) self.remapProfileKey('snapshots.custom_backup_time', 'schedule.custom_time', profile) # we don't have 'full rsync mode' anymore self.remapProfileKey('snapshots.full_rsync.take_snapshot_regardless_of_changes', 'snapshots.take_snapshot_regardless_of_changes', profile) # remap 'qt4' keys self.remapKeyRegex(r'qt4', 'qt') # 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. # Unhide them after the upcoming release (1.5.0). # 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), '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') ) } 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.setIntValue('config.version', self.CONFIG_VERSION) return super(Config, self).save(self._LOCAL_CONFIG_PATH) def checkConfig(self): profiles = self.profiles() 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) #check snapshots path if not snapshots_path: self.notifyError( '{}\n{}'.format( _('Profile: "{name}"').format(name=profile_name), _('Snapshots folder is not valid!') ) ) return False #check include include_list = self.include(profile_id) if not include_list: self.notifyError( '{}\n{}'.format( _('Profile: "{name}"').format(name=profile_name), _('You must select at least one folder to back up!') ) ) return False snapshots_path2 = snapshots_path + '/' for item in include_list: if item[1] != 0: continue path = item[0] if path == snapshots_path: self.notifyError( '{}\n{}'.format( _('Profile: "{name}"').format(name=profile_name), _("Backup folder cannot be included.") ) ) return False if len(path) >= len(snapshots_path2): if path[: len(snapshots_path2)] == snapshots_path2: self.notifyError( '{}\n{}'.format( _('Profile: "{name}"').format( name=profile_name), _("Backup sub-folder cannot be included.") ) ) return False return True def pid(self): return str(os.getpid()) def host(self): return socket.gethostname() def snapshotsPath(self, profile_id = None, mode = None, tmp_mount = False): 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) def setSnapshotsPath(self, value, profile_id = None, mode = None): """ Sets the snapshot path to value, initializes, and checks it """ if not value: return False if profile_id == None: profile_id = self.currentProfile() if mode is None: mode = self.snapshotsMode(profile_id) if not os.path.isdir(value): self.notifyError(_('Invalid option. {path} is not a folder.').format(path=value)) return False # Initialize the snapshots folder logger.debug("Check snapshot folder: %s" % value, self) host, user, profile = self.hostUserProfile(profile_id) if not all((host, user, profile)): self.notifyError(_('Host/User/Profile-ID must not be empty.')) return False 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: {path}\nAre you sure you have " "write access?").format(path=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 set permissions world-writable 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} is formatted with FAT " "which doesn't support hard-links. " "Please use a native Linux filesystem.") .format(path=value)) return False elif fs == 'cifs' and not self.copyLinks(): self.notifyError(_( 'Destination filesystem for {path} is an SMB-mounted share. ' 'Please make sure the remote SMB server supports symlinks or ' 'activate {copyLinks} in {expertOptions}.') .format(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} is an sshfs-mounted share." " Sshfs doesn't support hard-links. " "Please use mode 'SSH' instead.") .format(path=value)) return False #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: {path}\nAre you sure you have " "write access?").format(path=full_path)) return False os.rmdir(check_path) if self.SNAPSHOT_MODES[mode][0] is None: self.setProfileStrValue('snapshots.path', value, profile_id) return True 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 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 def hashCollision(self): #?Internal value used to prevent hash collisions on mountpoints. Do not change this. return self.intValue('global.hash_collision', 0) def incrementHashCollision(self): value = self.hashCollision() + 1 self.setIntValue('global.hash_collision', 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 '') def manual_starts_countdown(self) -> int: """Countdown value about how often the users started the Back In Time GUI. It is an internal variable not meant to be used or manipulated be the users. At the end of the countown the :py:class:`ApproachTranslatorDialog` is presented to the user. """ return self.intValue('internal.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.setIntValue('internal.manual_starts_countdown', val - 1) # SSH def sshSnapshotsPath(self, profile_id = None): #?Snapshot path on remote host. If the path is relative (no leading '/') #?it will start from remote Users homedir. An empty path will be replaced #?with './'.;absolute or relative path return self.profileStrValue('snapshots.ssh.path', '', profile_id) def sshSnapshotsFullPath(self, profile_id = None): """ Returns the full path for the snapshots: .../backintime/machine/user/profile_id/ """ path = self.sshSnapshotsPath(profile_id) if not path: path = './' host, user, profile = self.hostUserProfile(profile_id) return os.path.join(path, 'backintime', host, user, profile) def setSshSnapshotsPath(self, value, profile_id = None): self.setProfileStrValue('snapshots.ssh.path', value, profile_id) return True def sshHost(self, profile_id = None): #?Remote host used for mode 'ssh' and 'ssh_encfs'.;IP or domain address return self.profileStrValue('snapshots.ssh.host', '', profile_id) def setSshHost(self, value, profile_id = None): self.setProfileStrValue('snapshots.ssh.host', value, profile_id) def sshPort(self, profile_id = None): #?SSH Port on remote host.;0-65535 return self.profileIntValue('snapshots.ssh.port', '22', profile_id) def setSshPort(self, value, profile_id = None): self.setProfileIntValue('snapshots.ssh.port', value, profile_id) def sshCipher(self, profile_id = None): #?Cipher that is used for encrypting the SSH tunnel. Depending on the #?environment (network bandwidth, cpu and hdd performance) a different #?cipher might be faster.;default | aes192-cbc | aes256-cbc | aes128-ctr | #? aes192-ctr | aes256-ctr | arcfour | arcfour256 | arcfour128 | aes128-cbc | #? 3des-cbc | blowfish-cbc | cast128-cbc return self.profileStrValue('snapshots.ssh.cipher', 'default', profile_id) def setSshCipher(self, value, profile_id = None): self.setProfileStrValue('snapshots.ssh.cipher', value, profile_id) def sshUser(self, profile_id = None): #?Remote SSH user;;local users name 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) def sshHostUserPortPathCipher(self, profile_id = None): host = self.sshHost(profile_id) port = self.sshPort(profile_id) user = self.sshUser(profile_id) path = self.sshSnapshotsPath(profile_id) cipher = self.sshCipher(profile_id) if not path: 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): 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;;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 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/sshMaxArg.py [initial_ssh_cmd_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 too low to run commands' % value) return value def setSshMaxArgLength(self, value, profile_id = None): self.setProfileIntValue('snapshots.ssh.max_arg_length', value, profile_id) def sshCheckCommands(self, profile_id = None): #?Check if all commands (used during takeSnapshot) work like expected #?on the remote host. return self.profileBoolValue('snapshots.ssh.check_commands', True, profile_id) def setSshCheckCommands(self, value, profile_id = None): self.setProfileBoolValue('snapshots.ssh.check_commands', value, profile_id) def sshCheckPingHost(self, profile_id = None): #?Check if the remote host is available before trying to mount. return self.profileBoolValue('snapshots.ssh.check_ping', True, profile_id) def setSshCheckPingHost(self, value, profile_id = None): self.setProfileBoolValue('snapshots.ssh.check_ping', value, profile_id) def sshDefaultArgs(self, profile_id = None): """ Default arguments used for ``ssh`` and ``sshfs`` commands. Returns: list: arguments for ssh """ # 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))] 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): """ Return SSH command with all arguments. Args: cmd (list): command that should run on remote host custom_args (list): additional arguments paste to the command port (bool): use port from config cipher (bool): use cipher from config user_host (bool): use user@host from config ionice (bool): use ionice if configured nice (bool): use nice if configured quote (bool): quote remote command prefix (bool): use prefix from config before remote command profile_id (str): profile ID that should be used in config 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 += 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', f'Ciphers={c}'] # custom arguments if custom_args: ssh += custom_args # user@host if user_host: ssh.append('{}@{}'.format(self.sshUser(profile_id), self.sshHost(profile_id))) # 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=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 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) def setLocalEncfsPath(self, value, profile_id = None): self.setProfileStrValue('snapshots.local_encfs.path', value, profile_id) def passwordSave(self, profile_id = None, mode = None): if mode is None: mode = self.snapshotsMode(profile_id) #?Save password to system keyring (gnome-keyring or kwallet). #? must be the same as \fIprofile.snapshots.mode\fR return self.profileBoolValue('snapshots.%s.password.save' % mode, False, profile_id) def setPasswordSave(self, value, profile_id = None, mode = None): if mode is None: mode = self.snapshotsMode(profile_id) self.setProfileBoolValue('snapshots.%s.password.save' % mode, value, profile_id) 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) 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): 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): 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) def modeNeedPassword(self, mode, pw_id = 1): need_pw = self.SNAPSHOT_MODES[mode][pw_id + 1] if need_pw is False: return False return True def keyringServiceName(self, profile_id = None, mode = None, pw_id = 1): if mode is None: mode = self.snapshotsMode(profile_id) if pw_id > 1: return 'backintime/%s_%s' % (mode, pw_id) return 'backintime/%s' % mode def keyringUserName(self, profile_id = None): if profile_id is None: profile_id = self.currentProfile() return 'profile_id_%s' % profile_id def hostUserProfileDefault(self, profile_id=None): host = socket.gethostname() user = getpass.getuser() profile = profile_id if profile is None: profile = self.currentProfile() return (host, user, profile) def hostUserProfile(self, profile_id = None): default_host, default_user, default_profile = self.hostUserProfileDefault(profile_id) #?Set Host for snapshot path;;local hostname host = self.profileStrValue('snapshots.path.host', default_host, profile_id) #?Set User for snapshot path;;local username user = self.profileStrValue('snapshots.path.user', default_user, profile_id) #?Set Profile-ID for snapshot path;1-99999;current Profile-ID profile = self.profileStrValue('snapshots.path.profile', default_profile, profile_id) return (host, user, profile) def setHostUserProfile(self, host, user, profile, profile_id = None): self.setProfileStrValue('snapshots.path.host', host, profile_id) 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): #?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) 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) def setExclude(self, values, profile_id = None): self.setProfileListValue('snapshots.exclude', 'str:value', values, profile_id) def excludeBySizeEnabled(self, profile_id = None): #?Enable exclude files by size. return self.profileBoolValue('snapshots.exclude.bysize.enabled', False, profile_id) def excludeBySize(self, profile_id = None): #?Exclude files bigger than value in MiB. #?With 'Full rsync mode' disabled this will only affect new files #?because for rsync this is a transfer option, not an exclude option. #?So big files that has been backed up before will remain in snapshots #?even if they had changed. return self.profileIntValue('snapshots.exclude.bysize.value', 500, profile_id) def setExcludeBySize(self, enabled, value, profile_id = None): self.setProfileBoolValue('snapshots.exclude.bysize.enabled', enabled, profile_id) self.setProfileIntValue('snapshots.exclude.bysize.value', value, profile_id) def tag(self, profile_id = None): #?!ignore this in manpage return self.profileStrValue('snapshots.tag', str(random.randint(100, 999)), profile_id) def scheduleMode(self, profile_id = None): #?Which schedule used for crontab. The crontab entry will be #?generated with 'backintime check-config'.\n #? 0 = Disabled\n 1 = at every boot\n 2 = every 5 minute\n #? 4 = every 10 minute\n 7 = every 30 minute\n10 = every hour\n #?12 = every 2 hours\n14 = every 4 hours\n16 = every 6 hours\n #?18 = every 12 hours\n19 = custom defined hours\n20 = every day\n #?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) def setScheduleMode(self, value, profile_id = None): self.setProfileIntValue('schedule.mode', 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): #?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 setScheduleTime(self, value, profile_id = None): self.setProfileIntValue('schedule.time', value, profile_id) def scheduleDay(self, profile_id = None): #?Which day of month the cronjob should run? Only valid for #?\fIprofile.schedule.mode\fR >= 40;1-28 return self.profileIntValue('schedule.day', 1, profile_id) def setScheduleDay(self, value, profile_id = None): self.setProfileIntValue('schedule.day', value, profile_id) def scheduleWeekday(self, profile_id = None): #?Which day of week the cronjob should run? Only valid for #?\fIprofile.schedule.mode\fR = 30;1 = monday \- 7 = sunday return self.profileIntValue('schedule.weekday', 7, profile_id) def setScheduleWeekday(self, value, profile_id = None): self.setProfileIntValue('schedule.weekday', value, profile_id) def customBackupTime(self, profile_id = None): #?Custom hours for cronjob. Only valid for #?\fIprofile.schedule.mode\fR = 19 #?;comma separated int (8,12,18,23) or */3;8,12,18,23 return self.profileStrValue('schedule.custom_time', '8,12,18,23', profile_id) def setCustomBackupTime(self, value, profile_id = None): self.setProfileStrValue('schedule.custom_time', value, profile_id) def scheduleRepeatedPeriod(self, profile_id = None): #?How many units to wait between new snapshots with anacron? Only valid #?for \fIprofile.schedule.mode\fR = 25|27 return self.profileIntValue('schedule.repeatedly.period', 1, profile_id) def setScheduleRepeatedPeriod(self, value, profile_id = None): self.setProfileIntValue('schedule.repeatedly.period', value, profile_id) def scheduleRepeatedUnit(self, profile_id = None): #?Units to wait between new snapshots with anacron.\n #?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) def setScheduleRepeatedUnit(self, value, profile_id = None): self.setProfileIntValue('schedule.repeatedly.unit', value, profile_id) def removeOldSnapshots(self, profile_id = None): #?Remove all snapshots older than value + unit return (self.profileBoolValue('snapshots.remove_old_snapshots.enabled', True, profile_id), #?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)) def keepOnlyOneSnapshot(self, profile_id = None): #?NOT YET IMPLEMENTED. Remove all snapshots but one. return self.profileBoolValue('snapshots.keep_only_one_snapshot.enabled', False, profile_id) def setKeepOnlyOneSnapshot(self, value, profile_id = None): self.setProfileBoolValue('snapshots.keep_only_one_snapshot.enabled', value, profile_id) def removeOldSnapshotsEnabled(self, profile_id = None): return self.profileBoolValue('snapshots.remove_old_snapshots.enabled', True, profile_id) 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) 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 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): enabled, value, unit = self.minFreeSpace(profile_id) if not enabled: return 0 if self.DISK_UNIT_MB == unit: return value value *= 1024 #Gb if self.DISK_UNIT_GB == unit: return value return 0 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 minFreeInodes(self, profile_id = None): #?Keep at least value % free inodes.;1-15 return self.profileIntValue('snapshots.min_free_inodes.value', 2, profile_id) 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) def setMinFreeInodes(self, enabled, value, profile_id = None): self.setProfileBoolValue('snapshots.min_free_inodes.enabled', enabled, profile_id) self.setProfileIntValue('snapshots.min_free_inodes.value', value, profile_id) def dontRemoveNamedSnapshots(self, profile_id = None): #?Keep snapshots with names during smart_remove. return self.profileBoolValue('snapshots.dont_remove_named_snapshots', True, profile_id) def setDontRemoveNamedSnapshots(self, value, profile_id = None): self.setProfileBoolValue('snapshots.dont_remove_named_snapshots', value, profile_id) def smartRemove(self, profile_id = None): #?Run smart_remove to clean up old snapshots after a new snapshot was created. return (self.profileBoolValue('snapshots.smart_remove', False, profile_id), #?Keep all snapshots for X days. self.profileIntValue('snapshots.smart_remove.keep_all', 2, profile_id), #?Keep one snapshot per day for X days. self.profileIntValue('snapshots.smart_remove.keep_one_per_day', 7, profile_id), #?Keep one snapshot per week for X weeks. self.profileIntValue('snapshots.smart_remove.keep_one_per_week', 4, profile_id), #?Keep one snapshot per month for X month. self.profileIntValue('snapshots.smart_remove.keep_one_per_month', 24, profile_id)) def setSmartRemove(self, value, keep_all, keep_one_per_day, keep_one_per_week, keep_one_per_month, profile_id = None): self.setProfileBoolValue('snapshots.smart_remove', value, profile_id) self.setProfileIntValue('snapshots.smart_remove.keep_all', keep_all, profile_id) self.setProfileIntValue('snapshots.smart_remove.keep_one_per_day', keep_one_per_day, profile_id) self.setProfileIntValue('snapshots.smart_remove.keep_one_per_week', keep_one_per_week, profile_id) self.setProfileIntValue('snapshots.smart_remove.keep_one_per_month', keep_one_per_month, profile_id) def smartRemoveRunRemoteInBackground(self, profile_id = None): #?If using mode SSH or SSH-encrypted, run smart_remove in background on remote machine return self.profileBoolValue('snapshots.smart_remove.run_remote_in_background', False, profile_id) def setSmartRemoveRunRemoteInBackground(self, value, profile_id = None): self.setProfileBoolValue('snapshots.smart_remove.run_remote_in_background', value, profile_id) def notify(self, profile_id = None): #?Display notifications (errors, warnings) through libnotify. return self.profileBoolValue('snapshots.notify.enabled', True, profile_id) def setNotify(self, value, profile_id = None): self.setProfileBoolValue('snapshots.notify.enabled', value, profile_id) def backupOnRestore(self, profile_id = None): #?Rename existing files before restore into FILE.backup.YYYYMMDD return self.profileBoolValue('snapshots.backup_on_restore.enabled', True, profile_id) def setBackupOnRestore(self, value, profile_id = None): self.setProfileBoolValue('snapshots.backup_on_restore.enabled', value, profile_id) def niceOnCron(self, profile_id = None): #?Run cronjobs with 'nice \-n19'. This will give BackInTime the #?lowest CPU priority to not interrupt any other working process. return self.profileBoolValue('snapshots.cron.nice', self.DEFAULT_RUN_NICE_FROM_CRON, profile_id) def setNiceOnCron(self, value, profile_id = None): self.setProfileBoolValue('snapshots.cron.nice', value, profile_id) def ioniceOnCron(self, profile_id = None): #?Run cronjobs with 'ionice \-c2 \-n7'. This will give BackInTime the #?lowest IO bandwidth priority to not interrupt any other working process. return self.profileBoolValue('snapshots.cron.ionice', self.DEFAULT_RUN_IONICE_FROM_CRON, profile_id) def setIoniceOnCron(self, value, profile_id = None): self.setProfileBoolValue('snapshots.cron.ionice', value, profile_id) def ioniceOnUser(self, profile_id = None): #?Run BackInTime with 'ionice \-c2 \-n7' when taking a manual snapshot. #?This will give BackInTime the lowest IO bandwidth priority to not #?interrupt any other working process. return self.profileBoolValue('snapshots.user_backup.ionice', self.DEFAULT_RUN_IONICE_FROM_USER, profile_id) def setIoniceOnUser(self, value, profile_id = None): self.setProfileBoolValue('snapshots.user_backup.ionice', value, profile_id) def niceOnRemote(self, profile_id = None): #?Run rsync and other commands on remote host with 'nice \-n19' return self.profileBoolValue('snapshots.ssh.nice', self.DEFAULT_RUN_NICE_ON_REMOTE, profile_id) def setNiceOnRemote(self, value, profile_id = None): self.setProfileBoolValue('snapshots.ssh.nice', value, profile_id) def ioniceOnRemote(self, profile_id = None): #?Run rsync and other commands on remote host with 'ionice \-c2 \-n7' return self.profileBoolValue('snapshots.ssh.ionice', self.DEFAULT_RUN_IONICE_ON_REMOTE, profile_id) def setIoniceOnRemote(self, value, profile_id = None): self.setProfileBoolValue('snapshots.ssh.ionice', value, profile_id) def nocacheOnLocal(self, profile_id = None): #?Run rsync on local machine with 'nocache'. #?This will prevent files from being cached in memory. return self.profileBoolValue('snapshots.local.nocache', self.DEFAULT_RUN_NOCACHE_ON_LOCAL, profile_id) def setNocacheOnLocal(self, value, profile_id = None): self.setProfileBoolValue('snapshots.local.nocache', value, profile_id) def nocacheOnRemote(self, profile_id = None): #?Run rsync on remote host with 'nocache'. #?This will prevent files from being cached in memory. return self.profileBoolValue('snapshots.ssh.nocache', self.DEFAULT_RUN_NOCACHE_ON_REMOTE, profile_id) def setNocacheOnRemote(self, value, profile_id = None): self.setProfileBoolValue('snapshots.ssh.nocache', value, profile_id) def redirectStdoutInCron(self, profile_id = None): #?redirect stdout to /dev/null in cronjobs return self.profileBoolValue('snapshots.cron.redirect_stdout', self.DEFAULT_REDIRECT_STDOUT_IN_CRON, profile_id) def redirectStderrInCron(self, profile_id = None): #?redirect stderr to /dev/null in cronjobs;;self.DEFAULT_REDIRECT_STDERR_IN_CRON if self.isConfigured(profile_id): default = True else: default = self.DEFAULT_REDIRECT_STDERR_IN_CRON return self.profileBoolValue('snapshots.cron.redirect_stderr', default, profile_id) def setRedirectStdoutInCron(self, value, profile_id = None): self.setProfileBoolValue('snapshots.cron.redirect_stdout', value, profile_id) def setRedirectStderrInCron(self, value, profile_id = None): self.setProfileBoolValue('snapshots.cron.redirect_stderr', value, profile_id) def bwlimitEnabled(self, profile_id = None): #?Limit rsync bandwidth usage over network. Use this with mode SSH. #?For mode Local you should rather use ionice. return self.profileBoolValue('snapshots.bwlimit.enabled', False, profile_id) def bwlimit(self, profile_id = None): #?Bandwidth limit in KB/sec. return self.profileIntValue('snapshots.bwlimit.value', 3000, profile_id) def setBwlimit(self, enabled, value, profile_id = None): self.setProfileBoolValue('snapshots.bwlimit.enabled', enabled, profile_id) self.setProfileIntValue('snapshots.bwlimit.value', value, profile_id) def noSnapshotOnBattery(self, profile_id = None): #?Don't take snapshots if the Computer runs on battery. return self.profileBoolValue('snapshots.no_on_battery', False, profile_id) def setNoSnapshotOnBattery(self, value, profile_id = None): self.setProfileBoolValue('snapshots.no_on_battery', value, profile_id) def preserveAcl(self, profile_id = None): #?Preserve ACL. The source and destination systems must have #?compatible ACL entries for this option to work properly. return self.profileBoolValue('snapshots.preserve_acl', False, profile_id) def setPreserveAcl(self, value, profile_id = None): return self.setProfileBoolValue('snapshots.preserve_acl', value, profile_id) def preserveXattr(self, profile_id = None): #?Preserve extended attributes (xattr). return self.profileBoolValue('snapshots.preserve_xattr', False, profile_id) def setPreserveXattr(self, value, profile_id = None): return self.setProfileBoolValue('snapshots.preserve_xattr', value, profile_id) def copyUnsafeLinks(self, profile_id = None): #?This tells rsync to copy the referent of symbolic links that point #?outside the copied tree. Absolute symlinks are also treated like #?ordinary files. return self.profileBoolValue('snapshots.copy_unsafe_links', False, profile_id) 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 #?(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) def rsyncOptions(self, profile_id = None): #?rsync options. Options must be quoted e.g. \-\-exclude-from="/path/to/my exclude file" val = self.profileStrValue('snapshots.rsync_options.value', '', profile_id) if '--old-args' in val: logger.warning( '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.' ) val = val.replace('--old-args', '') return val def setRsyncOptions(self, enabled, value, profile_id = None): self.setProfileBoolValue('snapshots.rsync_options.enabled', enabled, profile_id) self.setProfileStrValue('snapshots.rsync_options.value', value, profile_id) def sshPrefixEnabled(self, profile_id = None): #?Add prefix to every command which run through SSH on remote host. return self.profileBoolValue('snapshots.ssh.prefix.enabled', False, profile_id) def sshPrefix(self, profile_id = None): #?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 #?\fIprofile.snapshots.rsync_options.value\fR with #?--rsync-path="FOO=bar:\\$FOO /usr/bin/rsync" return self.profileStrValue('snapshots.ssh.prefix.value', self.DEFAULT_SSH_PREFIX, profile_id) 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): """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)) return [] if cmd_type == str: if self.sshPrefixEnabled(profile_id): return self.sshPrefix(profile_id).strip() + ' ' 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 #?deleting and start over again. return self.profileBoolValue('snapshots.continue_on_errors', True, profile_id) def setContinueOnErrors(self, value, profile_id = None): return self.setProfileBoolValue('snapshots.continue_on_errors', value, profile_id) def useChecksum(self, profile_id = None): #?Use checksum to detect changes rather than size + time. return self.profileBoolValue('snapshots.use_checksum', False, profile_id) def setUseChecksum(self, value, profile_id = None): return self.setProfileBoolValue('snapshots.use_checksum', value, profile_id) def logLevel(self, profile_id = None): #?Log level used during takeSnapshot.\n1 = Error\n2 = Changes\n3 = Info;1-3 return self.profileIntValue('snapshots.log_level', 3, profile_id) def setLogLevel(self, value, profile_id = None): return self.setProfileIntValue('snapshots.log_level', value, profile_id) def takeSnapshotRegardlessOfChanges(self, profile_id = None): #?Create a new snapshot regardless if there were changes or not. return self.profileBoolValue('snapshots.take_snapshot_regardless_of_changes', False, profile_id) 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) def setGlobalFlock(self, value): self.setBoolValue('global.use_flock', value) def appInstanceFile(self): return os.path.join(self._LOCAL_DATA_FOLDER, 'app.lock') 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)) def takeSnapshotMessageFile(self, profile_id = None): 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)) 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") def passwordCacheFolder(self): return os.path.join(self._LOCAL_DATA_FOLDER, "password_cache") def passwordCachePid(self): return os.path.join(self.passwordCacheFolder(), "PID") def passwordCacheFifo(self): return os.path.join(self.passwordCacheFolder(), "FIFO") def passwordCacheInfo(self): return os.path.join(self.passwordCacheFolder(), "info") def cronEnvFile(self): return os.path.join(self._LOCAL_DATA_FOLDER, "cron_env") def anacronSpool(self): # ~/.local/share/backintime/anacron return os.path.join(self._LOCAL_DATA_FOLDER, 'anacron') 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): 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' % 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 lastSnapshotSymlink(self, profile_id = None): return os.path.join(self.snapshotsFullPath(profile_id), 'last_snapshot') def encfsconfigBackupFolder(self, profile_id = None): return os.path.join(self._LOCAL_DATA_FOLDER, 'encfsconfig_backup_%s' % self.fileId(profile_id)) def preparePath(self, path): if len(path) > 1: if path[-1] == os.sep: path = path[: -1] return path def isConfigured(self, profile_id = None): """ Checks if the program is configured """ return bool(self.snapshotsPath(profile_id) and self.include(profile_id)) def canBackup(self, profile_id = None): """ Checks if snapshots_path exists """ if not self.isConfigured(profile_id): return False path = self.snapshotsFullPath(profile_id) if not os.path.exists(path): return False if not os.path.isdir(path): # path exists, but is no dir: something's very wrong. logger.error("%s is not a directory"%path, self) return False return True def backupScheduled(self, profile_id = None): """Check if the profile is supposed to be run this time. Returns: (bool): The answer. """ if self.scheduleMode(profile_id) not in (self.REPEATEDLY, self.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 self.olderThan(last_time, value, unit) def olderThan(self, time, value, unit): """ 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 def setupCron(self): """Update the current users crontab file based on profile settings. 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. Returns: bool: ``True`` if successful or ``False`` on errors. """ self.setupUdev.clean() # 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()) # Save Udev rules try: if self.setupUdev.isReady and self.setupUdev.save(): logger.debug('Udev rules added successfully', self) except PermissionDeniedByPolicy as err: logger.error(str(err), self) self.notifyError(str(err)) return False # Crontab modified? if crontab_lines == org_crontab_lines: return True if schedule.write_crontab(crontab_lines) == False: logger.error('Failed to write new crontab.') self.notifyError(_('Failed to write new crontab.')) return False 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 despite the crontab command being ' 'available. Scheduled backup jobs will not run. ' 'Cron might be installed but not enabled. Try the command ' '"systemctl enable cron" or consult the support channels of ' 'your GNU Linux distribution.')) return True 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.profiles() # For each profile: cronline and the command (backintime) cron_lines = [ self._cron_line(pid).replace('{cmd}', self._cron_cmd(pid)) for pid in profile_ids ] # Remove empty lines (profiles not scheduled) cron_lines = list(filter(None, cron_lines)) return cron_lines def _cron_line(self, profile_id): """Create a crontab line based on the snapshot profiles settings.""" cron_line = '' profile_name = self.profileName(profile_id) backup_mode = self.scheduleMode(profile_id) logger.debug( f"Profile: {profile_name} | Automatic backup: {backup_mode}", self) if self.NONE == backup_mode: return cron_line hour = self.scheduleTime(profile_id) // 100 minute = self.scheduleTime(profile_id) % 100 day = self.scheduleDay(profile_id) weekday = self.scheduleWeekday(profile_id) 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}. " "DBus Service '{dbus_interface}' wasn't available") .format(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 {mode}") .format(mode=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 \"{dest_path}\"", self) self.notifyError(_("Couldn't find UUID for {path}") .format(path=f'"{dest_path}"')) return False else: #cache uuid in config self.setProfileStrValue('snapshots.path.uuid', uuid, profile_id) try: self.setupUdev.addRule(self._cron_cmd(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 _cron_cmd(self, profile_id): """Generates the command used in the crontab file based on the settings for the current profile. 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 # User defined path to config file if not self._LOCAL_CONFIG_PATH is self._DEFAULT_CONFIG_PATH: cmd += '--config %s ' % self._LOCAL_CONFIG_PATH # Enable debug output if self.scheduleDebug(profile_id): cmd += '--debug ' # command cmd += 'backup-job' # 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()) backintime-1.5.2/common/configfile.py000066400000000000000000000610201465446530500176070ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 collections import re import logger class ConfigFile(object): """Store options in a plain text file in form of: key=value """ def __init__(self): self.dict = {} self.errorHandler = None self.questionHandler = None def setErrorHandler(self, handler): """ Register a function that should be called for notifying errors. handler (method): callable function """ self.errorHandler = handler def setQuestionHandler(self, handler): """ Register a function that should be called for asking questions. handler (method): callable function """ self.questionHandler = handler def clearHandlers(self): """ Reset error and question handlers. """ self.errorHandler = None self.questionHandler = None def notifyError(self, message): """ Call previously registered function to show an error. Args: message (str): error message that should be shown """ if self.errorHandler is None: return self.errorHandler(message) def askQuestion(self, message): """ Call previously registered function to ask a question. Args: message (str): question that should be shown """ if self.questionHandler is None: return False return self.questionHandler(message) def save(self, filename): """ Save all options to file. Args: filename (str): full path Returns: bool: ``True`` if successful """ def numsort(key): """ 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: keys = list(self.dict.keys()) 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( '{}: {}'.format(_('Failed to save config'), str(e))) return False return True def load(self, filename, **kwargs): """ Reset current options and load new options from file. Args: filename (str): full path """ self.dict = {} self.append(filename, **kwargs) def append(self, filename, maxsplit=1): """ Load options from file and append them to current options. Args: filename (str): full path maxsplit (int): split lines only n times on '=' """ lines = [] if not os.path.isfile(filename): return try: with open(filename, 'rt') as f: lines = f.readlines() except OSError as 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] def remapKey(self, old_key, new_key): """ Remap keys to a new key name. Args: old_key (str): old key name 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] del self.dict[old_key] def remapKeyRegex(self, pattern, replace): """ Remap keys to a new key name using :py:func:`re.sub`. Args: pattern (str): part of key name that should be replaced replace (:py:class:`str`, method): string or a callable function which will be used 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) def hasKey(self, key): """ ``True`` if key is set. Args: key (str): string used as key Returns: bool: ``True`` if the ``key`` is set """ return key in self.dict def strValue(self, key, default=''): """ Return a 'str' instance of key's value. Args: 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 in self.dict: return self.dict[key] else: return default def setStrValue(self, key, value): """ Set a string value for key. Args: key (str): string used as key value (str): store this value """ self.dict[key] = value def intValue(self, key, default=0): """ Return a 'int' instance of key's value. Args: key (str): string used as key default (int): return this if ``key`` is not set Returns: int: value of ``key`` or ``default`` if ``key`` is not set. """ try: return int(self.dict[key]) except Exception: return default def setIntValue(self, key, value): """ Set an integer value for key. Args: key (str): string used as key value (int): store this option """ self.setStrValue(key, str(value)) def boolValue(self, key, default=False): """ Return a 'bool' instance of key's value. Args: key (str): string used as key default (bool): return this if key is not set Returns: bool: value of 'key' or 'default' if 'key' is not set. """ try: val = self.dict[key] if "1" == val or "TRUE" == val.upper(): return True return False except Exception: return default def setBoolValue(self, key, value): """ Set a bool value for key. Args: key (str): string used as key value (bool): store this option """ if value: self.setStrValue(key, 'true') else: self.setStrValue(key, 'false') def listValue(self, key, type_key='str:value', default=[]): """ Return a list of values Size of the list must be stored in key.size Args: key (str): used base-key type_key (str): pattern of 'value-type:value-name'. See examples below. default (list): default value Returns: list: value of ``key`` or ``default`` if ``key`` is not set. ``type_key`` pattern examples:: 'str:value' => return str values from key.value 'int:type' => return int values from key.type 'bool:enabled' => return bool values from key.enabled ('str:value', 'int:type') => return tuple of values """ def typeKeySplit(tk): t, k = '', '' if isinstance(tk, str): 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) 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])): continue 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])): continue items = [] for tk in type_key: items.append(value('%s.%s' % (key, i), tk)) ret.append(tuple(items)) else: raise TypeError('Invalid type_key: %s' % type_key) return ret def setListValue(self, key, type_key, value): """ Set a list of values. Size of the list will be stored in key.size Args: key (str): used base-key type_key (str): pattern of 'value-type:value-name'. See examples below. value (list): that should be stored ``type_key`` pattern examples:: 'str:value' => return str values from key.value 'int:type' => return int values from key.type 'bool:enabled' => return bool values from key.enabled ('str:value', 'int:type') => return tuple of values """ def setValue(key, tk, v): t = '' if isinstance(tk, str): 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) if not isinstance(value, (list, tuple)): raise TypeError('value has wrong type: %s' % value) old_size = self.intValue('%s.size' % key, -1) self.setIntValue('%s.size' % key, len(value)) for i, v in enumerate(value, start=1): if isinstance(type_key, str): 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]) else: self.removeKey('%s.%s.%s' % (key, i, tk.split(':')[1])) else: 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])) elif isinstance(type_key, tuple): for tk in type_key: self.removeKey( '%s.%s.%s' % (key, i, tk.split(':')[1])) def removeKey(self, key): """ Remove key from options. Args: key (str): string used as key """ if key in self.dict: del self.dict[key] def removeKeysStartsWith(self, prefix): """ Remove key from options which start with given prefix. Args: prefix (str): prefix for keys (key starts with this string) that should be removed """ removeKeys = [] for key in self.dict.keys(): if key.startswith(prefix): removeKeys.append(key) for key in removeKeys: del self.dict[key] def keys(self): return list(self.dict.keys()) class ConfigFileWithProfiles(ConfigFile): """ Store options in profiles as 'profileX.key=value' Args: default_profile_name (str): default name of the first profile. """ 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): """ Reset current options and load new options from file. Args: filename (str): full path """ self.current_profile_id = '1' super(ConfigFileWithProfiles, self).load(filename) def append(self, filename): """ Load options from file and append them to current options. Args: filename (str): full path """ super(ConfigFileWithProfiles, self).append(filename) found = False profiles = self.profiles() for profile_id in profiles: if profile_id == self.current_profile_id: found = True break if not found and profiles: self.current_profile_id = profiles[0] if self.intValue('profiles.version') <= 0: rename_keys = [] for key in self.dict.keys(): if key.startswith('profile.0.'): rename_keys.append(key) for old_key in rename_keys: 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: self.setIntValue('profiles.version', 1) def profiles(self): """ List of all available profile IDs. Profile IDs are strings! Returns: list: List with strings of profile IDs as strings. """ return self.strValue(key='profiles', default='1').split(':') def profilesSortedByName(self): """ List of available profile IDs alphabetically sorted by their names. Profile IDs are strings! Returns: list: all available profile IDs as strings """ profiles_unsorted = self.profiles() if len(profiles_unsorted) <= 1: return profiles_unsorted profiles_dict = {} for profile_id in profiles_unsorted: 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())) # return the names as a list return list(profiles_sorted.values()) def currentProfile(self): """ Currently selected profile ID. Profile IDs are strings! Returns: str: profile ID """ return self.current_profile_id def setCurrentProfile(self, profile_id): """ Change the current profile. Args: profile_id (str, int): valid profile ID Returns: bool: ``True`` if successful """ 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.changeProfile(profile_id, profile_name) logger.debug( f'Change current profile: {profile_id}={profile_name}', self) return True return False def setCurrentProfileByName(self, name): """ Change the current profile by a given name. Args: name (str): valid profile name Returns: bool: ``True`` if successful """ # Find the profile_id to this name... for profile_id in self.profiles(): if self.profileName(profile_id) == name: # ...and set current profile by this id. return self.setCurrentProfile(profile_id) return False def profileExists(self, profile_id): """ ``True`` if the profile exists. Args: profile_id (str, int): profile ID Returns: bool: ``True`` if ``profile_id`` exists. """ if isinstance(profile_id, int): profile_id = str(profile_id) return profile_id in self.profiles() def profileExistsByName(self, name): """ ``True`` if the profile exists. Args: name (str): profile name Returns: bool: ``True`` if ``name`` exists. """ 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. Args: profile_id (str, int): Valid profile ID Returns: 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. Args: name (str): new profile name Returns: str: new profile ID """ profiles = self.profiles() for profile_id in profiles: if self.profileName(profile_id) == name: self.notifyError(_( 'Profile "{name}" already exists.').format(name=name)) return None new_id = 1 while True: ok = True if str(new_id) in profiles: ok = False if ok: break new_id = new_id + 1 new_id = str(new_id) profiles.append(new_id) self.setStrValue('profiles', ':'.join(profiles)) self.setProfileStrValue('name', name, new_id) return new_id def removeProfile(self, profile_id=None): """ Remove profile and all its keys and values. Args: profile_id (str, int): valid profile ID Returns: bool: ``True`` if successful """ if isinstance(profile_id, int): profile_id = str(profile_id) if profile_id is None: profile_id = self.current_profile_id profiles = self.profiles() if len(profiles) <= 1: 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: return False if self.current_profile_id == profile_id: self.current_profile_id = '1' return True def setProfileName(self, name, profile_id=None): """ Change the name of the profile. Args: name (str): new profile name profile_id (str, int): valid profile ID Returns: bool: ``True`` if successful. """ if isinstance(profile_id, int): profile_id = str(profile_id) 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 "{name}" already exists.').format(name=name)) return False self.setProfileStrValue('name', name, profile_id) return True def profileKey(self, key, profile_id=None): """ Prefix for keys with profile. e.g. 'profile1.key' Args: key (str): Key identifier. profile_id (str, int): Valid profile ID. Returns: 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): """ Remove the key from profile. Args: key (str): key name profile_id (str, int): valid profile ID """ self.removeKey(self.profileKey(key, profile_id)) def removeProfileKeysStartsWith(self, prefix, profile_id=None): """ Remove the keys starting with prefix from profile. Args: prefix (str): prefix for keys (key starts with this string) that should be removed. profile_id (str, int): valid profile ID """ self.removeKeysStartsWith(self.profileKey(prefix, profile_id)) def remapProfileKey(self, oldKey, newKey, profileId=None): """ Remap profile keys to a new key name. Args: oldKey (str): old key name newKey (str): new key name profileId (str, int): valid profile ID """ self.remapKey(self.profileKey(oldKey, profileId), self.profileKey(newKey, profileId)) def hasProfileKey(self, key, profile_id=None): """ ``True`` if key is set in profile. Args: key (str): string used as key profile_id (str, int): valid profile ID Returns: bool: ``True`` if ``key`` is set. """ return self.profileKey(key, profile_id) in self.dict 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): self.setStrValue(self.profileKey(key, profile_id), value) 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): self.setIntValue(self.profileKey(key, profile_id), value) 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): 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 setProfileListValue(self, key, type_key, value, profile_id=None): self.setListValue(self.profileKey(key, profile_id), type_key, value) backintime-1.5.2/common/configure000077500000000000000000000247021465446530500170450ustar00rootroot00000000000000#!/bin/sh #clean up if [ -e Makefile ]; then rm Makefile; fi #tmp files MAKEFILE="$(mktemp)" UNINSTALL_FILES="$(mktemp)" UNINSTALL_DIRS="$(mktemp)" # set default options PYTHON="/usr/bin/python3" USR_BIN_FILES="backintime backintime-askpass" usage () { echo "Usage:" 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 "--python=PYTHON_BINARY" echo "\tuse PYTHON_BINARY to start Python3" } addInstallFiles () { file=$1 dest=$2 mode=$3 if [ -z "$mode" ]; then mode=644 fi for i in $(ls $file); do addInstallFile "$i" "$dest" "$mode" done } addInstallFile () { file=$1 dest=$2 mode=$3 if [ -z "$mode" ]; then mode=644 fi printf "\tinstall --mode=$mode $file \$(DEST)$dest\n" >> ${MAKEFILE} addUninstallFile "$file" "$dest" } addSymlink () { dst=$1 src=$2 printf "\tln --symbolic --force $dst \$(DEST)$src\n" >> ${MAKEFILE} addUninstallFile "$src" } addInstallFileRename () { file=$1 dest=$2 mode=$3 if [ -z "$mode" ]; then mode=644 fi printf "\tinstall --mode=$mode $file \$(DEST)$dest\n" >> ${MAKEFILE} addUninstallFileRename "$dest" } addUninstallFile () { if [ $# -eq 2 ]; then file=$(basename "$1") dest=$2 path="\$(DEST)$dest/$file" else path="\$(DEST)$1" fi printf "\trm -f $path\n" >> ${UNINSTALL_FILES} } addUninstallFileRename () { file=$1 printf "\trm -f \$(DEST)$file\n" >> ${UNINSTALL_FILES} } addInstallDir () { dest=$1 printf "\tinstall -d \$(DEST)$dest\n" >> ${MAKEFILE} addUninstallDir "$dest" } addUninstallDir () { dest=$1 printf "\tif [ -d \$(DEST)$dest ]; then rmdir --ignore-fail-on-non-empty \$(DEST)$dest; fi\n" >> ${UNINSTALL_DIRS} } addComment () { printf "\t# Install $1\n" >> ${MAKEFILE} printf "\t# Uninstall files $1\n" >> ${UNINSTALL_FILES} printf "\t# Uninstall directory $1\n" >> ${UNINSTALL_DIRS} } addNewline () { printf "\n" >> ${MAKEFILE} printf "\n" >> ${UNINSTALL_FILES} printf "\n" >> ${UNINSTALL_DIRS} } onTravis () { [ "${TRAVIS}" = "true" ] } #get commandline arguments unknown_args="" for arg in $*; do 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" fi 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 #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" done #start Makefile printf "LANGS=$langs\n\n" >> ${MAKEFILE} printf "PREFIX=/usr\n" >> ${MAKEFILE} printf "DEST=\$(DESTDIR)\$(PREFIX)\n\n" >> ${MAKEFILE} printf "all:\tbuild\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 config-example-*.gz\n" >> ${MAKEFILE} printf "\trm -rf doc-dev/_build/*\n" >> ${MAKEFILE} printf "\trm -f .coverage\n" >> ${MAKEFILE} printf "\trm -rf htmlcov\n" >> ${MAKEFILE} printf "\n" >> ${MAKEFILE} # 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__" addInstallDir "/share/backintime/common" addInstallFiles "*.py" "/share/backintime/common" addNewline addComment "plugins" addUninstallDir "/share/backintime/plugins/__pycache__" addUninstallFile "*.pyc" "/share/backintime/plugins/__pycache__" addInstallDir "/share/backintime/plugins" addInstallFiles "plugins/*.py" "/share/backintime/plugins" addUninstallDir "/share/backintime" addNewline addComment "documentation" addInstallDir "/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 "../CHANGES" "/share/doc/backintime-common" addNewline addComment "config-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" 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" addNewline addComment "application" addInstallDir "/bin" addInstallFile "backintime" "/bin" "755" addInstallFile "backintime-askpass" "/bin" "755" addNewline addComment "autostart" addInstallDir "/../etc/xdg/autostart" addInstallFile "backintime.desktop" "/../etc/xdg/autostart" addUninstallDir "/../etc/xdg" addUninstallDir "/../etc" addNewline addComment "bash-completion" addInstallDir "/share/bash-completion/completions" addInstallFiles "bash-completion/*" "/share/bash-completion/completions" addSymlink "backintime" "/share/bash-completion/completions/backintime-qt" addUninstallDir "/share/bash-completion" addNewline #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# 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 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} done #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" done addUninstallDir "/share/locale" addUninstallDir "/share" addNewline #uninstall printf "uninstall:\tuninstall_files uninstall_dirs\n\n" >> ${MAKEFILE} printf "uninstall_files:\n" >> ${MAKEFILE} cat ${UNINSTALL_FILES} >> ${MAKEFILE} printf "uninstall_dirs:\n" >> ${MAKEFILE} cat ${UNINSTALL_DIRS} >> ${MAKEFILE} #test for i in "pytest" "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 information 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}" 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 preferred over simple python3 printf "\t${PYTEST} ${v}\n" >> ${MAKEFILE} fi printf "\n" >> ${MAKEFILE} done # For details about the "coverage" package args see: # https://coverage.readthedocs.io/en/stable/cmd.html printf "coverage:\n" >> ${MAKEFILE} printf "\tpython3 -m coverage run --source=.,./plugins,../qt,../qt/plugins --omit */test/* -m unittest -b\n" >> ${MAKEFILE} printf "\tpython3 -m coverage report\n" >> ${MAKEFILE} printf "\t# To show an interactive HTML coverage report use:\n" >> ${MAKEFILE} printf "\t# python3 -m coverage html\n" >> ${MAKEFILE} printf "\t# firefox htmlcov/index.html\n" >> ${MAKEFILE} printf "\n" >> ${MAKEFILE} printf "integrationtest:\n" >> ${MAKEFILE} printf "\ttest/test.sh\n" >> ${MAKEFILE} #copy Makefile mv ${MAKEFILE} Makefile chmod 644 Makefile #clean up for i in "${UNINSTALL_FILES}" "${UNINSTALL_DIRS}"; do if [ -e "$i" ]; then rm "$i" fi done # check python version PYTHON_VERSION_REQUIRED="3.8" 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" backintime-1.5.2/common/diagnostics.py000066400000000000000000000323021465446530500200120ustar00rootroot00000000000000# SPDX-FileCopyrightText: © 2022 Christian BUHTZ # # SPDX-License-Identifier: GPL-2.0 # # This file is part of the program "Back In time" which is released under GNU # General Public License v2 (GPLv2). # See file LICENSE 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()) == '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.keyringSupported() }) # Git repo bit_root_path = Path(tools.backintimePath("")) 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) # This doesn't catch all edge cases. # For more details see: https://unix.stackexchange.com/q/202891/136851 result['host-setup']['display-system'] = os.environ.get( 'XDG_SESSION_TYPE', '($XDG_SESSION_TYPE 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)') # RSYNC environment variables for var in ['RSYNC_OLD_ARGS', 'RSYNC_PROTECT_ARGS']: result['host-setup'][var] = os.environ.get(var, '(not set)') # === 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'] = {} 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([]) 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() } qapp.quit() 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)provided 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) backintime-1.5.2/common/doc-dev/000077500000000000000000000000001465446530500164525ustar00rootroot00000000000000backintime-1.5.2/common/doc-dev/1_doc_maintenance_howto.md000066400000000000000000000123201465446530500235410ustar00rootroot00000000000000# Using Sphinx to write and build documentation Feel free to [open issues](https://github.com/bit-team/backintime/issues) or contact the [maintenance team on the mailing list](https://mail.python.org/mailman3/lists/bit-dev.python.org/) if this text is difficult to understand or not helpful. This file describes briefly how to - build and view the source code "API" documentation of _Back In Time_ "common" (CLI) - add new modules to the documentation - write docstrings - known issues with documentation generation ## Index - [Background](#background) - [How to build and view the documentation](#how-to-build-and-view-the-documentation) - [How to write docstrings for Back In Time](#how-to-write-docstrings-for-back-in-time) - [How to add new modules to the documentation](#how-to-add-new-modules-to-the-documentation) - [Commonly used rst markups in the docstring](#commonly-used-rst-markups-in-the-docstring) - [Known issues with documentation generation](#known-issues-with-documentation-generation) # Background The documentation is generated automatically from the docstrings in the python source code files using [Sphinx](https://www.sphinx-doc.org/en/master/) in combination with the following Sphinx-Extensions: - [autodoc](https://www.sphinx-doc.org/en/master/man/sphinx-apidoc.html) to automatically generate rst doc files from the python docstrings. - [napoleon](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html) to convert google-style docstrings to reStructuredText `rst` format required for autodoc. - [viewcode](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html) to create links to browse the highlighted source code. Further readings: - [Brief introduction to Sphinx for Python](https://betterprogramming.pub/auto-documenting-a-python-project-using-sphinx-8878f9ddc6e9) - [Quick reference of rst markups](https://docutils.sourceforge.io/docs/user/rst/quickref.html) # How to build and view the documentation Open a terminal, navigate to the folder `common/doc-dev` and call make html # to generate the HTML documentation make htmlOpen # to open the browser showing the generated HTML pages # How to write docstrings for _Back In Time_ _Back In Time_ uses the [Google style for docstrings](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings). Please stick to this convention. Look into documentation of [`sphinx.ext.napoleon`](https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html#example-google) for extended examples. # How to add new modules to the documentation There are two scenarios: ## Scenario A: New module files are in a separate folder (not yet included in the doc generation so far) - Add the python source code folder to the file `doc-dev/conf.py` which is the configuration. Then the `autodoc` extension is able to find the files (navigate _relative_ to the `doc-dev` folder). - Generate the initial `.rst` files for the new modules via `sphinx-apidoc`, eg. sphinx-apidoc -o ./plugins ../plugins This example will create a sub folder `doc-dev/plugins` with the `.rst` files (one for each source file) in `doc-dev/../plugins`. - Add the new modules in the sub folder to the top-most _root_ `index.rst`: # under "modules.rst" add this line add a link to new modules plugins/modules.rst ## Scenario B: The new module files are in a folder that already contains other modules contained in the doc To create the initial version of `.rst` files for new modules eg. in the `common` folder use sphinx-apidoc -o . .. _TODO_: How to remove old rst files with non-existing python files (eg. due to renaming or deletion)? Probably the -f ("force") argument could do this. Try it with -d ("dry-run")! # Commonly used rst markups in the docstring Despite using the Google docstring style rst markups can and should still be used to format text and cross-reference code. - Reference a class (with namespace if not in the same): :py:class:`pluginmanager.PluginManager` Important: Don't forget to surround the function name with back ticks otherwise Sphinx will not create a cross reference silently! - Reference a method/function: :py:func:`takeSnapshot` - Reference a module: :py:module:`datetime` - Specify the python type of an method/function argument: Add the type name (with namespace if not in the same) in parentheses """Short description... Long description... Args: cfg (config.Config): Current configuration """ - To indicate verbatim text (inline code) enclose it with two backticks each. ``True`` ``None`` ``de_DE.UTF-8`` # Known issues with documentation generation - Elements of PyQt can not be referenced. It is a [known Issue](https://riverbankcomputing.com/pipermail/pyqt/2013-March/032528.html) without an acceptable solution. Name them via verbatime text (two backticks) only. - Sphinx' ``make html`` does not recreate the html file of a sub class if only the parent class docstring was changed. _Impact_: Inherited documentation in the sub class is not up to date _Work around_: Use ``make clean`` before ``make html`` May 2024 backintime-1.5.2/common/doc-dev/2_from_weblate_01.png000066400000000000000000003076721465446530500223660ustar00rootroot00000000000000PNG  IHDRt-*sRGBgAMA a pHYsMMgIDATx^xiͱ=g?6;`%$xB]!;Iǥ+!d&03}\Ueyw`v K`v K`v K`v K`xDC{7J4'` H/]?볳_eE/:Xl+q?˿k,/Z9@.0w}7KzbAދXx7$W]/iy s\`>ߵ$^Ҳ%;%%r r #\`\  ;%%r r #\`\\O׉_kM]bٹ5e:0?MoE + Ӕe|௚ŅY eȼ\!]_]ܵ6n#6d;rO<;.˳_8Y%z~r[.}MM"r?7HWyMI=u,<ѿz>.%bOKgamhz ѿs$SJ~0k %uD;:n#7$+{FDKC㳟2TXM[pPŪ^p⿫Jf}n*'6-DzzFfZJZRBBZ]7x}SyGDfw@ni宯1ݷjUl k//\c}SL]DkԈ\;2DĬt;gyܩ M;Z ~|rO(˙Ywa=/)JH߻3S| e/~{ [7or^׊Ar{ѫ1: GY#5X^t 99X>zw+K3:pljȼ\r_7"^v78O/#uò؏]Y E\lJ(`!X)-Ӯ=L^lse?1\ëaAS`P{Mn^Ξa|?fmI]=]=}f]dCxP<|?tB=:O ݯ]ͺb45r9D$y#}VJuyN F}}S.3?ZL mn856:%ghmm\dge7Ե_@r_71O[$r0ԐZ0NvU[_mņ/v6uba׉KIaFm"pq}A5dy{mAR\laO3ݯ|{*hY)0&ZJj6}y#7-1!5s^\'wMѨ9]ƛHIHwGz3IU]5v5WOi{mβĤvFg .&&mwCP yii()6.5ittI''P۽9ԵtOcfʢR jsxxJ>-{WeJ\Lzq%/7f$DŤп~vBo95ZZشӒ𭁘˰=J- DF}zwFԹU.g/edf?sQi-XX2W2=Ai":#1|e#G*9dO?{ G p )&Ci.k""N4{h?8_כ Hu^'&wY½J[2Qc#4SC4d6G qLMaop23VFo3pMR\Yan+uY0Sr/ #cyM/3N&64BO/5ٝ㱞W[ƻp m.8 /w::ܺۍ ~Wil'5C_8M?bּ;eNscI'5HφDmnJ+se9I!Bʉcr ~2ǁ[ګw-reR5^~rab Emx|E)I$G=h3eǿR[fӝԘNe#3x`0)Fw9$*ЯXi9tD7{9YݬDk\Zbz  [0Xw}9I}Gc\?íG #3`Kwa+ODZu]ǷHQ6ykn'5c]0ƺh9gۡR}}}===]]]w$KWuufhc62lgWrmrNm9[$ vvvA!S>*H.{U{$4rڅ,⑅>طOѻHC&'l|ÑŰW!mmmFWWҿ\yOl-6kFf;i#1һW]C-]{;gn %mṉfmܵ[xO3ӹ[.*Fw}}e?uL- WΞ>:~^w"oDq4F :O]OقEnRl翕`8 ?{$DnΦO+rtI%]A+sD{&1-ۤ.n]쯂'6l㹨δ^ԉIL>%[_A*YVo3ǁ>>//Ԑ}:,OP347yщd}V)V%4g>DIr2\!qtⰿ??Ç۷oϞ=ǏOHH ^l@ cgv_ ;d<&<Ӻ(v%1[{DKK˹s岱YZzKeS.)&Jkm!}'ZHע@$355a 0LKI'v,@rpdM\\ܜ=U&ٿ+߇6:#^O u}/_y'yA&_VOq&e"S);4.Oiҕq(YޡcX!k0E 9Ϊq]7Hbg;2_ŌkUcr< &GNa4v|{;?_C˥onw;ⷪ/O[99BaPXk1}3j r2*wAaF@kf'ؕ(b,r'b|C.??ŬUWW3HAgnJ,j%6&)3ūWuEH"Re6|{8LrR& tX9viPy>iE99y-eT_mxK /u8_%H\?%FEzcmXvqdouGc;SL.b3;os3v0$ו'#Ïll:_(g+O7_ePWOSWó;ۊl_)7eR# r CCC/AA&Oy$ =OPs:&6_šBҪ)wZsbjߴͪV4i$G-ɖ煱 D^kX V0~lY+ri)]Β,xΈ* 1=jm~7Py_J餮7J];t(j-S5!zdcF!zre?Ѫy1Zglw@UF 2tEJ?)Z1fZEee}[4j #0o`1yWĖc EfZ^Yڤ /"{dz~iemK$s7a!Pr8: ի_c ߐKZ3zAoivr ۯ?1Υ&gf4Ar9U)f>N $1d᷏RF irgN}G&CCC &,*ms~FE.;ctO `,\'C/f0Jˬr'a\w޾G;zXd1uw=&唃Ng;E Bj&zs<6L?vbO vvˆ=rŋniX?]@_mYbͻbW*ZZH=Æ^\# *H^>sNyt~cGo=wر3R&!t-Pr=)UErqh6;@\FVFAWyW1fߙ~%bKwDdExdc7}kR4}ΜW k|9]A3_#xq~_nu4|48o>PUhҁ{ T7DU454 -<ҐI9(LyD|*_BkI<{"gRm ^1} W=+q6Z"5l,h/]; uR˞1) {gnZN8zuFo5J^Xv@>&$ʾ[ިYXW Ju=J- <2ʦ\QQrAc᳷(/Aoɳ&~W4r6W;G.cMeٹ%5S^WToűβlR[[ɞQeɊ*1ɾœqƖa ԦA⥏hM}#})}U]C Z:l{cuesVfkr2*臄DOuI~nvAEu!Yl()gnZQ]5JjCme[.ECZrs z&3ږn{>n!1^_7|w}]>PӋwϑ 3ZH{Y0Vm+U0).*,18qq&FOee"o(([Yh(/)n'N>&k_Yƪ~6Z]\Y;H_ʢ\Kۇ,O7WfdM17Yҿ_u2/3^3]__2<ܪA0ZWPه78(8rII>{2}i Υa{(`_@˼~ne6A/^5 J)ˡ,/yUQ&3`fS s};{D=~({?#ŗk* r +7}MƵߍks e9YySӽMJmxeMTde5|X__7v/y"C ~q>;p:恷s偎Ҿ6q,Z ed'eU L4ue9)b*9)3%w {H_G[mzg{umfohi{yౢUR~yjJ^J}nAϪ5){A*OyGDA~OYeK>oG9~?콼k8O(]1}ld}ؓXQ?C?vN8]RnO{wx\s MM8xo޿tՓ:$}~7ooT-_)<; DMaAA1Iyi) gx;.?x~6۲:`s{N^n GWzb?u,KW}\~pЕyA ByMn %X"uUe6ƞSK;/X9?M?Q%'"?Y@.H\`\  ;%%r r #\`\  ;%]0l3_qIkz s\`8ص[~1W?صﲥG;%w/wǻ?٠߲=QZ@.zF瓻t h  \;%c\;%c\;%c\;%c\;%c\;%c\;%c\;%c`r<427?@ |pv-/4wututC @ ]m]#@ ȏ̮Ҋ)8 @ 1UXVI$o @ |@vUN\B @v" @  %@ dr @ ٱ\B @v,!$$ybb;ɩ,;15?Զ!ϻ@ i )DB67]r45Q_W_]][]S[?Jx5955O'MvuM|t{q @~ANI&O w66wvM)F{mQ.'3)ڌ;XFJ^ֱɏXاܚ39J5~*# 'gӴ :'I_g4=ؒ$q @~"S{.sӷ=!=G6r9¬=O[%+l9i-Qѫ$2u~qeqiy~J4gSuQt' hťi e|SHY"$irlbB&H,Ц2jPh+)/')h"+ h")OQh-ϣY*ubc$ 'lʛC&g',ɒG[bG߂fu -46h89^b hg.zU@~vp=﯎aj"(}ytω|?'uRQBKw 'dr{Q$?l{Mtuew5:(IsѮ [n_ H"LLL"In+~}c'md-Pr 7 M-Ëې$Ntڨ'Be ےf7[dv8Uj2LHW/}I!R}C הLm'f;ʽ͔r]qNh"S'ju^`W20v h&f[͢ 1Bs?oxQguva+@ Ϙ;sRvyy%42A]|dl<۝'S(mښKVmaqmyGiX މ5uEn"9 3WiM>Ct!ܶis 3c-BX%3닓_p_>XIY*aPIhV"%Z+^t}~(q!ĒJ-+r% 遦WPfTvU5{>3Q&vmy9<-Ū?[ョe9G-,M\`//Q \$ݱ^[L>~j67==LkP;yM{2=GSfVhkr]Vr8;vZ(}{Q,">DCJDTZ'irxV y:AI6r!LſpKPH3#UBB13󔡶 kM!!DzzA] 3hzB(/>5K׿Z,v%V%kH jwNS)Diq)5Êѕ\i1a Eئ 'IRi;6~q\a zfD~XhLI'Bi)4PS4NLJ@ yh:zôܰļ2S[a956qwK,nD/Jum4u@!OMͭ6;w5w4773;?a.PgԅQ"s33sO "Qf3bOe'=}" ٜ[-u2h!mU7e除Q 37~jhMev~f`nv:3;7===InLqАy8hSdlǼcτCo9C @>LGϳytϾ|^ye-Dy|Lͭ{}y1g\ |fIL`M@ &@ ^@.!@ ;K@ Ȏ@ c@ X@.!@ ;K@ ȎeWQy2C$C @ ?2 ʪ K+ @ ]e54m ~4\1@.1@.ࣰ80?Ks4Ē ;XAQH {7%m)eqM E_JJ*Z:?,, :{GX8Hk"T5U3S7e3s%mc*FVN/Bz|+ ?ШxYu}-#e_P)hYBKH,:Ce'ߣ ,̌爞qX޵)侁eued(gkʭkf2j.]=}ĒydJtю̸nyºx^I…[6Dϧ@'˔QΛ1_ %Ӗw>OZo8|0syVB-&)u#c=N̅3./ήM#KE-m뎂]o ^Mrٮ{+6L[[:K.t%Zg=N:ηTdfmThc.%f LoiR\N43¥dit7SxhKJZzyK{GGS41`43%lh;r.1Vj2jc̶T'em&gO礕t``5JY~JVIMGw[Kg:ݛYe]S:sАWFF0!u%l-.RKrz鋵6᭧M"z<7Dy@a;35%y5xmezB,gh[c2#3#e`F5Q1l8:#XήlQJ 6N3WHj,J-ikh䬬ʶ 3Y`0Y,S;1N. ײn׺jrJƶ @ANvmh=yr9SfP˶sDz_cabVa.Egy!ǕQn{G?*Yl=ՙiLO)tVo]e7Ng_l6VRkmX{Y\F^7VD i Y cZu!F:ʓ3қ賰ҙMI5oB_o侚ԌaRx0ZCM-Ӗ*7j+ ϐ:+Rr;6#lKyNbAz/69={>lBw<]H0oX$6WWU;4=6O~Yn~LLLZ9*hmsB5 Mm](ӯ^.W[}wIwh4⯼]iD(Ϣ]Գ*F*OCRCVIlbo ]I'g+;gYʵ Vx s3gsG;GZ#e #$(:nnk~]+;+%!84`.YKYM-dҁz*VRLEԑ42q1׹y%N U] oG"▮N?SG!\Ȧ+ʙ>2+v.~z62wH;c/UVH\UO#hB7Xdm7_NBDEG3^ )Qik7[KY~9j^3-b1TPQEK't Iyy_ILċ*7Z.bXŔ6LDvэTRp,1Vw垲G6S'``l``&A*'`AUZ̮`k;/=JV!QȾ yBq^''"knkgrqD}[r\DHوLP=F~"#x%C]j4K}ɶ$8HI7omsacIih_%=5T35Ǽee&bhi.y\ lmyej񁃅JvqB[-z|8L<3rmG{CZ蛧MEBF$bl_#K"z~<S-?he1/w{96ɸmʶZ[N~d8M] W0H/^GR+X#RLVGE GqQ鮸}s "AkD ] ~</'!5{c&RNXKjC 76"b>cbCےtۮs/(D^ p̝Kn +:ᨣ!v$6mԖq4>]{D}w>_/U}f1d}[9~T4ЀhU%W ^6t1!z-u~*(z-rF׿pg6])U2zRɫیWFk%woFor'c4gqbl jjWuW4<9h6(ϩNom}@/h^6fi=U .J>-ǻ Zc*=WFtO[w}LV(Gg#vffƢ񽸹x JK H_Y0qrpC)E[c3%8$<2?R ۼpPݾ3ּx[/fV 0?F4 쌬4 ?bfijn.ak*n s|1]\}!1q8_|A޵ [NzdK[_[< ]"?>>1,&!,6;"^/QMZ;m6U%\q-~\j_Mn[AQlV7qs7M#jx3=r_)briIE↓}PTH:8.#T@\z+(ҏ9Tۀ[L% ] Eo@@<8)#2&1,:!*te1}fH.cM֜UiM$vuu~0R5> |j'OϮy78`4&(&Y] lWF}H.->t2}/u]aeR@(^*fU,䣗+^e{!1%bҊIsiM.\#.B76Oy5dֽtr%!T1kb҄fAGm!GCl%׌ͿGd-tc5Xl/ma@%:ʱ*F%&'a㤨elx\ (vq1&Ngj\+RMar9%.M2SӱiF'&Mt)'Pb9ʟKLAcFĦd<+o3v(mXs܆ސQm{"\@ 6 wVn˓"sYvRL.՘E*v8c1}4A{5 -mw <5=;WѱqdCSoQM`V^1}k痔WYػ}_"[5|(ogǹ]GԼ ۮYLdh\jUyV,;?(k"]^t./,<$M9H-s_/̅xMCr ^[8̒Kof>fLRJ(9np[2T؏(y&_"S4pp#H]^襵)w^sVQt)qj`(%jx{/Yd)jwVxʕmTs=G;PiF7L6d)xK#+/؞tU֦ ^E-XIQՉ 1atSl{ lj9c'RA7oӷyW})4]jSeQ|kOpm\ƚa;T]Zs{t Z a Al3z:NpSf ڄNrY@̑׸+_ԍ;~wA 5frIjw6)zgJKJZ#z_4vY9!CQ4DG"!|le^B#(o|Et1E`5 Anu[;W&IY9NQV `zm+u=ڐf fJnp:E?GZ.],ec Pcox\P3{Oŷ|* (fKEJ脠hQ1]w6_|K,R á;nmY䈘 \w1q+x".Aܦ0n XhP O }SڃIM c ׀LnvQIR|Y[P8q_(}BW-Eאog}.qZݖXSn% zkr˯= )'* %Vs x׀;P5EqK]0DIǸ:F 巑xl,&p6c|uB o4zTmCҹ<ڊx>Y93>oe?y9P&& Vi\w{GfUh+^S 4fq:H]W?")MtSzG?'܀v':>1z21]Z %akm\k@jm@W>8":X](yO)WQ }YXXt|amHҍcǡj+,u+j[; px"a[8Z;?u idz"bCm]=" @jC3e 1A6|BE,"6J[\\LA?lz#C^\jlPdq&&,'<:rt;55LcUuo.J o$Еi>hxtHͅ?73 xatn LOep@O`TZ-v'q D)9g,WgzEvN6b>;Rkp]sаH_hOC[r9~gxĤvcoψ įcdjh۫j7~å6?/*S'2c=j&^5r2v:sa^~aYOw${mK0doPIYyڟ<<ԅšyӲk2:[ZҀƲA-rkHtw^IˮG^l)"|m yXA?Z<߰j\00Cr}~ZWM=4 S=G&Vm^ޚσv\6ozLUvƌ-$xXܹq#-őXg9#axXJEk; 2i-p}h_vtܖlqlbqv-P}`Gɤ%l۫C^ iq~!Cܞ [#½G c$?W~U[D`Eyėeܩ(oߐfU6҅qe_pb ~|Rx狨Kh39Sjdo~Kr!L*6`EM#h" KZVGJ ~㱒vtv&ir~avtzXQ Y y@[5/='}+K&S҇Y_[Gm%k\_& uuu7EVlrH I,n i:7#~&u:*E^/7ˤʳ-4֗ y}*p%HyMüQ ~W7V+im0,.݇]5,ĥdF&PS72 IHt:wGxwb z >IEt:VN{rrJDNO:, zzK~%rI[Uq QUI~yޚ譬e"<.(2FMEūLw˫ori`3ڟcu!9lNgOަ{[5 hiR| )H4|ee]JEun XRu]2o ,ZQUW(CKcK'I%Ĵ,lxn VY:,"waaqnnɨ̼W0-VkD,MՕdDd b`|TG;+bCK:4$,Ύumk~۬G5l֦[ۉ_# =\jd`7uO˨!(,4ݱtQݼ#;,kUMd:vI9vDai.].p)]zKz^A)^\XX0tx)r~ \:>Q_D>bhe֯H.>>ݘ\"ڦY?pv++؉`5=9u ޽n!knn~yy+\ƙL˥ L JʥȥnL"3%tv~1xU.=/..Ql]?nqPR: HO߀% m`A7B|\rhosM=a5S:TtLbPʥ: !-1kxGƴ>%mc2\ 7ini7r2s}y02cj"=.GMuu{;GdNVMo/H.3&.Eع6v_$cRQ'uMm?`uMLN=qyqf{tLM ,bB\zn^Ɏjzꙩk[ ]idel:ۀ,K:pkp4>u!h|-#-x5 GAEhtD(x: G@hBf{GnĎ ]P>*?.1э^w{}pKO->݃"nDKد,*VVȏ#&[(iٹϿcK&93GZEwl"[=~+ ʪoxmedM-\|U5r7ZNݠX[@bmĠU4JN!«\| kkkQoZI؏UKŋH9u}_É)i˪K:@.~,9H-EETFMکX7r Ox4Dq~9GKio`X7r cOR46S6ANFG/%ܢ9W1Q<>949E,r VF'GFFr^R^?8D,;r  r  r ʫjNq-,klo@ @~dvVԬ/.-A @ ?2p%c\;%c\;%c\|Db>KOԬ<?=S;9uq͟#::6Nς"b+HS, ?3kkv*f2jzZF:&zf(j?}*ikHٺF%V@.~N:z-ݑP*h!MAmMQ8%3wmu7rgu<2NY%>뫤AҶk/gHxA)b ZƟSn )kn?Gg.A ? {JRsi2܎-4y 2``1I *q@i7Y, fz[k5%3WXrWNݶeU-}c}mB=? *z#X$Dy COZ+)&am  rI𶓹çĐ`!Zu'[kS"A+e"@Bqޙ\&S0-|uPBߺh !>>UC?,d5kѻj_HKlḓNW$ \U=l;z";W^HQ^y{Pg̅.O'zO{g LPx6ihd4~颤3gZ4M?Hʵ3KR.# 3^1<2il˫Yni~ht"H[x5߽\. W![;q~D|&Br>69Üa*gꉚ$g/eUڪUT1Q4R66}_RY{lb{mr8Z,r8gOZ7 l-&r%lzGѱ5\@n!Jj)u p@cGH {tW2FykRW!q={5}N:q̡6OZ_/])27"Ywc(ˡUϏ+ mAݓQxAx]$Dśb8s$SVzx,DDX!"qb2uH@"-)B#^pn[Ѣ|D%\(5T}Tu{s;ħg$ee907GP:e7Y!mmD_:-p6RFE)鉩ɺ²-HDyW8dm-#ͩIq1Igbg 721#[5fy^uzM+m llN?s5$19-))INF*w\J^od71H"8v!iMibzy&J+DP*~ :(y7`F&ڀhWw *"oa}ox=i6ۯ ݇]jTZUw#zJ&y{Yںt|R^y0`rY!˵ ;i~tHN2x`7J[薑L/r[ecڐJ#^e%<t GyIvy5rRK^՛W"Hi 6}4s]Df|W:˦T;8K[ EwE:X|S.)uaOr*LDda5 9N LfLe[s5Ȕ7֬ų7O?/`< :>_vGs.TW5a|!伲N1i(^jIY\7r=H`*VEm,m4Y tY@\c ZZMr71~σ#4 Yڻehd|u]cEu]eM}YumFv2mc%h$4PQ15z[raXпs5hü63YyG_|CZA 6SAf߱k69392E4Cv+vx `|K5r9lXF%d~5!H5>ܼDmKwŝ#fKom$;уxgO$.ʘ\v[]QÇѨl&^2h mkU<,M Dtp>\["j}J4|AQ&y{uEz!pKC@%ԾL. l7!x=ՇgrǷuEȹyGd֓ y"|XE3,5mV.E&I=x7m8Vs1}~\h% K\ ?U]jVի&v61??o{93ؕo`g4DQ6F^XU '~ hgcл3s 3C r9S|]翿=SJQetxU){.N't_fWvKNsxu+t-]f13 [3)KOSN~Լp/Kxq8gSK:hpu*#%1޺aA/5G;xFN5E=wt-Hge`+.ՔvL'7ߐ@D7XO }Y3J*.l/Ĕ6</ w-4&;HFbx}q}<}ók[[jo)nk,}Yum~ٔ˼2=SIe8VP\f깾ksОF$y=غxD*aT֪olA*jVWW fʩǢWg@r904RST^]dlŦ\>V֪岢^JYyqTt\.b>X.+itOP.~X:~`3ʦ\^eSފZF^/fgD;\>VҒ0T2~?:ݍ: pr)r 3\pc6o/)W7ۼ[I9+~HrFNHTh-Շ=HQuxR$'bzzFBAsWp4l!GZTtWQ76f]N <+nr ӡkfdnB]YQUG,%OGJf/?(i8b. %OezHIEJE';X @.~RR3s~K% C+br 𓲺jn&?2)DETFUX-\L)jzf;?oT̐ǧd * ?M-FZƿq4(ֈ%yKN CKyM__T1V %u\l%mOq]SYu}  K܂C+'Ie*XfIO3@JM#Ac$b % n 6'MbFKO\Eu]bJV@h狐!σ}Czzg@.1@.1@.cWyU- ?]yEeM M@ ȏ̮e<@ #nv K`v K`x'׫jJ_zy-,pSq~52:F,r 6׻z5 -/ޓ8c8ӷ'~}G>?x{N~ufBr&2Mr FL_rωΰ~wo*O w=weGKSRQu7`ڦ\ [;=][[#V\[^$އՅa֏]FXd~jV׻TUח HD2(3mrY_YZ;xFjn;e}3k˫9Bٳ~IS+鉩Yz7016]}&xJ}Зgca3=rF?~5|}ᛣjks약'4-rl'&$j;L zGJo^S@hNRv=t=}nQt\&?.^QT{ZuDX[%je<{ kYf! D_)Lֻ9^\w7Rg.ÉƸӭjy%==?kkwSRQeh S_~}Ao˞x GLʰz}U?hOufҋ6&تiQq?Ⱦr u I *DWc, E ۥN!)- - QI1)5)!)f ͩRR*2Jv>[hi$\Hn\9VzU8 9Rq¢|3 o v5I&Zô^dV'x\6 /󴒹'hҌ>dTJWz>˯l]K{hwp^hW_<6ۄ-9T)p}]HVޮ`m*/4x UHIgmqƅD{|`N^\Ե VX ^ jGYQ㑐~ډل#m|U z)kѬ* TId$g^ڇF:H *hDoזfœ#ÄDx}Whϟ?ִZT}"=Tq%Mu4.‡jK<.D{m/t~6P)}AD]εmhW%9Q^99:4,-1$%[E?5%@>nc%X׌}o!efTwyKOH܈ƛ'~s٘ V!z|n͘c?t fGp*2\[3KKS=ٕhȫ64R:$P<oڗGTAXaJE3_%-ui7-TSvujdSwURf5 [#Yq?zZ9HJu;v7np3[vK$u83,n9̓9٤'ҜL8c&ѤZc_6n웡DqM-/v65aSג)XWy9nrW#|zAƶqUNGZV E>+qq`l3YFjHm;JU.UW=6ϭ[s,v #s :uagpNesks,dnS."js׹ѹj˃;Z;(x[Ϛ&,9ޗM+E‡TcZWW)|2}n\`&}lb>IdJe嵻ۮ>o?Cb!Džqa؎U4 S+'BlZdmFAL_򣻦؎ԑ&f68F[mg@?XnQΡor෍cpg ا7^.{3\|3n29-l t%Zz f9*[ho2WD:2R>/눵<˧1H% fzV,VyWwMDej%3"\LRrt":+&x<6 Vriȏ"J˽8-U|yui~U%>V4aJkCNǮzӕ=ZI5>>bxv*.4V)<חU bF J-D_IHz|7 R{3U_ڴسZP!c {7:~}>n^>EI1t+%s&{qX/Q| 8}Z(FKv4zy2%_C_M1LFwix߈A)pbj(j6_T." a&O(3IX3hI l& Zt(}7b꺱F/;NvUxyG[ꘫtO lwq.}saqNz<' rF"5>'혹@r)C3J|"PB"r=V>1:6Tqm.E.dv/븡/}hf.Ա^/%WZ+/!Go3 ;;O=hVrhf&-o]J!ëdiɮ *Rs^]lbLo:zFq:[Viy)q6Jtu1Sݸc@N> 9ve"s12 q +x->JiT[ lՒ8UҶyMmklw^哳lFͤ k x: 6r}'MWLN&\,ɵ8)~Hz>u">bWr{,Mj"*;0]\\h5==J3:ϔLCpkwm(D!`k\VGh?Zؔ˥$EDG25ilUi݉q/L}-tyݤnkK>Eg)Ի髃~SĐ~2ڛI)egX> {O}̷zz_ \&0`Vn"Y=Wp@r)蚁/ׄ 5rҗs#qK YԑU. |\Fef^U64*'͕xEj޾DYF<2IG+bX=WW %z.>ɥXL[h=9zw˒a{()xL}-ri͡M_yo$aȥRNL.Oj%;)\ ítύ$_4Z=p_6gֵqsT;)QGfEB(+1Drmو[Arƣт_Hm`6ڶhu5nڤo"m:MꍍXd,^E 8o{zS6r1^VP6\qoX;3D5-/<5 Ugqg>"!R+&;,Eu|#-/Yw=+fDgJgXcduOKW䒶j{oKH0^<}ݰ7w31.#L6&DI޼'q]D}2Tdib{cr9fUD"; aGxc#v1J"кԖoC'sT|p|! FW%^^IMY22ݕuntdJ%7eS/BԥfYJZfJ Eq[_ݲ<X["M󚉺Pٹo5ູ^9>M7l7D,&:fZ&U* h^uGIybL2xA]~3YQ%>!WtvYiJ'ɕR[Q:"JvKunr3(E`;a ph"iV'X~r髲/ʰ Rfr/#}SΙbyytLeyia5xQrw| 9qɑ6J9xb;4rẀF8VR], O|LVU)B욈H7y.r ZW?/xJjCa=~'Cđ\C\ !fA3Qr}/مEn}3Ǚ+oO#f(gQF4d_"CgyrxxW"pT1ќVQC +ڼtCB)%ܯ%0&}ê]=XbrY{F,c{݀_sޓɯр]Whcrw:_=&xeE"\fiE??3Ze+hV'n۶w֗_\74(Ar9]!WYw:er{wW\.{-8Šħ{9.q =sFGi-vYev~k>wJ1-A|;&(_x8fãcXАm߷'Z@.^{~\2uF 2!uw/0H9`?GRm_ o1=~s]ւ]Ϯp G5A%3EzC ֶm!XIl \Va7)r7CQ_^0ۅ2r2q>wu?pn++?> r >!o>+_8,)\}Nq1n].g;Ҥrj.[⟸lڱ'䑼x0s>s"ϱ',?璐IJ/v)o~=i? \w?5(ƚnWє>rqoAA?K^BeK]k.v U]/?n+N^ DܫQiws{–5}=݉sƘI_z`fBglbo|z)fWD"Z/3O#{ ᗏDzϹo>Y}g~v_:㞓?Y:֠O2?|C'3Ә&џIr @`hف;[;py/5>!N9q;Cror @7x߷wx}g2K?9N @.^b_ba96ڜ%cx:r3fbs/(Xk[xwY9hnJWNT|lg֠='M> !15k)每0AZ;#(Br zZ;:=~?ʾ,_<B7r FF'^=δo6ud;Ōri~1bK4Ittrg|ݹ/_7G9kQƯ2|umuLJ*V@.ޕd秲j}W6 /^Xx;o1@.1@.1vWؕ_T @ *.L@ @ ?2*Y`v K`v K~ͬ7,',/*-JC% N˱kMUz|g@.Ѵi5iޅGXD +z@sj`b}iXo_kK,DXį?HܘDj~u\iXo_+fo%*91"IݻyI;tYAII$H"" ("ATsN>s:ݩ7Nw ]n췽⍐ވp;md-_Y[1%"ܼr? y7"p%fF).QcJ3t>|tCȷ #;S{k~ {25{δ}^sB6.[VܿE\Snq$ZFr7\Yx=K{6AjHóR#wG.WVg))Vu6G#qN:|e $xQb7ڰd.#)W(*:>j਻< &[c{*\1F6E743S`VaGnWǸ~,0<"I7Eqo5pDo{ިlƳlll}? _2Qhij{|gmM <pl"#|Ye fwZ&so6=l\ityݥ'B ,<Ϧu@Jbnb9Sk?`.4aKAvم )D!κfFOr[8[2sZސ|CsΑ4e2;KbOxZ,#r~QYi9Sscyzz| F*3[h[ٝ_O6gznxNlg-? w:N/zmi_2א`-XT_kZܶG>s.^r`csC*#4_^,/sCt2HSv{Lue6@]x~㉩p^0xmՎDZ}sJ*TdG<fla~=h)ul_/(Ug':_~H}>tGZ#D^L}#ӧM@0>;qwhSăLv KOi8t1912nh̾dcv\ŝ+(#JMӀ[eS[D8;{].(^n8dkhi}fga6 XxsCuf>fR &5.<8}E $9t }ԩx\54q9tl;`%\t.a}K^ We;g32 T0<3na+}qA' oԳZOouYG흯*(jo<_wXG]bckc'K_@}?>)/+:Go]JXb9p5˨f[Eafiuv.Tg{Z؄~ Qiw=:6ts^OžaGk=paVf6œ'弖;G\||8Cu+)ƪ':2ukoMK:Ew4ab*$1^ 6,? :~opU礢 m}}nS6ln6YMzH?zxҶtw#YARpn5joi&ߪ:FfC9< {kz1Vgi[%'-ZAYL0 ?-SOU6}skYC+-b9nk{>n'CJQ& ٹ#ui`@#6K#wjOrJ^q ^=1.q낱qn9y0:O[담y6d ? q\}nuwnpf)Fu V7u󁈳wVf>7gCZWZ"XI>آu0Hd 7oi F/Tv;Kp7<;֕[UR/܈Qdzv^зCF1 L uC2Lq+L`ʓ6$9y~31 8(D]l^;hm:kLn|&)8Qm#Yv)]Lmz;{&ZRnqI(wtݞ#=gDV, "+lw!.ٔcե4'-ԃ_Ac<7Lr7Pj;%r/}αՍ IdM%7u,(7ڮg~i;o#Yᘐzbb6O +nٸ<)3Ln3F4l0K"hL?@=I{M8Fa#l/ZMZWrlGN?,og otjVHr uŭ~޶VmĿs9PpN}5[ 阨{`S"i.qdC@p#)>j蛤34̯[F dlT4O|u0^VJ?m_wLP6`yQLaQO+t[47A9h&wE^[t󦺲2"/RCvIc<۲Q9Z9z.~{L)FpXj%mBG녶g~[})(w4 yv[kRIcSYwxUUvtw#Ȭyŝ:-٤!iڱշޒ³mp5zeVyekLrmgVcj~VcIb+WਗC\iI[sjM;bIAYl/iMji^Ǭ{@=_ISV붴߆i@ڡzq&8[1Y4\;y$KXzZ*[l6s,uܔ$G]l=nc{r!FuC%T#*ny'zO73ŕ}ni[$7щԖmtkt89|K(p=3@TQYmvV}խ. 9toN<ݢz@Ľq57:a%tX] <_Ɣ<;+ꫵUh[GЕqMʻwY $-5\R(s.3c8-ק-ge-!à Ѐ oТݺ6t*.pvV84^ٛ.0\Eoc `;-nvife>tEv%Q!?ܦ+(e2NkdM[@SeS˽K{@ҧ%8쀙C@UN/#ۭV< mN-Mz܎)Fەl[cϜn O)~tuSpok ӛermOcPk s5jPy}CzK PJN\JQ^IFG#Atm5YGƚ5D(t7љ'tʆӼl.-r6r®l m8kZ [503FSoʋ4y FvkزKpA ExOi/ng]6d}%e4vkh%}St8[f|gu =qIUNMcO&μm?প$g!ۖOTk<#u!9f_R̎ AvkG0ױP6IfKal8xEE8]~t^mn٩)Y퓦oň_"rFV_1=I#p)F5BgM: yۿ_66^ IS9ry^xjlknV][$ ߅S݁ShsN98mg\S)s*s$"VR争+t>9wعO}gl: q`.=/Ndsڮ$G aq<-r>B~GnK2; NQ֤9p#y}(,ˠ6t͘U}XF>/u2}ޜ]pv{H^5ۊ^iG\}\Ri]y-b&=`yJ[Q 3e;\._3rߩs+#DZg+g?8xv/0WBMӻTZ"2$vx yyxȯ5QćV\`zj7ER]uM/8k pP/Usyq\l3}ɆC Et4 'ؘ$t|J\-ZM*0Z 0 YG+r 0Ui#.|6N\iS-5{?TsW.֋  l }VH;K8VSR43VepS(%m\ˍv7>QxC)Oml\ōg{`AfhW}qTC8r:‹{V.;*p=8:D+tд_"ZsLb~A9M9>~糵>N&zP՞,Sq_}PXI 7" }N1) ^YG[Ǒ}fF9UgwFҍPn55%apnz4az[,}]j1}iOb6;=7*OF.\X " ?Q-h.Ǣ]ee'no"G;|eMWYT ga^Iʧz+Cf?ŋN7|ar=4+=7` i{ICoqNnoSWS t$D |iqEf~SXr~E?(򶟭E8pLvݸ`N@HqEsË8bCbX`^iE[zVDUXδ+ a+A o8Q3Cmn L?8s7BR?Nwڻ UD"L7ڸ5j`3|̟CՆFf H\ŭCMx,fk"/9 {=tK`a c~d+ ,r{Cs<+Hc~(vjU}Kr E%Y_꤆w,YxZ0U-^麲ÆlYem҉kke=Zr|p`w4*OaI%7Ff{De+7jYoB:K<~5]ӐApb'#+s.?c“OL/Rt9C߄E^f~K,ٸk54߹}ZM{O4gKUn?V\}:k:C`#_w#P𼲃EةKj/lq5V'7dwEAȪZ7ܩJO8bNl|aډ=Cri~/d͖4tWj,`ZA[Yk[ 7;1jZE*$/yq2n;,<(r ȡX) ]xLϘ*a^EDu| =KhWvPq7q3*RLto})|Z܉UvEkᴩ9dBm]hס~dgv˴SޱXd9>WH[Է4@P̣2~/04u,Lݚ$-ZK7zf>حm.#^vKټvW&r^sдP6vR =5=pmykdBt.tϙc&۶-ִ-rUE&GrU*Kjm>*Huv͆n6\rmh&Jk,Rӳ; X{CQ÷9(gjN3pޡx ʓd(mZF\^p 3%e9}rsThtk8tgm)aZ[-Qr ^cN;m[{+}@z!y5ib>^ U5]λ6XK_3{6 Nu7xŪmK9Qne4łT 3=\*%].5їر#`0 3wT%ۖo_(CY}5/Y!.9tnz^>m_(c[-T)UM[>t-qC7JM~G_@>=]{b&I %N:WUu3^5{ܾpU1ESOnOO,Z J>s1T?P+wy?SE l0õ`YH-TgwJzjWe`r3f* Y3gꄇvz1EE:n5J!X4ʦZkyln8/SN.pl1;;Ï*G}meT>Yplr\bCv(m?G+lpx<|0j]d9zp˙ZeOdWِkM+bw/".G{eN"+'29g@:!]:IHΒmE%|qR%OvY0L>! [Hn!Gܲ"pR8'""ZKwѻA/H-|'-@x;a .+ y6\R<8\J{H ΛሐKLHB3Z8V~{YmL);Ku3}V^7*^3 .V5t_̚,cjROF۳׳*CF\qG  "`^;ISR^r[2¥UI Bi5)pXv^V QjU>rاBm.}O@3*7QKw}Xb_ƫd#.B>_x Ki`1n{DϪ{KxQ*7\;Y_, _ẫq͸9MKoL$HڝqE.𗯖wdlpks@ p`._:Q:|PNQs &G4\{8Y14g7M:z<TԜY.P@uä.YOTP ~I@8m.&8."_w/l#Hg^@傛i [YH3p-?DU6|5%@ Iz=Q!F]dh.1%¤յeGpyͥƌ(e*?%+H$#d>v0u\?.L19tމ_=eUu4 ًX\_[; ̥h5"9'z*^T<}(© \"د?R|^)?!?ERl&f DpK8, OjF,R /ӂ<!:kS̹|\zl'ZdބQ!(Eb)O~q&ײy_s"軘oP$e}9%EB!+~p.&!@ 'w5֫ZzS;ǸL[!yWL?;6* yr 6_c۵.{#,' >l555Vj;Yrql, xu/Hw]lUkV,/\zŲ\rܩBLGF'Tbfs쫮^JC'4xc|bd LsofrmT5m*X/Am4$mr޲UWX<˯XrƮETqU.ϻu"!!!!!!;KlSRhsiZ!Ƈ0|\BtN H y\lb!qbidryX(Gar Ãú2^uY,`@$`c fE &'`8q* O33Fz^ RG{V+ `FQ'5 (e7RcHE ,9RE2 gr@pisYneat![a.=SL9sWØ#&r`=>830Yq}AՓc=]l>91%Xa2Y,({ev&Y<"fW UHGTzϸqda='Ije jUNy%Ֆ1 s FR+7EqPlP0a8V &&ں{GڞꨛdTww |lrâ!] ER!Ã-HFBBBBB{K.)`%$|0Ig9ʨp`8UN.^GWiK8;rz$a CM}܏8:cuw&W486\wqu9t򐳛K1mlLJww|Ȕry5#S3isy@ԫq\eW>7gpv r^L˻YX?%O6<^~<|ª^}!MRCN>a7;X'X5q.^WBᏁ̋ kd q$0awX< )*7`{äF]GbOG+c 8zͨcsO!Տ2KF&&΂ tp9yOZX3!^"/uc,HDu >iY Fea~E!Wky3t嘏3/ $?锯ˁȄ">ݱHHHHHHt}?s1K,"J˓,8}?q>E7[Hr㿯2s'W=*q߶`y|ޫV{> ؃ ~[l#nxu*L{Y,L6Z.d =ۖjڦ[,Xd;xEN{.c-Wւ,&h=n@Րz4\M]/ tf}椵Z9:3Q5;Cwآcc`?|`Bee2\ott(i&{|4$~vvVžB 0D_1jd7=X4Eh{h]n23ko`ng{9CrӸBiK:R-W쑒L:[pE iWE>g-x'- qP5tv:5Mg{򆿮Ahy#-T68]LٺL? mg󰞧,78+^kl0T:X}QZO`g69X\pްRǾ.+&h՝: FqV#R=cgjbu>v-i{VxĹ\xKsyOܹV/4K f [=e~ݡin ٴ|Awz3%Ed5vwrR.Etv$H1Qyy皁Lt fmOH9UqeݸhQNh,؅'5?b1uT{_|LԔٮh20xr+#HDu:miT6K(%1%klvo6T٤u.Zb&BnޗZG27^\B`T.LߥbW'()EM\Ѵ1luZffؓ!F+vXY?pX:%d(N [&0|kŹR\a.@aϥzX>uZv`^fK]قQ"gU|;D&s) Ymoը0/m| #)yU_*K2Um‹)4Z3j9#!!!!!!N\> >o~On kp2gUoNx+)[Tk RUfpkiDe]u!riki03R^1.c/~f/%b:hwݩv* L/4>p&15ϷX0dg [=EnY.aHkͦna/ĝ?s&&&>1q\F\z˓\\;ײsl>8lOqy<,qy댯bތ4o]~80\B쥢wlB֚0oզ>\?+g1 'T%$%;xsd'./b䍵WUã?H\YicrUV$7Hs.lrU\6\ܓڡ:k-:KTd2kU<7Ob{Ⱦn.gRڞt=?\ޝ9a$#H[Z5Y|ոPbϝwxKaK>Z!|eKBODsAab@Ilns- ݣ V&Xi?^֜7z/_wX=vKjfM\"KsmtCN\>'~zNVggz0^A,.\666\1k ꇼQl:"x#>w(J. Ie׿R,l6Ǫoz- ͷuʁ`cGU8vx\yD*̏ڽ1^"'N)̥Gkt?"p9q5{ IbsIQwӢMlD0SkR}n۱']H<ЬK |@8!dۮ^Øl=571gy|t4C8kFBP !cvRxc}> x 6'P [m[nY([fu^ 沛 ;[vL?Fघ$`}n) t9Lrq95y&ݲ:[&3AJYwb]j*'>8lzXDT%mLlt^s91W_C"?wPn$.qzJSsY5y_+feCI+V8^-%1Ǩ=h8.'F-ՖZ̞Z''(JMWL_q'LۻV%e ؂b{͕vg x:p<^._@˛1,xgAUZ\DlV'OTƗLIQԪ3x_eŃo4ƋxؔrrykHJc2c`iGlZ ᣻h{.䳊[os藇`^VCsr8ipئv6ej\-Ҷrvۓy suyb_dۦz2{GUlo.5~sQE\?{m+?I7יD a$=V[zڵ֪EODkKC}jqpYA:-֯VYj|7\1ٰVEe6cu>.p@;E<ڵ~/=p(RgI.`΃u`d?ÿ[0lFxckXe5TTTUl0K\ߐa>ʇy$j;,|*LY穽v՚5jJznp?#^Uq6\4Zb| ~%zt88W|r%Ztmb채=1Yl/츯 GKuwߡ0:w;LK7 O98/[q~ޡ-<^irUUյ[S8.dq5qڦV3Xe6Y]jGf^_(=N3"]f>.NrwZXdc|y k!vNd@->=mS;"'o|ݏw`#Xŧ B./}~EU593 []%[ &g 09 (,`j.l\|}'f}LH Ǹn_)'$ N'&q*\E 6_Jf%+w7>X?SAd b#\.GěKB(|Y*2-?~%8)Gcڝb\#V0XRwׁ# |́$ ߥdqY9VTKrY*,"@5X)7Jf|!< yGc3Y3=,.K2H,$'*=' $[9ϒErl!_xEIP`c! 28)׬,TF'¦RK@JP|_%0Cl\teɟE '+ojpx&L0lrP"!!!!!!}ގd D-)w=޵BLx뺭3'!}/O0F&S4[o\q۟NLB+FHHHHHHHHߢf.~yB K$$$$$$$$7&d.ޘDBBBBBBBBzcB K$$$$$$$$7&h.9BBBBBBBBBBz GI8ҏ;UEI@ ;erL@ @hD @1D @1D @17E(aH0$$!xB wC!c" ͈ @ d. N;K#E_M𲚅X,I2٫R@ `̥XD"mH$$#@y}DD><:642:[C$A*7B _*f.\R?iV3T*8LL &\Ns1rv _ I8WpB|5% ٟX, ԣHCRD …B}0BH3_ H!ݎSKpO,)l9+6YW?@&EkͥAIFsmiIMS,Q?kR2JԚiϵJ$"`pO٫:e.Bٜ^/$Y/?*1)/nሁ1e=vJ:B<+#q?ɖΊ%& vqvsr}C- !c` ȍm%cR`^Zmlqc^UJcqH*>k+ fb{icW4qZE. kBP[=r\eӦԉaLFӉ1\]M5[TZQUP2:xQl@ @\q,lŚuܲ:\"SI|7Lun~:"?LW(/2`6pkK[thH/.A}^cSo01,I+Ct2foK"2\"QBL.9%?˭. @JGwZ_|R܃>K+J FJ_?f\J򃏀 H.sᄔb<faҙ@/`t+@@HA _W=$̴ fNgǃbT<0ZJapT;^U7)VL$$-鐩N!Dȼq5w7wI Bfvz%6%4bNIFK68_VvɽrpI}[R11!7 f*0y?xlOHҴ"dx2>%ɨޣu{@^r:b1RIJ̈́ #%HѲԃWEڦ68٧A툤*c,9Tv^I"Ι*xncB"j9)흙<Y2zaP[t" Wu&. W7K 7.4T>ULeMN0B1& 6T'L>NGgM$2ƙ\ JE|6cllDŽ61ƚg\:BI(JF,\&8􄀄[Kd2 eqf}ʶM&p`B`o΄܉1G g a T$ `p1NL&N(I.@ % .v|嚀4>&bP~`3&&0ahb3$9`2Yl&Eۊgrþ_lsyXe3!tcc\l/1j=]F*G!lhBru--+">G'FL7bć1ON9BI?/B9ŅBJ2*,-1o0Xq6O'{kSZȑA0%eΝaA6X=>)?MbX( FX#! *QdCF zAq a@8{.l<\/<;nV_w(K#<6; 9T311d &FL  (>< N1ѕ54J P$"]8\`vMQDWa/?;^`q<'>󼅮Ưט߬[O>zw2GxN0\Wdeej յ;Q;\qREaކ<t_z X:Rc9 ?jgosMk "3mXW=.S?Bh%/|L2"S}j/bOi&/.cќ/EۼbJz@R#{UBvwq۵6}iηdJ͘K|2.#'bmF\*-xXRڠpgd6"T-6-9P~Ij >Nu T$J՞u.5a|=HuW%G6.{<5U3ea%j_. Cx'ie&{6k,;\[Z$h9tC;㫄l8W_ͽbOSÍLHPj]Win|]{f$ jƎ $e)r3g牅BxTױڽecC|Z1It q kLm?WKlx|x(M.9Xô+Yj~Cht2d6 [|s0vXdٗRZjQN9 +&s-},!~fuaUu᥸[4\xFx/Vr|\ݮ wx4=wyR8Pz1YT_2^ɩAI1~ȁ򚽑#b9Ez%?7}˃ r1W ،P}IhsGչ&n_:>IEA5gp5?phl&r^ppɡGo7 v p9ɛBFEBioWIs IʃlXWb)17QB)ERXvBóepdq;N`ϥůM=9ǃ|`3W콎aqQc-ˍknsB!9QgwgޘXL;O>|b@a1:6ru:|"xu>H70I?bee׹ rfI"j k牕c>X\zKw 8(ɨ>rnrO0,KKd.{O)xbsVnMpv8@rx =Ww7mުwfs~slܥ~w΍!~N{-4w-bȆ}[?h jK)*))0j!h%4ݒmaOaag̟cz_?(9{4O=T PTuK%ťOݶ-rwxCw /Ns~?5YvcÊ+^z^'.(/ȯ{Gݚ?_;V4$\ [.Yvy_x c;s-Qr`d9v5ydHJb嘠P.izRbBw>:9&IqFkÎ;_ʦ2(Y3(` ܇$7*rދO师+sI$o#r1=։R)5W4+ sKpιiႽ@5wE7' SX")syث5K)3t}R$ێ<((cE`)gLHgR NC "-` \b![ K$ʫavp؟ ͥaI{3%%a⁹wp;$ bJ\_Χ$mF32$,m.?*`$D.l]v'wqp!U+r锕B+ڧɨbnrXƠ\.HdG*c\LXO[wZrwŪmel_b8(ֲhªZ L>ս' @W*^2u;z)JݡR:bTB1N>@:Ip%d\,?+89/ !}}$wg"NG|*[YsC;u9A(s*qA@=Օ2Aã |%YB\"w5~Ye YG SnEx3hi)"'ύn^XW2̇3po6x<Tغ@ۺZ=ަ~#}&~L%+k Nh*}=[-+Ȫ-E㣹`mb=4@sJQF>[m^ck<)=k6_,↕5׷gj 05+[U}5S g: )vᗟ[Mqxc7}zk T_b"kr7K*-޴r(-Šws*T"s cfs ,pd|g3uK0F(v*! I"HWA]\eLQF! S21=P{PSx4pCG"b/tsg<򒹔ȁTa.w*"ʩP[9`~aw?%"#<ͥ84~ s 3rX ҝabHIKp\~b~#Yvr k1kWʃsx7#Va. ʝWX.is9(\bOOP6] 0dDhh./5JsIĩ ZWyJ#6FPl Tˆ\/ SGl"{+Y8~;4[Z'jF] #űpU$$clڀ*6SKn| z\Vn?ƁR9/lFvՑb0IDATQ}N,ryɨp/1c.ݟq޸m.qR.:GMs6{T |>?_gO:f]D1E޽}my_8%@>a5zqSݏ֚SXd1 Rp=e. JG`M;^eo|wzuwPe`~Bm4O.rPʫ3oSpyŻVG=}U PaUAo_`}TJd1^jF 5dX[BOlDB|v\&_ikV4 2#;G;ks`D.5]Tͭ)RUA|QggQ0( 6!ZVmk8]M oPy1@>Zbr+?JO09 .]H=4LKL"\>xHIJ !5Uĺ*+\}=Js Bm/ \$ޕBeD[%@|o0ܰom۸/W}@oW`[\>h7-^Wj֯J5ꨯx/ٰf?Kz_nI7\;sT\3=/?y?ZeCװTRgF+z#3`kTq a}Ūzӧ,׾G㕅VdΝյPh7G\?y)X=:ɜl߼tc9>T>^a25/螁#X[ޝ2OE/Wk_Dm\SM1pm&5elc.)Vtrޒ-_?_Ұ=v#9OM!K( !쯟/[*pg%͔ɫgj"b1)_|ȕWm@*JV5*ӱ)7bqp ֗Wb\|O行.9^>נTݧ I %XgYaZn] ̭gp*oP7}nt;`HF⥪B@D;[ĤrYr2~Bٟ$O *]OP*j6.W(_nؘ5LF-[+,vgP&5Oޙ+G*gPq!*:&>|a&`m媻ᠰSSiVּtnL&%VGJﱍr\9|'( @?o+9kb ΕcDZe'HjޤR.4?>|J0 W( pH86lL͡|3eVU.5b[Cf?(05Ҭâҥ*e0ޥTR&òLΗ)tf^c[iq麛 ")IF#Pe+3Bsp `WhūNTD{YPq HE͖*u=Q[3HhpDwԽt9(XHb7]Bp~b]Bn?ɤbFqJIŎ}kj'I "oj(ͥHzvrar󊥦囜Hݵ8Dq|IYG"-R2WS\2uyX< zy}X<\Gkx's%7ǯHp8&Ϭq;qn 8G1G#&`G'ώ r ^Kއ\<]7@3:RzX}wY|$30Z6#1GC\E>K;z~D0^!*&-1䃁bt0I=8Bވ1M~4!c^_Xd{^aÜM%AAA^A^'oPr^N^ ESW` EΑ%77a+KyC*+vRw;8Na\gˤTF;KHDg&Fp˄8,nyb\D(O#H.S,1BEun3';B{g"!O@ r j=fǪ) 匳TA(iBA}/GJA uH` ?.*PrPNbIIR!z"#E8LY-ĀY8H!HTbY* pԜ;I_(er A^7OH)RHjf,ŠĀUpұ1F2SMт Stb Pҙ>EfI~yk#s@ ~ sML?@# q7/_1|BX*H$;4N$ӟ&xY B/H6߻h>#+i S(~@kşN‘qDBBQM'@LhA  D =U)C _$\"@ \"@ \"@ \"@ \"@ \"@ W#@ ?w(kGBBBBBBBBBz\JR$$$$$$$$$w*hs@ @xD @1D @1D @1D @1D @1D @1D @1D @1 ̥p/SQ!X"W $ Hm $+@ oיKE-|2- L Y2Izd$! #Cm}ZʀAO]o?K-$5@ e}B}JOk^};szoW~hYI'Zrsk`‡Yy%m5׻,dr:wY=ϞX靬6ʧFxb:29gV@ /3ц;?lUȁ+KKB7nY{p~ PNm%W?}9G)| Φs|H3'{ts>cce9JTī,_2O.߿.)?*C xyǜ]'/S& us,|bV}?6f')M_ |av򩻱'_?]GS*eE=da-~w!v[x݇j-5Torť9H16<|SVnPww<u( ֓`݈o2-w?Q@  \}vG?iMRc9 -p?ѻk 5dj1,Zt pSr%~_}nCQE<ۀc.ƱѼ; ےMNQ|% UՇI݂'.],xݦxwRϜ bX ӯϴ{㢑CZiB"mY1x%4 ގX>g褟GϧX# [5b3BZ8.CѶf7E0xqM5Y,9nx~@ \01zZ-~ɖ=g(?Z}Cl?#<ݾUeQ0t렆Ν&8.{ <廟vR.Xs6䃹ZC`m:>6X_oT(/qV}9M7,M"94mRC̽VZwNVdvSV-.|z[}'ἷ kUiY`W{v&zLݸh>W ϖt&zw P(?'z~]mYsD; .>LLs6N8a 25}@ ?ך8yM!/ѿPG]~;2QǟMPD0+N@{H!\n>Gő4os|;`.cvX_{s.Xh Ov峡B)sIQ'?l};#AӅgύTU; =ο\n_oPemXbvyOVA%oH"g>r2]qokBK)g./5Lk# \ctoIX=7fsɭ߽;l$&Ϛ,0{݌w'gUu2Ϭsni3@ Ŀ>6M{ž蠘;7}"zxAfu˶6{vR Jv\>,1M/wHP]f%;w<AѢl_3XlJݺn`XZ wµ7oh񗿸/>8QQ忙g!As㤢kݯh}]f.sBR)+14_3bfS80zh hyWCϠE \hnJ^|)Eɳ/X2wٲOz'#OvlbO~>Hf8 ?wݹw~Ob-`{D>_s,08ɣKHaK~Wa#EE V7@u׍6aXu읿fvfǿx4jf< Sljh+{sW 9wn߱?Z=I'r~;{߅-)ߛ7澰ܹ_dyv)Aå=Kp˟v!WYM~N߸;iW.hRU/{xw_B \/2 J/HxYJWedʢ̔Eг<ٮV|QkIzʃY* '?̺}'+[1Q*)ȻuAZv64 *?E9̤1|*Nfڽ"z H~4:{#Urr^hʔAPyiދևw3WГ<)SQja{EɩYqBNƋe_&eu`ǚ?ȺSw{W4+fdN!b<wvv]hkeGWl29)%JrZ^ԇKw5@ a8wE:Q?5`(ۿ}LpF|@ ?)o\2#;c!οfa`a@ Λ0?+R1!u@ ˿D @%@ ⍁%@ ⍁%@ ⍁%@ ⍁%@ ⍁%@ ⍁%@ NeMr@ q󴨴 GrzX\.!!!!!!!!!!HSVY+D@ ďK@ K@ K@ K@ KBAk @ d s@ D"R(c"_KB]&@ @C E g ג"/_bU?#-[C,IV$RJ.U.3!(O$%B 8̈H9 K>k@U^g.%Q\\rG|X&%(ijZoدH27G,3GCX$+ i5o%RdnkE"{3G~,cC ϛޚ<8!ҙ#ɟ\b ^ -j+)ǵTC}im7=sPH@ @\J(xn꥓%_"RQP1 <2"yϋj:p?Mwcډ'xuE'HX.!j$%ց(KS6 }sR"»F+R,2u {e;pҦ+8F{=BX2ђz#ągϝxLܩ_XݓMqIDXsm Z5ʠ7xĨb+z Ra.qgVq\xD\/~ Sn,&EbPHA_[Wek%FbU]8oĠA fm6e.% Y?<3qg ?(^ZϽK̥XFɻCj=bJ6ӼHnua]8TwW}gbI1qĦ0=m䲩bU3&fV"`X/W;2 >1WFanZQ!I%3TI0*2pkT.?Ay,M[o$Tp;,9V]W ί2Rp(-0r2|wtRP^7U 01ݻ ݙ=D8:H6v{ Eu[zrSe;a:Σ̴119N qܒѧx-q 0@Y63;}&Ĕ'ج AA%P>~%r \|t\NVP0r+Vi?\*;z~&6h pݝw4x&t0&Hɰy=\vf,=<,@NL g#)$u.ny76T˧(3'<(,<晒 &<} ‹~H < OOO*CW6_M#46/}ba$wz~'GQTmzh}$] 9W2 l-j䉠-i TE_MϓCϬRA73 7׏TwqBtzP$l b#&D\>W8$iJbN'L.CGWg AcJO%I'_?yqwqF؉/ %1og^LJIr±۝\.~~l(zYuhԙ>8%LoGB^vq't*8yy78h%kk$VlyRR(]S&󎞈] οi' {;0%B32 Z칄kgiiգ`Y,SCN<9xVBJ)N%u2g2 c  DןJ*}W%&D kW2X@#mi*fJIF-Ms|.qN/g<}и'd:w@2ీ1J,(Jwi\t,}͕#2r˾b.jOL_%n6;>AƩکTw.N&JkxߗpޖLВnRTk%ZvݠV-;Ac#,kW?Y|U#dɆ/W';Div awG@åhH@E|hGUE6I픸#V}H{O{K5ij2 6;:Rɼ~آ"k[Ն&;,^?v ΡKj ?_L8mu'K4\OtLD:=o PeA>yg\5y2$у{n5pvEc҉;QҍfB;ľegQ\|88C:s*-'&|V-гItb>ϳk3 `OjNIN9۹O6Z>EM) :/4r|?kt?i.Ɇ~ieᙄIR90蚷wH^+!șG9;ޮvl.`]vǂlPX6Sy{D܁\Rf^3^Ģ&NYd J6cmt%;뚙FT')ܣW=y$\_5 = 5lg(yY¡m6)فW |$MWms>z7`is S"f3{Ne,Vh}zza0#`Ko&"w  Nس1H0(=Ayͨ?i\e.w1;WXW%Odxi{/ =6vBRĸyP62LLM=TWRN))8@Pyr&GLj SG3b+u+O ˊER~c~S_SZrme>Kxɇ*n4[GdX]\ANK(U߂bKx{|K wW]_#^JaŮp/@Dqf[c bt (b d\ oE +-&:[l56ek)d^W=(c>|S E.iS8u>U>`0f[\d% L__b]Gx`K\Jʮ\e)|a/ZA&|*/ww.XەCxaڢyˏkߝ EBgT4oU|1%;{ubꆥV灍Q5ʀc3o PeG [mN QT-zoL%ܗKpgG)ri3; 9.5gե"w؛¿{ O>b*8c{Ox Sl>vE=*%-ykR`fkH9==-麟cm>6@^KU/҂]Gݦ{.%(*V0U{.BWJQLEj_O D3~a \A5 \JQ s$-#ZC.lP/yk42  MN+\ݶ$xgt8({ji`]9Α)A NYj\8^Qs_W% #?IH1^Tc=ȐpVGMK>q0\SÖbѣĻ-!Bpt&-@7[&yLDxv)Q1o%xu @. e74%:sif}}<M7X0\"[仛KV~q*rP2Tle`筠+]'pWVe|L#ajjo.JQ&zZ 4TA Vo \M'x{s5NCyCWi>}3?}r ?eO vܲ=ukGW? H6)jG_mvज6頋W4ߧT,(KNq}\7w-c7jh둇`9'x`CZ oƥV+eÅPpv{-!?rQ0*ǝvyhKT˼nsqǞt5 :C1r-H7.@0t؄e*02޹` Pc1;B A ìm%^cjU\:k8׀JD\&s6HaOqRGx"9F謦yg,LD6vA[is 'Dr[u:G(Sc`W{E >݆n]לŀd9 J9$T (Hq`` CSD7ܺ%5Uw~U݃ˢ77qlfB`G3 K zDD7/M髥J6(_0r153hI, Р~fE`T I_ʦ#~ rY k%ms;7K 3e[z=;抲p`zAi cEiR_Xۺ\vP;d/5r)fo6njW?(~4ԠHpC-^Zd@G \)v"x1sE.yd|ܒB3>I pBhjEa-azgE@JŝML|IW +c=n1Had{k,'0S>'G΂xpjrc*:KP.!GGwL/lU,1G.rf{s)&,MV2Ǐvil|~JEmm%!7HQwNJC'kb2ݵXpMOvk{ nR ȥY6(!=wNe\R0^~Ӵ nxxc!]q~{~S˥)гu)G-c.;r!~aiabrj2;CCUǫ4+C/ "{<^M.$!|6a31uo_@~&b\$4L/Z[Qq:wW1)ɷM3c o[stX]*P }t\.{L:8`vp`v8]ri:!VVBC@ ߦ#hšqa兠i*urrj2;ʯl\.mS\:։#qKj$)ebjzzv ) ܠƐχC_k]oKdOˇm~rb,n1ާ[AMYH焘_)JNHe/cgyfF{U[ȥ7xjOhT_xoXWt ¨ L}M˞[5:r"TK Cb :4^l#PQ6st$![(C[ͱȋ]i2@$0gNmn׶k4k^IQƑgz\B ȿ+Ag?ثΝ1&!M_t];B(=cÛmkf=%Oc߼޴q4$αW,c@.DbHuST ArgT;;/fJ?z8A#bf]S}#Q=ULW,# ŝnQ(m*(',NU%rΧ+ jr4{'x/waY.KPZzMX >xޛ=I$p62{m_NiD#jEsjb&='L.$brIH6t|xA˥Ε)ZbZ0yy,X@WNs m;Z@C͖i7~mBz2ςRq;OWwF۳c4ʆp oeF??}cÂe풉otb[rN4B'.C(6_تو҇rO{[ pE\)2ʔJDlH>NKhz-BZV .1i>+ա\%$w{xYbyd~ J.IcL&46G*9< 5ARo A@ =G.edcG~޺y7~np {|󗵟01]O׿񆽖l 5>>զ p59gBLm?y/sǁ*V'W߽<|Z?mBB ǿ~߮S .&w 7o:H[/#?y7i7+}ɯWpΨa7)7?ze5E,Yl7R@.3{?nWMZo< ?}sͼ%Db_\Ju^c?28>Ǎq |thfttX\&gvRY#m{KlϷme^ꝳH,QBuM -]I=秹S"QW|$2ݙ,T#ASlbl*~7 "H:z ο8'YoΚIڕ< \Z7`35%T%㚶&>QUԜ0tZr)jZ<ɵ16vӻ恅7m?NG]&N(zG8]P EgAr֧([(>alb㪭o W 1\xm݂`D[x|=!/jZyfװ/;oh::^>n;@G#¶5K[uUP-FaqZ:&dD3{ۥlOZVΗ:p'}@[s0ܨBt $jha"tX₧KL,Pg4ݻ_rV?W,f̭L[ KbPڪn>Ю_'YXoIviP!x˸3=Z:g]==\tU#2'\zꦤ@ӿ%T.,P\B ?K'">sqqn@]\RtOȘ#y@"`Lx@%D,[ 2GƇFg\ y`? 18Ba%{yYt 4`ݡûb +^#N NN, X$s3t@O[^bl<֏4*K$R.}TGƢ/LIf<V|(Kj8EYWs&wD&f/,amRwa|gp`|s?86hL@^%pЏbB)h 4@D$ã|psin"y|^ 3 Pwh` ,T&&G{ɣpW hsq=.  E* ER0;@=ΖȥTbh:5Drذ5ysC&h E qPm'H%|(08NyEłřށ)pKWIe"et|n)NOM/rAgʏD%/p8j'qo,!/p !d{d(AvP3.l71(]--ri B9E [GPw xƕeD.IL* Jr @ ,[.&E_,_=ilpEw4{nEynCw#_C].#ZU|u;wUpwVeVWXf3hv賚CT 4zj("geWVъ-wr&(꺜o+]  [>(v(Oe^6g=Y}#y -Z ܡYZ ڬow)Gw"HV?F;_|ZB ?(){-dS(񾾾ṹ9R/ @ Ӏr#\.pHWWW}}}cccoo۷oNNN @ r#h4p___uuuBBƍ-[ݻw֭ 5|> aB i@|͙!d2ӧ666wo\ܹ0@bAl6K@ ?(-PWWWDD0um߾ĉ>t萂ѣGt*))eggHN& @ D,/-- ܹ YUEEE@4{x!іi@ / C&DPL#O>}}m߾ݻ7olbb677_3?hw_UGC}řFNN/d^V#O"\@ ȿP._CqP(@"bfee8p/^*((;wbcc-,,qܲe˶mۀ\ @ \\"0pDv277ljjyIQQQ@@ܸq'| rff[L}9v)EpL$CDmvځqE"D«ͺS7&ŅA?GI쌀6"!2J^:ov*J@K֬&Sl nZ^f%Ew:Ce2qmaD Cz ˒;CRunj^ t<;>ek"_GV@ ? t:w```ddxc֬Ys餤{q$= 3$$NYY PU|@N}@؍-1^%b4'p:֋zP(k H*"yz,= ˀ ")It('4[+b §{\x v 31z67hPeIfER``AKEr8XкZPr) wo SiԶ|ǁ-ܽ;2Heӧ  hSj4 \=ozX(}?.aGC FP54A =@|ք ˶Ǐ[VVV999iiiYYY<NԳ0//l}Y]yl(`EYƗ,= WanW@ý8?M USF˂uΪEE/$_-${ADu/w zZ7)\ty~6Tda[\a{SRPwpR$@ ‹gF;\8eSƖ ¥[֖-#i|T %O>gTOz#.G6Md2wZ>C#}Jh֩iX3APwP ] tBko'x|+HWY M8;^AIהy9U3L@lGdb `{V*빭vC'+$+BiFu'B_.W,g.8#MW[\{X1`/%A v#ryREu'>&"h޽'K\zmv|pZrP;2\ha '/xV̳fz< N;\o^B 䇁r!```Xݻwondddggohhzyyѹ|2f ܹsʜ$v2KC *puLPҏ;D0q$((F>$pDα:fV#&uhyLerM#lkK>#224yӒ |4b $R.Y :s*ێ{Z /#_ճp*lݗ'A[A,SAG X`NȸON;Q3ߺ{;fcO? J`]GmKW3|>| @ ȷr!HVO>mnnryׯ?~ rd%mmm] !JSVЃ'5&Ddw{"0l=H"1-.; MtKt.85s8Ks l/ Y3Ycyz'5` OSt lR'NZr@`auH-^&4 ;|5~DU9v%½`+<g.TЄ5; tۓka6"PZ,2Dڙ툦G8D[Y $q&Nq$"+Z\@[K]4N;,H=5hE\>D\rarh6Ft{nדYDLeGUb izr [gMúcl`Q.2Tt vX}~V;&ܱZU}!ŸP\:|j "Ke_tXqSSl=#@ u(KU$ (fff{JJJ FjjYXXֶ %pv K oȊ.s?~-#C$b>{aڪ*6ѶHE &BRu%BdbtP$jѸkVmhdxYWWGK]0mX*Ne;(Ըېew5ksPI]ɡ \$MUOhM"ܒLP' i=BQ;U4 dNҍU%t[FIp|#f 4Y>_4 gb.Xh^>npEw4o+WQT8s1qkP fpPdǰ:RQ݄{ s27n;jS7(LDk~420N)PC<-Fd21erद<(ؒq<)kM 31ևNi>g۸T]T]U}#D7}󗎝9z~U"dJB/j:{)C@|sss3=Nooo__ߤ$ ^^^ o>,YIp8]c~e}5D ;Tٖ]K/D-`E1^,]NpdQWJTWWoSxC4sGM-X;sH^Ȕ=+<7c@&DXM>?b㱖/ᔃ?\Vi:iN7!a\xL&HW\vڥKLLL\\\^ DSCCmee%(k%f Vs~(m\ύ3#mdddVVkRRHloof @ 'u"˝"!!!KKKKHHȸ{nffftttjj[8C@ L訮$2??͛999)))fffT*U*@ $BccӧO+++ SQ1ax8@ 4rl6kxI@ 8DB@ g@ ʀr @ ye@@ 2p @ MmP.!@ 7Z;zۻ.p \э@ @ 7ZڻI@ @ ʀr @ ye@@ 2\B L*%^L"@ X\B B*\-` _a l!#m@ ?({A7KL] Ka@@@ ߋT,A߲E`KByK0d2 ιkP@!UZʥluB]:7k o@gq^e,:ȖY|g!lKk>֋! O_ɨB rs'&?|ы?q"?y0b;=hZ'g@ 0?h._ $" o-_3_~bLJu[m0:_-~{ݍOO) -PNÝMyTz# ]wQLy^HI79u,_2li#TǦ%H̿߄uGZ]m8Ӥ$Mڏg @ʫ.%=!o,-҈lAwjGxP[:gY$Sa! d59A&)-diFdj-䊸 SL~"hyD/ ".Yylh =TƫK eryۏ6ss/N^2y%|\W%Zwv[[IYm*/>"Oґ94˷Qu/[VO$lX®Oпa7Z۟|hz#َAXxd/&4}xYyEUgpֽ]?Xj}~(_tO7\"֧3ύvH$I6,2;N]|3Iu1[Ͱ_@&hW Z&ڦ~\Y QC9y 6n<,h |gZ7- ;?ҚCr}D͒;=6[2]>z|܇3<1+Uw*B*d%! {3rb}b137?;/SӃC#'\yɐg><=CWYճ"+\J{fr|@bV,$8lV;e|5}ꇖ𜉆;qV"3wcˀ0n$A7$"0ךWHf>y]3ӞL`"ӚJ:_:uԗ;֝5 HYQApwsHzjyɭyCqj {2 O߻y mI^\RasdNv/rR˛i&yga[D0[Q}6ǘy~ikR{K"~<vohU ;Ֆ<9rT=q\!LJpWrn$gT` <019'B#i-+AԤ' ͭ5=Ͼ!_w6 ,G0`knݼ|!oKA̻t\\8rEO S7R ܜsVTqO^s]7s^F4Ve'yQKE N}qZLN٤<, j̿GpG?=A}qzfe;&Ksk+ C@)ψ)`EAp/LO~е\T$^K6W,VE ȥe<XL;70w}<8{šKcbК-QSO(Iq- T Ryb1OD:=2-K6O"%99=T*^+F|\r69TвSaeCWiߐ)@ %j;|>7쓪O@ybrl_>Ӊ+}7xӧ[n_v)00SSՆo~ܯsoϙKj_͟>l|M'm77;kӖSmu?[n)PGxN#ZsK6;:>YNoCk[s䛷o^w&?޸8 yzˎBBO11- ޼m[OBǼ~'޲n Ih?.%Z1udÛn]"2a?z/}/_kMcF;"^O W8fED\H&ɋ:u\-M*0U6ỈS?v!2y#t, o$ACu\rHe:^9YNT\ W_'9?|L?f'>}SM(k,J 2Pwʣlݮ=&^*G2\TG1ziQ p*qO!sյLc$WǑ7"ʰ y饢y#]y)N犖풐z1azPE_̭gN:E;yd(;D;Gf[nQ'zo5rJSWMo>L?''c:Njy 0D|X۶ՒG]2Vd?qx0nlOׯМ(G `][ڤW-d߿]j!, :^i;1Oo٦K!юoU `~ /k67mʚMgWb@(܌@~IЖt<ȚkҪˍ7[w<(&eSmoQZ[ơƉ;hS2>r&-/ dt/utҽUŎGC vx= ]WOoCo_.H|@PK!>03+XYg\E#HRMdp~.XAsK9܌KgTEʰؖ؃2?Ѐ &7 '37Lȥhܥ\=  5=YNF'\3Bu*Q Ej K4u cLMV9n^t;5i(&uK\ vfugmzy}~E_w}Xhˊ3~糘;i~ q w<w ߾s$'g`~J_lFo=*ldXk͖6:ߚ&+C|/$#Tk #Љ>\w#+5n ?o8-LC'8Xsm^nCMՏx]+Nz+QS8M4qUx+#E2*\"^D nʫE 3 " $b o6g"RPF잓qmH6kA*P/PZz\ndKǝO~.ePf;Y24[ᗻ/xyw#{?Ffһ<_Uŷ2Zuf}eGc ߃9BmvEꩺwZ+ '`r9tCSa"rH&IƛcahYFNh y<2Bl +cP-Iȥp1Tdž̹\:jy_) %ҹ|^;ǧXLcj mC~v9Wi- @G Dm@.roY] .2 zn\Ul$1$#NDarvD]` Cdr0i? $eGc!*'bGHei{J^ލt?t ǔ(tQl|&06pIH,%srNܗPbU\l )Ógr)D ݊@nBoeRw_ :7vBwzs9B@}.x!9iRP!4a4KC?.a\R4Y.>X"O,Bf;t{{gN؝EM6,D> K'rz܇U ZꛫZGBb/+_+e֩^v RHMYe,ךe?o}nU4kEMthY@OSMC:P}O>1vWɯN.[,Iw,h!,> ^\rF]OӁ*ܱ蛃_mx&6:MU9`pxqv~b/nSs,mv^7ѻ5 jLJv nu_e-էٿn5{ Z~sB-kL-͌wW|K.rɯD>ݙ";=sIm}nҐs0ZjisL.&|õ|f9hjf1ǹC_$QUzVkO1iSTv'(L"+{h;s;ljuɒS/\TKl£h.+҆ym3^gk.5*KY$CsnhT.Jm?|A%Iry+v*ښ~3EMy9+) QlRFcه7g<1X|~rQZ+ED1.C,w5̠c'm^#tX>뿬?ś/t!=k>>;jhrIoR}揟o'vġT' O~xT3XhudWz{۷Ѳ_??|y# 17;L+ykr=_(_{(y6ݣ0D'viZnYλ*e/{NP{Bgʼn{O֦Oњ\\%Dbz޶~'\?o`MC% ?C[--67~uεn \ܽwdǙ{v-*a‘#Oq;_ݵoʞM;Q8("f59s%%<.BC?;]b,.e5kPͰq|-8k%:gw@6p*Γ.@7fuUܜ[{ah宨z+'2vLJ&ׂbS.@%v8JЕkg/Y뼴 Q"HUоdCDreht2F5=+'NmpBkiܖ?tߜ( PS5psɝ٢ϺF_ԴH:[srϞt=jnޡ&wu< -<t]@+ULy7\搐B_ΙkqQMEGGOV* +m5knW}"-%\2nƿYk7ݡz-. HG+lx>:tjw=Z4_{'c3pV=ÕHّ ndK"t6L Q7HmshyT["Ʃy҄ga94e-%rv%D4 ijOhH>נ59hs=rI ʘރ${ۍlP,P4HmVD-d0X|FC@ 7'b$,*&z{&2><1\&Nzq:_~r{K|<#9?A&'|qzOg-0fSK$LN͠чF&fK%O.ԑQ&94eljk3AgDdLY^X4;RX02>6$L8;>26!9wd|rtlM"݄)*:f8 >3KCB&P=2G\hi|ƏɜcnG*rHS  ksXkCG'iF$иLȜeGha=947F GMYC]:1;IΛA&2! K[<7ڇ~jg,'io#gh,Nvwt74v7~<~.yBb/Ϣ]-VTS`L,#ᎎO.sCs+W8;2_ <.1g;:Hs+RĘG/)w`E%|_2}ihksGk<ϐN8.;zG7:UjhO!R.-RE3C=<)ocr&5l"c`DǪKTE.gI =MyBEbT4N8! tiiA㯼Eh階^,[ \!馔FcsEPbvuVL<`uN״O73'=%W5Sj; #KH|HDħB@ +'ȿ'Qo '; 3ҿ'~i/<}OcIrHJr / r"._n3k6Xio7T^@ ?(䇐H_Yg@ %H%RP$,h=EߎYB K@ + 2L$--- TTT x< nl@ \.B l2--ϟ711)@ OJ%HEEE>>>jjjk׮]nݎ;9ݻW__bxxxddD$XC @|Jccc@+SvyA`AmWWHlii_v@ y9\LLL _tww߻w 8 ٳhS=~P@ rڲ̒D"eee;vl׮]JJJFFFVVV'Oܷo˃]IKK~w@ rzpDӧO/^x!eee{{䔔0ssg܆s΍7n14?M6kHg^!y r"ɦ%P̐ݻwݻWKKϯAAAru 6lٲ%33[o Wdyb>W |<pK.޼_%tb_ҋ"s#sK/-6 @ y\HR`@+GFFZ[[/\~z`m W^tӧw׿LJ@ ~9;;+)98O7r<+O HJ{;0DR.k)Ϡ2 _Zs1=Hw1C R4C[^KC}}#3&zGgT?6cvHՇ@ '5HӧOOrvvNJJrss(--NYTTjcc׿\ @X kW*ILIozSF;&fsW{1Q~7[N= >s梅;8GXdV0}5lz<EQKo6#S̓2>{LA'a_7uJI 4:!QQ-x:{XXOqѼڢ;7Fx>)6 (_E{d6+'s,W7M[h '->'96(UkY{}^d9͸kJ0+Kʈ2A @| !@.@bhh(22>244>y򤢢"ꚓcggt)MMM+++33͛7r XN|vhPԒvdZ".hౘqaK.\\QНn6-!|6Јjzs&bil4KzMׅbۖGLʀ .iwp٠4Ft9'Fð4݃US=J"^g}@0ܧ|yTn{f<:.iS429;LG}6:AWMU\ȇ9?{d.`NO4F ]!3hy*Y]9K ڗ -zmN$5S LߛMپ#ɴTs/ӑBדx f.Uc/RIlnk}t3ab~WFa(-UO?wE1g"bPb;-ܕ+"Wc!@| 𸸸ǏWWW߹s.##䔔^r%**wg~|ͥsr9".GȽ7``H(, )tqlCB#Fٹdell\ ȷ˥pv0fj\gn+]zDhxr #fn3\siU^YŧņߪCķ+лn~N.Hs[7XF5>/N> wO5-xҪd\>S>Š\w[ӽ0wU鴕OtJ pDBDr9޾\g0~#zYK*Q2Uƞ6*10Ӄ ,5gJc B r@| NI A2 X#H =☟Maaa\1/]gl8׏pa4Eh<UP~ۚVPldR]٨7U5tii9uʮDotqvXIwcݓʚeDJ K0K))eNfr]IiI6zDilDnqP S+d?jvF=2Yz-deB+k+T]V9|& &넮stW&FeU7XkfT킕{:bf=fʽӇzSJ[-wb+K 0bxʱj9y&BW(͋;T9DO#1fqzcn~; M$ޭjjs0v-DS<4TqN&PXd;A~ne.}W{{} N&w@ erBR cğ1$$ԩS ֱ...qqqs> |]?\4?/yxzJS]EdO2c@eG#}܃^AI#CC]6>{ G;$pr9av>a޾^ah+h !@ '5 .+K3 @]]]UUUKKAUyyoKK|TE`'w ~C @| dK`@_nnn 6q@4MLL\]]ڀ\\r8/EÛ򴺒QPvRr߳L\0L+/$y䉾ҥKS8::'N:u &(ׇess3X!* *YU \P(Feel free to [open issues](https://github.com/bit-team/backintime/issues) or contact the [maintenance team on the mailing list](https://mail.python.org/mailman3/lists/bit-dev.python.org/) if this text is difficult to understand or not helpful. ## Index - [Quick guide: Synchronize translations between Weblate and Microsoft GitHub](#quick-guide-synchronize-translations-between-weblate-and-microsoft-github-upstream-repository) - [Introducing the localization process](#introducing-the-localization-process-in-the-back-in-time-project) - [Transfer translatable strings onto Weblate platform](#transfer-translatable-strings-onto-weblate-plattform) - [Transfer back translation from Weblate into Back In Time upstream repository](#transfer-back-translation-from-weblate-into-back-in-time-upstream-repository) - [Setup Weblate project](#setup-weblate-project) That file **does not** describe how to use the [GNU gettext utilities](https://www.gnu.org/software/gettext/manual/html_node/index.html) to localize software. # Quick guide: Synchronize translations between Weblate and Microsoft GitHub upstream repository > [!WARNING] > Do not follow this steps if you are new to this process! Please go to the next section and start reading from there. The following steps are a quick reminder for maintainers of _Back In Time_. The goal is to synchronize the state of the ongoing translation at Weblate and the modified py-files in the upstream repository at Microsoft GitHub. 1. Press "Commit" in Weblate ["Repository maintenance"](https://translate.codeberg.org/projects/backintime/#repository). 2. "Lock" the project in Weblate ["Repository maintenance"](https://translate.codeberg.org/projects/backintime/#repository). 3. git: Start a new branch. 4. Download and integrate Weblate into the git repository via `./update_language_files.py weblate`. 5. Check via `git status` or `git diff`. The `po`-files (not `pot`!) in `common/po` and the file `common/languages.py` should be modified. 6. Commit. 7. Scan `py`-files for modified source strings via `./update_language_files.py source`. 8. Check via `git status` or `git diff`. The file `messages.pot` and all `po`-files should be modified. 9. Commit. 10. Optional: Check for redundant letters in "shortcut groups" via `./update_language_files.py shortcuts`. 11. Create PR and merge into "dev". 12. Weblate ["Repository maintenance"](https://translate.codeberg.org/projects/backintime/#repository): 1. Go to "Danger zone" and click on "Reset". 2. "Unlock" the project. # Introducing the localization process in the Back In Time project ## Some facts - A translation platform (Weblate in this case) is not for developers but for the users. It's purpose is to make it easy as possible for contributors to translate strings without knowing technical details about code, Git or GNU gettext. The platform is not part of an automatic build pipeline and does not run the GNU gettext utilities for you. - The Weblate instance on [translate.codeberg.org](https://translate.codeberg.org) do not support integration with external code hoster (state: May '23). This is not a restriction of Weblate itself but of Codeberg.org. The practical consequences are that it isn't possible to create pull request or to push directly (or automatically) from Weblate to Microsoft GitHub. This may change in the future. - The [GNU gettext](https://www.gnu.org/software/gettext/manual/html_node/index.html) system is used for localization. Strings in python files enclosed by `_(...)` are recognized as translatable. - Despite _Back In Time_ is separated into the two components `common` and `qt` the localization is not. All `po` and `pot` files are located in the folder `common/po` and used by the second component `qt` also. - All actions should doable with the script `update_language_files.py` and there is no need to directly run one of the _GNU gettext utilities_. Some technical details are documented in the mentioned script. - The current setup do not follow "modern recommendations" and may get optimized in the future. - The "compilation" of `po` files into `mo` files is a separate step not described in this document. It is part of the [install and build process](../../CONTRIBUTING.md#build--install). ## Brief description of steps in the process 1. Scan all python files (in `common` and `qt` excluding `test_*.py`) for translatable strings and store them into the _message template file_ `common/po/message.pot`. 2. Synchronize that _message template file_ with the existing translations (`common/po/*.po`). 3. Commit and push the modifications into the development branch (e.g. `dev`). 4. The Weblate platform do pull that changes from our upstream repository into its own internal repository (manual triggered by maintainers or automatically). 5. Now the contributors can translate using our [Weblate "Back In Time" project](https://translate.codeberg.org/projects/backintime/). There is also a [translation landing page](https://translate.codeberg.org/engage/backintime). 6. The translations on Weblate (as `po` files) need to be committed (manual or automatically) into the Weblate repository. 7. The `po` files with fresh translations are downloaded (via `git clone`) from the Weblate repository and copied into the BIT upstream repository. 8. Check the modifications in upstream and commit them into development or feature branch. Step 1, 2, 7 and 8 are done via `update_language_files.py` which is a wrapper for _GNU gettext utilities_ and Git tools. # Transfer translatable strings onto Weblate platform Add or modify strings to translate. In this example the string `Translate me please.` is translatable because it is enclosed by `_(...)`: ```python common/backintime.py if __name__ = '__main__': print(_('Translate me please.')) startApp() ``` Run `./update_language_file.py` with the argument `source` to scan get the new and modified strings into the _message template file_ (`common/po/messages.pot`) and also update the _language files_ (`*.po`). ``` $ ./update_language_files.py source Updating PO template file "common/po/messages.pot" ... Execute "pygettext3 --verbose --output=common/po/messages.pot common/*.py qt/*.py" Working on common/backintime.py Working on common/password.py ...snipped... Please check the result via "git diff". ``` Let's look into the modifications. The _message template file_ include the new string now as a `msgid`. ```diff diff --git a/common/po/messages.pot b/common/po/messages.pot index 6b690204..e7bab701 100644 --- a/common/po/messages.pot +++ b/common/po/messages.pot @@ -15,6 +15,10 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" +#: common/backintime.py:1196 +msgid "Translate me please." +msgstr "" + #: common/config.py:95 msgid "Disabled" msgstr "" ``` All _language files_ are also update with that new string. Here the `de.po` file as an example: ```diff diff --git a/common/po/de.po b/common/po/de.po index 68dc7795..b6e07b50 100644 --- a/common/po/de.po +++ b/common/po/de.po @@ -19,6 +19,10 @@ msgstr "" "X-Generator: Weblate 4.17\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" +#: common/backintime.py:1196 +msgid "Translate me please." +msgstr "" + #: common/config.py:95 msgid "Disabled" msgstr "Deaktiviert" ``` In the end those changes need to arrive in the `dev` branch to get recognized by the Weblate translation platform. Create a Pull Request or commit/merge and push. If the project at Weblate is correctly setup it does recognize the new commit automatically and update its internal git repository. See this in _Manage_ / _Repository maintenance_ section: ![Upload to Weblate: Commits synced](2_to_weblate_01.png) Looking into the list of languages there is one new (untranslated) string in _German_: ![Upload to Weblate: New string](2_to_weblate_02.png) As an example the string get translated. ![Upload to Weblate: Translate](2_to_weblate_03.png) Go to the next section to see how to integrate the translation back to the upstream repository. # Transfer back translation from Weblate into Back In Time upstream repository The starting situation is that some strings are translated by contributors on the Weblate platform. Go to _Manage_ / _Repository maintenance_ section. The counter for _Pending changes not yet committed to the Weblate repository_ should be 1 or more. Click on _Commit_ that counter goes back to 0 but now the _Outgoing commits in the Weblate repository_ are increased. There is also a difference between the _Last remote commit_ and _Last commit in Weblate_: ![Download from Weblate: Committed translation](2_from_weblate_01.png) Go back to your local repository and run the known `update_language_files.py` script with `weblate` as argument: ```sh ./update_language_files.py weblate Execute "git clone --no-checkout https://translate.codeberg.org/git/backintime/common /tmp/tmpbbv11b11". Klone nach '/tmp/tmpbbv11b11' ... remote: Enumerating objects: 353, done. remote: Counting objects: 100% (353/353), done. remote: Compressing objects: 100% (163/163), done. remote: Total 353 (delta 177), reused 320 (delta 166), pack-reused 0 Empfange Objekte: 100% (353/353), 1.31 MiB | 5.41 MiB/s, fertig. Löse Unterschiede auf: 100% (177/177), fertig. Execute "git --git-dir /tmp/tmpbbv11b11/.git checkout dev -- common/po/*.po". Please check the result via "git diff". ``` The script downloaded the `po` files with fresh translations directly from Weblate (via `git clone`) and copied them in the local repository. In result `git diff` shows that the German language file was updated with the translation: ```diff diff --git a/common/po/de.po b/common/po/de.po index b6e07b50..f2b73be4 100644 --- a/common/po/de.po +++ b/common/po/de.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: Back In Time 0.9.5\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-05-04 16:31+0200\n" -"PO-Revision-Date: 2023-05-02 21:26+0000\n" +"PO-Revision-Date: 2023-05-04 14:59+0000\n" "Last-Translator: buhtz \n" "Language-Team: German \n" @@ -21,7 +21,7 @@ msgstr "" #: common/backintime.py:1196 msgid "Translate me please." -msgstr "" +msgstr "Übersetze mich bitte." #: common/config.py:95 msgid "Disabled" ``` Just `commit` the changes to the repository. Keep in mind: To make the translation appear in the running _Back In Time_ the `po` files need be compiled to `mo` files. This is done in the build (packaging) and install process which is described [elsewhere](../../CONTRIBUTING.md#build--install). # Instructions for the translation process ## Consider Right-to-Left (RTL) and Bidiretional (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. # Setup Weblate project The setup was done in 2023. This section is only for documentation of what was done at that time. For demonstration purpose in the screenshots the BIT upstream repository is not used but a fork of it. First login into [translate.codeberg.org](https://translate.codeberg.org) which is the Weblate instance hosted by [Codeberg.org](https://codeberg.org). Use the plus sign in the top right corner to _Add new translation project_. ![Weblate setup: Add new translation project](2_weblate_setup_01.png) Insert the needed information's and press _Save_. ![Weblate setup: New translation project form](2_weblate_setup_02.png) Weblate differentiate between _Project_ and _Component_. One project can have several components. In the first place there is no _Component_. Press _Add new translation component_. ![Weblate setup: Project without component](2_weblate_setup_03.png) Insert the follow information's about the component. ![Weblate setup: Create component](2_weblate_setup_04.png) Press _Continue_ and wait until Weblate has scanned the repository. Weblate do recognize the structure of the repository and the location of the relevant files. Choose the second option with _File mask_ `common/po/*.po`. ![Weblate setup: Choose translation files](2_weblate_setup_05.png) Modify nothing on the next screen. Just push _Save_ and wait while _Component is being updated..._. ![Weblate setup: Component is updated](2_weblate_setup_06.png) Ignore the next page _Community localization checklist_ and all the red signs on it. Everything is fine. Now looking into the _Dashboard_: ![Weblate setup: Dashboard](2_weblate_setup_07.png) Clicking on the component the status of all languages is shown: ![Weblate setup: Languages](2_weblate_setup_08.png) Finally a Webhook need to be setup. This enables Weblate to be automatically informed about new commits at the upstream repository. The Webhook need be installed, not on Weblate, but at the upstream repository _Settings_ at Microsoft GitHub. Just use the _Payload URL_ `https://translate.codeberg.org/hook/gitea`: ![Weblate setup: Webhook](2_weblate_setup_09.png) January 2024 backintime-1.5.2/common/doc-dev/2_to_weblate_01.png000066400000000000000000001143061465446530500220330ustar00rootroot00000000000000PNG  IHDRlsRGBgAMA a pHYsMMg[IDATx^`ƟӸ+$\;E  )hBq-RHq@qwwwIB]r]HB((kΎ?ILDDDDDDD~寈 K K K K K K K KLUN܍#49HIJ@Zr_ĠAVz"o?O_?PDDDžܷ.^Grֿ-:ܻCۥg-JAXh I٫@naЩd˹G?uZu{S/c&xPDDD Ǣy>n֨ՄK8(]j4QmO1y(S}gqr\̿Qx'/lh7K@0eP(dXΘ_M7 ~u/Sv$n\UwO?FHdj&3Μ>S"!_P\z]u=8qaO[xt/;\s R5=MHrG\~ MqIxx*N:W@eN&?&+ـqY<+#Yxr Nۙۏ"B&@_;GWco_MĄpuDgKܨD|Llgi/M*(}Ic?H?7M?4{TS5M9Twƛ>04ih'#~0O`ӼiߚM3{#m1iW""""n=1&apnaroaC^is,_].Enj:Ba\X`ґ|$z4C01d~\wzbىGxk&l9).Z2rdFH+O?FHMe7`ڧ- ف᳗Rm;q;N.X*X3{:U?@_C.;ܹya$%ݝ_64&ss$HQ vh^<p xOgMȏЪLID)nJRL!.U#q*7 oqQx~GQƬ0+Y ~~HA& * oƍAKa0hpq ]!z:ݛ/ W#Nã?c Pzh WdgUЭrYHR ^{ Ck;KVaj]Ц}{8se@wt4jۦb#^K^ 8Z-NVDDDB4 @OA:A؇3Ԉ#%6/\qS"ȶ W-g67]'Ul= A6*Cb+|@uHIsBy<6Ao#'K-٪AF:dȺ_&'P˚Kbn0@QƧ@x~-Cg=\wW\7Zuab [2Ge^8|= ;!S!0raXCAϱtrEJ!%UHKO> 8xx$m)sev/gV?z2;~ OG>_>2pu\}#Vbsi)YPQԪ&G:s͏ 2%[+w-ǵ|fE};qOw/ /SCHnP?z Ib g-8|5^~Z6ӯ/8WD8DаyzUDDDDDL'S""""""OSDDDDD%SDDDDD%SDDDDD%SDDDDD%SDDDDD%SDDDDD%SDDDDD%SDDDDD%oy8Q0EDDDDD^DӉ)"""""$FL ny Dy Dy Dy M?""""ALF$FD|`0`!1v4\D.N4E/A%tzR KV,Q!KS;yc q  JUD?#B^ d:ӰbX/-4- d0yc>g:Y95A )UH@Pg I`24Hʅ`4l2v討IRQ*a4Px}m2PR7Hw[oYd@y'e-F=1O_ *, 3(2jGaP|TP@QPg)Qӵpy*ckxVOy񱺳5ό48>f|?sI؈GV7cKP 5?x@|>EHP-la͇S`ָ8â0'gz/w8a[SR5\VCG#sR*߻Yqܸu)\˽qb(zQOK<ďr]ykq}qzZOߖ8f{^Zg.9n[|gR3RsB]ylOӜy~\b~'<:ä™0!\ K+,> t;ژj[(}2Gsvtr=hD=Ars۔"n($]z|Q9dD5Wv\BFNq#p˕g2Xp&~Yώ^4%:KU/+O_OZL™HPʨr%F#I#eEG Mf"CB\uxDjdf$#:::2|cN2BcKcIpkPPd,D!(,)FjT(fxƧ(LpD )G/d&-A ӊqW)1 CxB* |NR#p+O$bH WHjTR%tq!MRǦz|[015[؟tD"-[C)T&ԵѱXqWK0ɨ,=)\  }\ A;I"1AD@u36i9c+rcb"IT4r *<*I#$ !%% *cAѴ%NY|8{L5߽%=+awc1yL|ӳ*՗,(]Ϡ:I%vG* YZAąPVa$酐{ ͘j)Oii$yܠ4 ^tʷ4?"-]ImDI#e]f<? Eb^0OGe/AfRB(%'5%$Z$Ƅ#82r:MӛK+n7ɔ^p}ھBf:@+z',7Q[&QX>?8 RȺ6 B+Oi a:8 R\x7=ޞL%kq8IR}2cc<9n5iӦ^Fu  O£}qo< B?EvG+5-HS@R:斛Iyŧͮi1?5o@q8 w*^=> WN}JTm* SVG=g?~{A`e(d.m[y-8ܰߌ)Vax$C۶H"̞6m=^:ȍ#h_F I.D4v/\\8b>~JYױhdp"QN}pŁU̳uAC0vn\k,~1_ބwQICRp&L4C&,swjtFeqq^ܒیmRps ޿v]t[8όI8RL6#.†Y5ŕݛ7y$ڣQpnt|4v#'ò0|&yF5\,?C–aե38Hhl?|RbϜz={<>k߂_.QأC1b\k Z*MAoCLKsȯeC4QՈ7УЙ؅6䵋8}2Fqήо7dxli&fs{R?/(oKu{ Y|*nݺۉ.]: ?gCc!NS8ނ߰\PaV;L!0tcxE_qnOFe! Tۉ}uByGQ>4irFP:qug*'B4>RPjmQoǒ{y\OE7+C0ᇐvc_jWUl߽7bh\UHG*ӗp)CfUaGy%J~t۵38v s(M¤NgQ#jRgs`øp}rUQE[{0k6rGW_V錇ǰxVk>}Pϓ$~dCCbʥJ4sF%Ȋ.rzz^֙n de [;YSp!4;ۺ!+S5=!n#s. 7!m[\hG,į] Ψf(J~J:MyDKBSQEcxrFO`ߦ-Hې|l  ۡ(9AڜYH9y) .t2$r#l@f 0o~ $n0γ8}u&_aCѽs;~߂Rnݜ} „{Ph|+` j ~مߦ F:UKf3zBKp YvkiS LnZ%`O?`|h=vz0`#ĕ*`¢3h?d5[~sPIwah _K1y2Hg㽲9X3}*m?GQ}[(ņsEAY[Q󽽓մ͸s,:fEG܀c#.Xd":at,TKV&E?ç# vL z5! 9Y[*6:ۖ/ڡdH xg nw0q)g?'ˈ$]hW[l*cطߡw=X&#Sfï'!y,3Ǭ4n*~X['M] ʻb̊FȆ T3 CXꞜ՘z,FNICi);KqӪp*]{*xd%<ĥd43ʶ-#ǂZҟpr-KybwS1ih&Y;긓j)N*qux_#L uMP ~BR{~&g}zIRn (C0y$\Vܸxx|5bEouS c`2ca]\ۺb5(U= :I[wy9쭗Pqd[)d7TTOj}-hݓ'cC#Z3>“Esp8#s X[C1CP)tFHf,R1 M\#݇~8ttO!_Y+CK0O4p> rPIo|=|*~+bVơt$BJ^?}I`y#a@*#B,TЩa]8I t(MFoۈgI^ȉ[Pi=DEjEdbȤq+N4#Q#cv<΂V#| rj[rDΞC{r3<1_kKH B-W$Ԭ& ԭU˗fxCZN0y>[FU?gx4 !c0ғ4#oOϲk5`P >x-rT)plV~? ?yfЯiEԫZɪCȤ k L,opIF/faڧ}ѿcshNsq=QCoct~hS* f7u`|ruBlٟEd4bob{0~hmG!ŀɢRIq'ڧ e<Jl` {&x[v83bㆷ'wnnBCs5m ߾Cѹ/f^(v! {ֵ{qTCj_Ԟ9r.6(Y:jU(A=pN!6oޖʚ*J7_@D?J| o< #U f/Pu8]J]^Z>hK,@* Rv]Hm _ɟg \BMr;8PYO#Te2uz} bo&*hrЅr[L7Mx^`|1Wϳ1V-a/ &&Y SOKV2v+: :RSC zKG=bހۼ:gH٪ɔ,U 7#)t ݺEH 8;Rc0_# ''GJd $'7WU@y3o 0@$=CYq5:e fk!+;EAUcag!2o//֋BՈCMsfy,; atO$nB㢡cgs$48b,7Q@.\ j2㕁"Лᑕ0x-}LBUx=:aog?&;% qW.5j#a;::}[ s8j`kcD$oYTtN SqyYu5'#%#WFNHUϓA|Ph̝xjmMLA؝:՜)!8[qy!$Fxx Gz1v{ۢi0]In|hRSUrZ6EO&\h1램֮?z*ꀅ]?Fl3 CfSsUk9WgR}6}PIM*Ӏz0P)F)L 'Q@¡y5TR(7/ikn3W$j5y\:M9 BvaiF# +ޞ>uڢKyNxB } $ZM?"),DdF$o aGylbJ'EAuDQw P j]F6Kq?8 ׯLבTFIFH/ȩ_%V֫ yj?aӯJuҍAJ-](1~PNbQP'$QAFڿB}i84Q/+P҈'Z5{vb׈fpRcpzi/8r>k#K6aWJj'{{)d-p'8::ƀ4߆w(n؎/fR|ٰ +7R1c>+#Y vTıd&' !왚F$:jȗ߅CQu1c,9VI֊ HZ kIPwk[:Pv;S>r`^7oF\cKuKbָz3qgav^<{%[^{BPΫ%v,xdoW+,f>Yi,R&)j΅cqf߭!TfH,^ VJ'ݡ{+ncgx/O; >S+b;QNtDu1 h\t?p "i1y:ݣ0k T/*9.ER_b tt*n3p48/RV8nDAW: 1pO3pI޿p[Ų*xE[JGfRP҉;ߌ#*Dޮ:g<\.ƾ9_L7 pf,>\냜b ]Zd32(Y{ʅҭ"PH:wv]bb ?lN ߻c޸0i T6d;Z |Na"_- IvDmd6i`fh.ΔxkcH|YfCpw2-.h8 V` [^iiPFV>FwU㻯 rQ. BOȊŕٗtm/6DewQc b~>/3I֚DYiITVb٬ؖFeHw!T%EItiSv*2jSTxjڕU0//&VS Jvyo轢Vږd&GS2lr] SArG)kr݂N% &1fT4 rGeAuN$ dS#)Ot4ʌKDJZ1qE ?Ʒ`oq-y3KmDn#>*>S$Dfpһp<&/ˉhT^\ТSW4 P>}6Υ1ϹuВum2hս;w m85|~f-{2f︂=43;re}EˉuRr-B˗ P\>X{i$r'T8?|A5 8sדP#gCnGڃ[Gc_:ѬLFSt)& urԲ1鐙ls퐶z$*5zGz0A CFzZYldhhAfkB Ϋ"Pkك9ٙft/۬-P<֜%5h1$ Яc6Gf1j.y{*~jZ5 wF¯ֿV뎪 ۡRF~L,\%gR'j9ڐAQN^bLOv .AI!VQ֨֨-ʵ &~),yNtktҘriWHFbiO0nfQWu FM7o@PXs1Fym|7_ԧb=:46!9 VM֨Ҡ OݺW TTDxzzj/م s1TE1o"c\}D|FxWTTO^zz"7*k~Y@+N6u3>ԏc\mQ:рɳp(cWfpg xTY$ ۰+.tD Jl[4[) i=^q#՚,L6>G0,8EaYnro_'g, !B0/pSaiLʻIbXᷛX;~neԑ5t5L;gQ{I?kd4+PaRxsQQ煞Hi7XnD_j|~z{~fFQ;`4i!\擙¯W)J⚗yww^G;ܼWIhjlQh ow^W1rV'olomk^sf.=?6zra&X)zo)Y 5/]:R==!M$Insc-0mm3 '_5wyϼRR&yʁhNXۍ%r"ܮuG(?:͇w;_JHk}kYB_!ľPۨ%_MH/!}?:0{aTyc^WI6ʡҵ Q[P#B:ڑiHJ$2g ǘ&ݢj+i 2 ?dVmQ`j%@2'MepVH #K@eKe/?\.&Woʰ 򼺵o%xZۊK@58}<-l?{e^,XE2Z sU Cp.sYp]M!)2hv;΍ya9(K(8Biѯ NOAMקb=XgŊJH0ށgoSUB[x+\5j*{>lgV_MVt{忁 lQ-)J4 4eE-`T5Q6d%VmVnYo/q&<=\" +G _+lq,go¤r1AVEbzÂ] dSfEr{cOGЪY#8ulEL$܌1 ",<*X&*Fa4JF)HϮfRxK  _f("""򲈂)t:~J+Y4;;;aTɣK&_DL,,P#''* w???K# _ALVE\\ܹ˗/#66V8V%5j[n(V ,lq)""gS?aJJ1z aaa8::]@YPUO>(QC#"""G_7dee )XVɓ'qyị |||&%[[[ʕ+ ]rX""""Q0E^[X,YxmQA䴴4\pAs,<~ fBrSSSY+"""{)‚##߸q7oJ^,_<*FAffE7۷o&-ϟS 5cN__?2 ֊pSS3gxL2TaR`mus[|Çe+:)^Ąd ?gXLUoE)P*#5!)jëMJ^$-?GiTL,y)?("%V+*,|"%[ŋGrlܸ1ׯ/g1YX8,Xl1fgg [zO2tNVLcXtE( VR+uIgd ˥i8rS^.@Fx|FŒ{G0ujJ@WEc9iPohBLX, ,nC!YHû]YK~gyZ^zڵ+4i")ed"""%"ɖfRR~t^fT#:)la!2 PS\SR0QSOgP@Slįg ]M)xz,+8q"VMg4 yF";3#-' ?~廈ȿw@lAfPGm=N?~\1ۨQ#.]Z@ÂgĢ?w|`Z {X9g#{sAQGWҹ$j);Rhզ7-t\?,Cc-̜as0 68;gMǖ?tVY5=+Kpzl,& 1wh$__nQ| c!~8cVMODb:O/ ɳ@])Ո:ݿ0m z Z^l1 Y{r6 CwcZƸ\re|û!d G(XU".ERP('Mł_/62pcDo `ͤEăGx%мyyx=ѰUWݦu lX9U0bZ4&?7$0f6z8xEP7oU.Ϫ2tPhCbp.q &_uCZezͪ yp* NAR-7?KC-H(f>%B:7CVLT)%O>-\aY!GZfJ5aH}tyaި傀F}VEO܌EDi{_nu׽kmı{J4vf˦pC So2EwpFV0.  ?(",b %&ox^wƖ*UJީS'aYY ۣiӦ\Caq֏~yp$2h:ٻL5(F"l4:.I[;l0I;= zxHu0cFh0a!Za+Dۦ]V4b.g ]`ϙHRym$lAnl1Srl!ASq?^V:ȝ&݄c12v|UEr9Oʢ@uGo"^t0 4x|d1#`gChH#zspTZ%w$gnBPڕRdwz%`^zQSƐOWڠ- \~*Irүo[vaX~*,R>Rpj߀V L\>E,(Xɚpz"8`XEjergӲv/Qg,[3?筏?Kvc+5}Y; V oΎ%ENb3X^AXwsO,998`6 SXHx4>;茝gO9S9ؓ(0ޑ Hn;npX|HIacgwrбD%6( 9-9nMaܿt .ŃqcdKs[S.rkP#!+g'n^@D [ _۷M>BTB^G>$d)!p1Jqف)ԁ(GT@w}1j8NƖEe s]Q0h>J-TRxAJ(xI}h,"4`v9[Y4y7Hl,y}QJ0 $[X N7H\Op^'фb0hט$NP&D!"׼9i R'^lB!Em: I?}f#:( 56· SknvN'js|]9:-u6IEJhEj:P):YU=^Mk-VR&m˓imFr4:عB}/\A|sp gM.`h+7F}7>CBb WYl\8@%%\a< ɹ|*{XJFz gwb|$tꠁ3cא{;X3 \'aЩ lR\,E"}:UᖷaJU! k[_,lVXc@^5Jެb"ʿZ¯{NY^d7Y!eᰠZ1i5Jӣ|+\tjee{KaW3Hp3t$,:bO1hUHGA(>PaJ:;uƪ+8,xS?}=Fbd˦#hܻ)b6D]z;AG/Q!M7o .(HNb =g h5[bsV{S>#>EIд_-C:Gm()L&=ʒt`4ڀGn}˙8Mcㅷ;+к;(\TM)e#YPN->FGQK?xxBtA+0 -6ow(.QMH؁:'Sp g-{p,7g{⍞]ݑX7%[`KLFR0{-P e?^",QœaOHHΝ;' @x:M?lE2&KO~;?YN!LR*"3c挼CbDNZ2T&{ *S+Y0ٻӉ(yi} ?,Ȝb SF6E7"i?<̛;ˀ<$:Qo搸 *ϔZDy`◣K &wl t={_/5`wQ,y, cÆ ,]t09L0jf+;]:;M;\Fse (twc;s#oaq`5p(.|K> Pd|:hq} ,@Z~9OB9 +tYKcx)꿰ryA"̳U'\aF J2D\9g4>|s@YhR[a]"Q0E^;3bk:y9O?~?:lA5ifedy_Sk7K _;hy_#"H0=w6FSsQWۋ}cQ0E^KXx]2U{sNAyMwòu,|| : 7}7o.i Ϛ5k DD^5RӦ< CtJg7Pn1-oSpJD5-CgR?Z'X,z7h2bV&(iEd~a; ("&Z{#Vctl mԀBQ0E^Kӥ.X΢ƢS,3^,ᇭI~1c8~:%JbE^ xNg~V/q#`{y}e˖c&XA{xOڵ7ɏXCz?DDDD^q S䵆EwXX vK8FC? /;3|G4&Lg9G˜f]#-G}eںq97 is^%穂ibܸ=fhL ũmذz.L۶a˶r8C۱C!Q4Q[ qװ`ngV`h߀\ $dQB=1dO<)0?z=]kgOHIȄAp3 [9?; |BeۣX\ z% _/K@׎iظ-9{}O 8^ -bE!,61PeDL9)Bd[y)%  YX o/Q=}>n]He$lųKxPBD`'IS[pxk5R%$X%rHj87Z~_V#TiBEo/VNqPIk"HSPtN8;u'-w143{r|;i&n*Fg.Xz1/D0ug؀k[W5-nD#.\KF[ \}H=9גH9VH 9I_vO)x4",+qOLuh:h+vn7ĵߠRpk>N%BU;`C8O=D> M. l7&Chp.3΢G6cn\v aW!fK2"CPIX,߁+O_#:NJAx՚V.jѭI-Hkk(Ͳ)rH"MmLJ:4~BGZX;nMU[Ǫ f:tP)X5V 3dΙh^d{GZla@fLfJSktHl[-&z} ႖fԦXv!Aƙ؄C0ky{~=Xw1 F=,n^a\Eop 30"?q/#;,~mz}Jb~܄0ࣞxo0b$<gR,+To`MOʔptrnNM{:lop}<<={( 11{tgs~4|6Gnɇ!tE M2NWq& e&~:2[E1aы8z:[ MF͢2/6؏X΅6!6V]aɅdg3a6b>h= ^}ǡK71xNDX܋ q3|3A% L9 ػ~2|eLn8v,=Wv)gEժ7.A⤔ÐuDrI< ¥d>eh`"wn<+!-yCuNVe⪅ j7Mql!#1ՄGX\9u'OF"yHS t) P@CR0Qn.1+ hH5j+EҎ Lnx}J{kPTyM.(&?z~B\|ÚX8kb[C ~S才sǘ =tl_ aaS)P̯:{aUd>ڂrtyCiT:a}0-G,6}lPjDF V:4jQ~׫ ]* '_?zb 1*hx6(+0ulI'=CF|*o@M' Lw+U NV6u򽄋t*;1q rX^ήJdTCz1>aɍt{O-0S Z ÖD(ZMIԑݏD߶:sS7"2J(㥃Nob%iN=Gk't ]y"Or)L!:1_~Z5O ͊=cZ(N\; [phؑ'l)ޙ/f=z| *(W ;_Ԫd@X7&f7¨7 f1j)ןp[joϿCqf୹1lv,",QdDTw0}+_G*M[FzM5?ea1v4(bK7KJ[sFDૻ0{:vUA{j-` ;s/bhp#W1T xm~͙R3lN( KӦ &-]'b^\^ZA"s-̘3M{bpK˄kX.W['KCr$,?_4ēp $~5sQ?*痒H9H+vjx*,+A?ªD /WM}^ +-k@G/;hmLLTAC7?u( {O|j,]4 }(\i&bx0FpKdH$f& XJQjÑ={#Iǝ˸G=p]q1ZW}EgℲ#AhԠS7=$~Pм.k%= E z WSE_4U1b8|ٺ&\|ѩu DF  u -tYPۺ>;>i= SD9OOmeu8Y֨WUhm:쇪ღp(#QS4ֳ8zW/: D#@5^ BۯЭT:·pL`3j,twX:1VRx  - 1rH_d]B`s*[i)m[/Fq7m#pH!-$舮 v#`EawgGap$wrhd !l)صj?MG1 /C$RBHt@B^xI\zLM 'd+aaCW:C1Y ѻBpr 0$(KT]7%C1v0B0x~;?,ڛtWwb,Om[Ox#::],jy8IdڎF/1chm %R1~Kx<Ȇt0 *&(8RhR?0Ո|CY [TD<Cw`y sv~^J@ \|Kkּ½I(7QQ(K9 r WYX W_>5r"KxNƨl爈RiN9NBl|.\0Pٻ r@ƭF#] x4mZ smzk<Z~E4dv"=[[, gԪ8ć@u _ ["Fi̘A]h=} ov+Z:v.xYXR2M),rBB NNG6}mK͂Z]:ikgnK*Q_~xmᎍڋny QEΘ{TFi&WLR{4Z-N]0< fZBDDI֒%'To֞Aײ3ŵ qȴLx M_{ W;~?Y/ "P?zut64!r=B=\u,THwdGL/Lh[3{ȩ=HFgC12Rѻ'! !QDeQzZ eI:IN.! OH9d-OI ՄDJ? ?R_J0Yy*:??ͨHhʬNpi0WD82 |o~S=[*`$Yc*NdF'_TPށlb>TrQs?FnP * `fQO1 Rk} fQ~7xy2" Go^By_o,]r·jiDt\@Je:CeI# @ҩ8uQI-u0V,f,/9P=U[3Dn£ F\>~:ZFDB֧ Z6/?!j30M̙6dHQTsTLHͰӿHIπk_0{g1.|7"*sz?s: ?-jI)e:!IzV*+<_F9$F#,7`}cd772:~4,e#u o O;AY=ƿ^y+]*n /NtV!k&#Tmx'%I 4/_O1N&|9x>/KcR~{qŻz#qd[wO, |GM⻍jvC58Z=uX^OC2%/FAa2x 4ܨ񖭁fGL$zUr4Sh༦Ѿׯ[>Q"c]tZعC^_7CFM]W͎N,$:9T^S2]ڠ0`|;c!.F ,YVeS洑]/ 8O}<ޟAxn/RSD{ z=f \\mPaedZ}yײP_7SF}bޛPP,f #TYpcsw9x<MBWI5c'LC1 ?IxYL$H6eUfO4f& jaVDB@AmJA:B)$lGЇS4p3􉷑t<)$(F+^#xO)jSFxwÕ5x<#CU=cԑ4OCczņ;SOsaAghHPh\gfJO uZ.LY<\S#Q 4J£!AG)Qq 08GY/;貓D (U$\-H9 \*T@9Bc}}YI1HJ쎒pVjDBx(Rhkc (jf 4:\N( QЈxjb(M$adJQTI'4 '+: :"UKbPT9<"7"=:no1d~H29|}t ΠJATt2L$- ';87>q\M`izDqD:>%P2Tjx)$'8zӞsUjRTL|Qv8 j"]-ճ$ܿL=T*'5j1qaQçz-T6kFē0ū`cbPӱ>._!qlK#@Q~ ]jR]R!_Q1$bsH6x(c5!3:OrȖkMGH%KjqF02Mv(Q ٔ55bMH`*\)&)wPe0\4«xu~S҄E"'&:n~황K# )8ݖE9dd`ɨpB1mlAFD6vp.O;i%ALhHlP>' rCa9l%\ݗIvV *%qĵEPCP VAHѡ0[xWwx׫S%`$|n/\a~۵Ʀ 6_.YW>o7T,RƘK$$`Y!5WL=$n+_E"ro~,ByXGX}q!7OUOU"|m3ޖH SRaCPR>d`.&wzXdaJ‚b4DDD^GV-ǮGsA_<'k0Q,Ysqr"__IB @yX2cA3u?11._wO~g :s~VX2,z |HKOObb |RґJzbnȿ¹1ȑ8lzX81M’wzw^dol OpRIs=8K]PRi(!  A4sf-1A 'ݼgDmZ6# K )0 7_[[[e p:aD/111PTFbbx7"_AL,e;wիRL.Y(V]_) ''GH*peܾ}N9GGG ~XPjWT }Aҥlyzx7]#nyDttbnӧO ""#XFOgggɓ't[|֨ELقdkQT ,,,pssĐjٟus|YXϞ=+|@,)))ELreccڵkº%kիWG PD 5J2y XH</ x3** Z-ʅ! k [,j,,r!!!T,Ve@@*W*UN:WFӷjZMڄ:uJĢʏ a=!)X5!7;|=#35y?!7 iJS`P`Y؍7he2ePT)ԭ[M47.'oǏ#22R?ly>C#ڸ!OqQqGBnG1jE1٩8l5>JZŸU~5Cǧ0o/7(P}RgOYYES䵄Œ-C:7^sH4*O f///Kԩ:t Xl:'kP3ي} IP_u7As,LNoڈ_O}Y۰1*4 H}W(0R 6 w"1h`ja݊p7rB Q0E^;X([~,n<ʢV! %[,li%;bd ٪U+4jHxð>ah|AX_6Bc=YNq0uB~tH8ħEx y&"Ыu^:$eP(͛tϤ):)/\tZ7Mqp+&=妴z ܏-T^nUcZI}zM&bccSUAH[(>4  =V D6dw^wLMMͳ:M<'ODRR6m*U*5N&''  -Pu(_N)8[䡊űX7\Srrp(U55!V}w"ɠTcR6xm BtJ,MՖ]Xn']a`x _ǧYSNNۂWcp55 ALzU "+h-0A p:8 p? s(+k0y"lueg1U ۴Kmkar,~bēsq^2`Ĭ-5qnF^:]CTݾ/[fj-z4k7g qcVe lLVx(4o o!|$g!Mž3m)jIؼG0o5ΥzAhžyMر۶mDži\QX yn{RvuGWkRv(Y>wRkhծ:v K{И3 t<ύ_hMɇ[l6C:,-$Y,l]ТumRrE;m>3c =קsq0{"hdOƮGlK Ök/$|;qytL;+v~!we,uuόǽ3ѣITmЖ/8t}n^[1{oD>_a0 0/6$m}rMR#‘RM]WQ O /JS䵃EY@9>ǖ"O.l7u>՝5Lr8V|g +M&:-~9U}I d _ 8g)\\<fEeC{ ΞƁ1ky8,/ćk PYg~w O3qdJU wz9K-X8q*~i":syWk20yK~SYU k[,b,h[4l=eɏYA~:wvލ3g;O,V0Y0YT #u2|ԯ?3N0g ٩rctÄѧCq8>t[|FhU9Q$FqFdp66JMԕe{z|2-]vDN~voy'wCk~8mK[m $ D0ZD0<!RJ`D/Q1bSM1Zs>U(K0)2t.f۪9E+H΅jp&~BP_ktu&N.+zPAƇa+`VM|v[]q%YLQp%tOZd]Y*="rbi++pIˑH3*P%KlWuu|!\LsUSe,_pK.t,Ӕi1IH0E8$㓴,)@7)ǎgc3i]Q Ҋt&fOKK VXa)p? --SctF)?75B Q:H0EI%ݪh9URB:1KZO_kɎ"d^JHB@cQ8U|~θ#ݬY, ,\.G!u}RF)JZ,Gձ"YHV"k2qO ׳E ?LT_̒!&t !č S%9Kl__Μ9c׆u\K$i=rfO(T+>JxXzx'rc1cÇ{nC`6 xU",jF{ E0*9'iiE}x.Fl D 췳c1c] ԩSKKWtc\!Bjt|m޷x=:-2ӡhE[v5At&|+K0a1c1)`nn,p3?6z5՚ׯmtվ^+x7.i!9N.';OH™ @4Z'G^tWoFrraˬBDk Okdd OM:d"C<2c1_T*E~iЪUHπZK϶{/D,>85.4-˼Տ5P8J5u^35dhRNK7l&=U.WBc)WJX'L29MLL~tH 2Ao\0*-wwD{H<~pw}_Ƨ ;b FN D'E|SܻwG|x~1R矏 M+ RR8p~ǽI22bw2eX:jD=ET[J {"!(; 2~j9q+c1wP(^% F7/o}1sH];wo-$Eć8}5 1qbcc,k3ґ'cb"ץprL kxMkTit HL6{%A-{Xu/}7dK34iP8~"0#p3At*{($ PTToėBzA𰲁 [3QĠ"-3}CJI@ . ~-pG e&w}G5l& FE,g ; _C}t枢R ω ش-ofc@ѳ Q!ݺآlG5HQ \Vo)%ca{ &Nɘt8̰ 򇩩oLju0r`w '}E1kx;oˇW_ӤUnXfI)H̰Y:Jvv,i[f^Mr]C,~~Z;ZHװ kN[/eM9 O!I-}OV>gjiG{(!vPe"J:K6a鋻Sh?uSwv_Zm: -wc1c2Y|oӪ(]?:9HP>%IЦZaqGoy.hR!IN)}CdkmP|5zDH,:5TX$趖(??<]% ?׎c98 ^yKۯVXp{<u{fa$:?Bu@''-S*h2.N=e:PdX}6\Մ_cp޼!.Ye4J*d^(VP yKőeK0wfbJKw?^L-҉gawX6gǸqC~v3z ;\whT݃N?_ox_-֮YN^uxK5HJ&JG^ܼ~G~[Ѹ\zkVsAǑzhC\;Π7}p˧-,NE,Ab8TJz=[JAػe1~'cz\{| ^P\?rqűc8T͆Yh=h;isQ9~FohPI^qX3/OЩu1c=I}E6V(Tt&1;*+/|y(&O>=# S}T7bʨ+wCpzWC4Ʃ# pf~NW3yq2fϟAH3#`ae %i ,j 3P Q uTp\ժb|_ZP.}i%8EIlNVPDh.'Ta[A*\Loȉ V/t xmeJb9;9kt*k9)ͻxޞPdGѱq=`annx?7*w m'-]T5Ht@vQWC Z{i=D'$GE :&,q[ae!h^(UL|hbJ=FCA%P{%nCvá@ix_Oifgc +c)DfG)nրILM`/Kݾū7D*d昹$i._iK7a_΀M:hոbiLO /7@asp4D +~ѨT~(R"iOv uυ)f ɴ_AgjkgaeiP+aI}L̓x|ch?E* 0 O#ӌ1cМGQr% t49aqHufS33NA!jJmUR) QkT,VѴP š+A:ryVO+LM`g&U=Xtk倍M(53r64prZe")W4JDPMV.%$ν 8ĿKIyǟ0"ZV#iy3< v`ދԔP(m|3Vg4f6*Awgih(^BC_F=DB%nQ' 8,L$<\DWo[% ۡbr8xQ"9+&])c'`hQH^SA{Csa%v4Cz`"Er^=}Dntrtr+Q%,.aҪúuo)Avx8s FLӇq>WOc\տ1c?埙fgn?^OF0<  ,"8jN?Ɩ^u>U"@M G`X>ƊXc8.lhhR1o>i> DkkljA%[壘$ ]sOFFO`3ê\{>g\T24"6T7lR@rQCXxUw\ /rw"9D`_{KH &{ԅQ>Fo"1)YM4䙆5ӭmöo Yh(+0Y*j%[P(u #kE(i h;q >/]k;c-:"}+_aW~/w$ 5o)s| Hlۏ|%P"}Lzwa&OǁK_ߚd(ZZ Pġ]x9xE;6=0㕐6c1oYS1C%QQ, ѩPY=.vReK^n6_ ێlfY;<7:fRJ]NQ>_O <> M*Cҥ1k9"Wh],JA})4/Yu0t-Ja1zzZYE"\- Uc]R *  4`'֕σ_ui,,Й 2$Ҵ0OMAˆ:XW%s3/ 8K ̟eJGvDCѱ@ta$vFA_6Z8'F`SЙZ!% j}+%cХ(t0ݧ["93Dh=PHAJrR2cv4$%$/=/@rRX^d9w&T$"=)IigV%!:,TdĈ%vaxl .c{p^(dkxWiqGcBP(n>Щi\"*-zW";3D OoT- 炝R|7ږ:IQH~s݁L`/wk(t(LSn >@_#/nAxN(^ 54<WoBͪd݄&yc 3~+SD&a䌜kE"ȭLGS_~Lt|5.{ "2 OC'[%- ܾU#RO 89PECKE(L1c,¯kb݈))p33>6ʼcZ!jH~q=K_~]Oʊyc0V&倗T*GFѓ2إ{+՛''\}8c1c1 55<Û+B*2/"EB WN/$o+׈Z#n޹H9ZB"[1c1c? xRSy}`bʁ.c1c; xc1ccc1ceK2c1˖8e1c1-qc1c,[‗1c1X/c1cl^c1c1c1Ʋ%xc1ceK2c1˖8e1c1-qc1c,[‗1c1X ǯ~C(ػCIZlI,8H1c1?ƀ­{Ҭ'f0555,eV =:eXc1cxc[x3) )iڢVLlXc1cz5`551٫ Kc1coJ`tixI5c1c怗ӛ1c1 -c1cl^c1c1c1Ʋ%xc1ceK2c1˖8e1c1-qc1c,[zF41,buw)7AA3=.&@ȼcx"9{OLcGhTHIMR?o:~NN7,y-ZQCQbX:ΜQieR/GgAIa{<*? ?MގIjòK :ųr`aKuqAxOǁ#1NL; & o_qejF?S?00N%mG ~8ҩdC_˧0yixi,ރT0>{^i ~a'G (2^<3:poAH"^5Cב hTq(&ߐsoW K:&L_W-AQ[Ê,)^=z[ٳF-܋д$[`,/~:\ljSeFul_ w"^Ũ`O?RYRo =(ov ns-* 7S8| zubaC_ f&oSq6vS~V!޽gAļm&{ ݿ =zWqK}kDd00>NŔuFAp)M$7O`հ6mvѸ° 3-bH4Y JTibfX#89>aHHNNFZBrxhohzNԔD$GD_& 4HF!%]%Tqb] SE.>;=Y/>b=F.^+K3xT%5~gz4xĊ紜G' %>ߧS'#~ b߮5ZG]|ּZ KK` 4q#ۍ_VVV0<ςT xcUNñQYo`bjKk+XZZHiG $԰$$#0*ߍfY1m3 ':XxszfNٶ?f_#1"hi8z$n._]/{a٧Yg*]CDJ8yVkд0\8MCb]H`vzְ6\4|OÌwwvZծ![1hؠ6% QELR0ekXU c[V R\?fcøww?FuիnjO+&TfרZt BۊqS8%%|Ĩ1fCu",V ij/緾q-V?O€G{+GuFӦub[G- ÚŊXb(Z lE߉cfNn]/6i>EnpCݮ5>S77ư꣡O~-Q%K s"m+KӠ6]{aWva`&stq |Ezt;mF5ƀ%o\O)ĹDQuJhqφԮZh (9LM5@ϛ`ҖH0bq)L5>i6C~5I!8b~ukWs $p|~_4i`Xd1i:ha܊؄>>Oܥ?ÌyI^DE"|`75PaKLQl;g;IIMHUO3!]Xvl8YQ(,Ͱh:(_EZڛyUQ,6 Ձn{H{rt6Av(^Zuxzf_~ !'\`j8  m+Oe BUrsB1ȃg1Ӌ;W?&G஥8w!/MWǗ`T6hkF͐+54)N5]p>ݴ,ΨmtSWh`r \'c S5E1{r 1 W_d8qhC-Vt SоC]??oƲQk [sǏkfW<^AȐp"+!QXy\1`XT}_ lq4Ŭ`)v؉&~_:c@x/UW\X w6SPONMank%@'JCo;q  +=3Gj P AX뎵1J@Ѭ6M_nR5q۔E])`J*W!Xj>;Jzg=zbuGNGd E`nK)j|`ظYq%cXr8 а~?1;yZߏ*vx5)ط` fPн .9p \n ع~+B5fjj'PR0nk/uw]i8|$' :q ~'O"Y$/N#Ǧnckc.aڰH, ?|wZUJ%'{ӊrWNbL=2ui2W1i X_!:EOtk֡xBC3딇1vV|u{<(3HT.兄]8r8U:nڇShڪ#s&a)}#_j]]8i0 ~k#f,*4;451uкB8|:48x^ӆcifXvƹ1Pdh.WFaa| ncjSp7uAI8}.S> 0~ĻWg6 FvfЫw(?M2Q Gq)xb8{#Nۊz VCC z=[YqK犢'E8=I29 WQ^/[:͒Eq{o8 JUI\ߐqjL tKVCn0X3})N 8~=HeW$Mcwb|B_ڎASx77L]G}avw w.Fl:!tȏJk瑿݀A0av!HSLG8*ŋ&khբ+pE^)4h>hu1c;!Hυa߽ٚ#rQ#٧:w2F0WA:;oQ'[9a gυct6Qm6.3*~,t)=ڢw0l*He )ߥ)0mSJ(E 5ywѧ'ݎ2{$ZdT8K!~?|v:X1Xԛ{ %uhahѱ7NS2lmp),[WSxF8 ,;CrH$Qk{`"*AСFxDv|9"w6qD5PBI8ZUSsC~[FFUQt;uE\WᜄD)9ihU8rWVucٷW9ZsmAtk_ b#דаLP'c?b杆-i5*@vOV9 o\" ~,*/:qGХ/2dr-$!-O.\ - 7,ߘ`&m{p&|W* ^d * 66VVczҥpvvF [t7spEc9X`1| "&|SWڨӨ]XPgu@ 1૛S( (-(M(m(bbbdei>؂%Qxٹ9g_Y#w+J冝~-l]s WH\.y\lW%zpŦ!ߠj/곓 >jtp.,q!." FѨ(RέZlh+Ui {&-wfo4]kvtH@xגu$;<`FOPVmԭq_Hg]GeQg &p/Q}Ùg$^ku!KcnB0׊|O2TT x*ȣIq6cqv0DkaabŊĉظqǴ1Ʋ[w}qi"=]ױVnW¼kPLbPpowrx-K+w@lp,sjDʉzP>Ti*"X=3Tow" Rsw'-2/Pc.NpWsQw3!=#IHt"x6^ANj~Onx݁W,ºځ6=_Ԩ=jHM'd񗜜TZwY-JA{f5rm 㥯bXFC mjb@XTl{w l9=gC"(;3͔V'#g|'|ۣŠT#¹ .^@F"p ZmG? Wo/zBp5 : ¯ k-=Cf="? Eׄ -4/Yr< LNJ̳ƤZً/^+;PMG=-8q$N<~A:kX"UŇą 8~h &BcU騊hH]JլDhx /Y^;Q{ tFz}~W3Jp,_Ɲ`{mZwۯKזX;vxqзo_<+Љ0@2kxK-2_@m49#,jb"ĉ?A`I{fm.Έ>ee@S^H=/444225+SʤDQ#XOn$#N)I>HMG0ۜզ6gQK"7"QU4l2O_asbul8yNFR H쨠F(c,(➭; -q#Z7⍩+:s%b~Ssl튃׹3EZ~'Oq+8 __@ҷ^z1YU_%rAKs|8\-s؆툜D<41MkUZׯG/_>1ƲLQ7#$Ge=*ճه74*;FqfR%R2_⑩f.b m: }=O' +~6ϽJ4u醾-?\\nY_bϦ\l+/???׳9Uls6F֢)|RjON4 ރCOгu4 xy4â/rm3j:ѿEBߺy&+AkuѰYSL'R:IaI}%<# ħ FSKboçh2dn:y_@ƹa# igOw a/2)"ǘ}7@غBt;>'UIJ Fc)cC5TI*a]vD*4qx~8vt |Fmxyi|ij_“{eMħ'KBգz(}X2J' _Uប,[t}6 ҏ8"SGvbx"o s[GqHSD0/`歏۔J[^_~5k:wlXBP#XBˡpY,bUZ 9"!#>LCT_CiBiCiqBQu jqyW6Qj>@#OP(UhE@ 'bJ}z'>@B9q"f~gN/",W4x|XO6P'#1(Qlj93N<GuXp,;böf0qڒcbXw8~_ƶU*#m,a "?&rzrɡ#GA_Vw pك~71lj *2bb[d ({svCZx4~}ܹscĉ3Ʋ AݚHLLTT(|߳Ihm['W;g|ރK/X?_r 45.@B" 3t ZZ]P\Vпr<&;kX +N&Pqm'nG6å+pA@eV1C`ﯖnn Gsq(`!X>i0k",B/Nǭеl4V8w015e/NO@1}Do=7Ư?@^7+^\3DPjW{`'xر{)w'd;X+kŗ(c8q.tX3wMEcp~Y=kCYQݨ%;WZ>:%Ѫӑdm4ĉ`D@R\,RTD9?>;CZ|W[6tpWҵ+tqnZ[D)}`gtEq#H>C4 ߴ52'-qjZzrb_&J54wꄾ#|5i,1.yt2# 4C-T/#)IET rK-Cѵw >BTĚ$DlA٢`7wB}0za`Fx7Zb\t#ѡ8bb@ع#E/EWwvHUY雉:Uatf.(Gb6Ð"hP|׾3:|=r^М8x_::u@^l>ߖ4J6FYY% ,sMv~nx4SE[ǰr9;ڵ{Bh4)#JkW6/[E^1=ѹStl ŷ?5dzKХX֍IIHQjj/F}#q8"!)E:- Qq HWSyy/hssѻ8gY܈׿w(f&$d|~HyY kV}}*\— hyzV?ETNC|r C VĄDyXNKARrAJiySz0I؊V;8]'NOjWOww~Px(ojG `aܮPO֣K*9Da?Ӧ"20npRB0v⹭u*Ą;mE(ȴ]8lDc -c"T?@0D(6|`"K'd\,1zRS8gbr3 u@DEVptuENQۍIaNn9`Ȁ?߬HBdD8x\_#!M8yyUԉ>ƴc=G%CQqrN=p\㎢uh_< Ud}뺘Uf 8*1Ue*s| w1^?S,]_?(Eܐ!_4ۊ`QzS>;D~`L2qӒT+vmJ`_66]1."EU18T/a!6k)r>:x^6+Υf:-RRPI nZ0>*7G / dćjW(fg؈1{'G`DT,YfP'>x*९J^ˢFA7ҽ SBiLߠHAc^4Rf0nJBNqO`cwA؃ ysᩌl!-K vWqc1cG7'/6V?KX+qK>zVV,e1c1n}t/c1c168e1c1-qc1c,[‗1c1X/c1cl^c1cқ?K/c1c15ECf1cw{>Qa[W'$"O.ÒG+?0E 0,x +R!LLܦ~}Z'ϜGq:xCGO!,2sz'ض{?. vlg@ް 䃍aǸzRRR*Q~Z~. f-D<>z\(_^rQpqv2oP=+#"y̗烜sP\t ɩ)pǂB08%cZiR?ûtF1k?Ŷ_a6w &Za] 6l~ŋ@m>zq<5 < 5H.ZV6Bdc>+*4 8YTS [k/]AL@`X!48!"p۲'N7<  mdM!ǐ;74L#8soŝ{ģv}JǚF1,DM߁JRc7m~s^TȠ>!1I;rGfse7 wƪ[1`x\zð6ؼcߺkxF88('3jgws)1ieHJz? x% [SbEпw畽Cv{^6RE-ĿJRV"?tɋC6IOO3J/,G+]&ap#Q|ÒW^ ͔018OԘbCUvbj-(^mmEk#u [a?Dţ"1D= "/SYx0|h5/Ѕ3cј9a$׮ߋ".Qk8H~դ!L/qun- k;'Z/QٴW¸1wʏ04[fʪ}GT}h!zdvsV{()*ƶ6"Hoavs K-( P՛r{qM~:ckcous lؾ[n;tXYfbE $/]ŤًsqΕv'jOqUXX뇘F߱j6Q>ϕQH|HtL,, Qcʖ*.LU)Q=_ԬZIU<~#@ss\!y~\qؗt!E>!utPR@ Y <=C]|lh9C|yyy8{P^YbI.?| Ӯ}Gp羯6Mޏ>k8^k~Myޯ{`x#O9sx풒1Y\/\EUpu<~?)B"QJ","V408䆝XUݺ{;DLqˬW|oBO})9n'΀'"oqH:Yq -B7E k}*ɳq@XW@E䫺 W;?}>Vo&??2duqq8,1:Ij (1ZR霳AgQ'%%cՆ혵p=pD%Boɲr~Q~!(eS\qZr%e tf[Tb9YkA~qV`5q:QK1Y=aBDh-1A.l;,mY=If@*$ Әtڷ󦍕e% 7o#A,Zo?H48^1cHy:$$,c'Avd+htL&+ΓFBB<&s2-;:/'AX$3"I0z$@OE'`źr]qqPPPG6'W=/Q)P cGO_OFe'{ń>vF]"0${g/R~硴4\rCZ0"805XbX^QF2w1;)`yj\[ϘF}p#S/(խD~ly\U}.WaZv&L57]zڗf̓ -k̔CթMBDEzSd`iFH J9~΢Թ@N.Ao}leEc@5PpOYpX.`سMZt$yPiV,MC}1K)hMܼu_%akk%۟9o!ۭnىwʑ9Kȇ@}R@~QSH!q,#jp9sEY|StIE v]oiٸM6|W7ܗР^M9塬(͓;aɋR9c%Ap޴qX ƈtN5B=֋U nn?r;Cyc5Hqɬce=pyΤ1CDI’2fLLX4k"J,!c'2rs= #*ˢ34Zc艨WkEyOC׀&ݘ4f(ծ.{^?6>kCh}Q\q@B>u{R`\lYj5Je[)Ёo<+]\d[p~T,[Woܖ먇T2CZ} QP8˞"CW\^,#c"(R8~)[&z8 Aْ vaee}#U+ʊ*]'L(0 :"h}4:5ERKAVSȩ{mUe9ITEV mm/O7Y-gN^US/9 VfPS0L(eOԭ2ѸA=':իJ/Q9mk,P[*si FTRI68)TDиN 6O4Đz:;t~2nܰש)CJ:7viR!A~7˵";;9`PXa|ո؇zѓTT1GFƘ@q ]A6F $hY #͛5z`\/C53Qʋmǔ;yrBN/OVt}dCl$b , g.^ #_˕ >eMA7ΎhI-)Q 6Q^#V'GtS#ͻ0O+šzbҵ{RE(CꊠZ a&Η M /2j`435yEf{ʸW,gj,չ,ڴJel}kccH=:} Q/?y(W%gԠDI`)q|Y[2_W7З=hL,@t|wq}H%}զ!I Iv &. qg4 -ޗѹP=y"{Ut=WKnF#ƣD(^STo ]*1V'B3 3!l6~3t-U6𶲲~pWȊ/ӭ\N\x ;}+[XiخRMReg1z%}kԼ#_&rgLSGgfTUqR"J{TS%eh8Ҵz{2 c?ubܥ+}QøbchƲhOd/\hU(#)Ƽ1+}EOT(E&ҜSmx (K{Y˯ej~.!7T23W*;:lRy6zz]_?EUkU"*Wv*#/w罛X,~4f+~(gBR?#lZكEraҟ1B{(LMLEJ;P_E*"'JOK#Q֖-Ky8}>r~V~ȗڴ@cѢco=z",hX(BF^du/3 r'?/)T0?nR#[y7bI2KcQv]Qӽcklڱ>Rލ]j(|aOeEaժCPP*Kevڄ5^ RgtW)Fg;翛{bhf.mBz L9\Ho,+)"F~iJ>9s7Qc}wwk'q_cO@#x\WCVd/$>s@e Sp''sAYN߆G"J;[,}{z,6Z 0UHؚ15]?**XFAa4zUylL7k_u/P$KfOFsh$RMTeG(]l̔!K20įCcLJ_vKx=Y&thύa| G?}_4«q:8t{{z f©QoFLi.6~OcC}FF f.?hך%p>θis k"A؟ i] "7>7?n=$&ZSUr?k Yߺy kzݥ ?mXR8_y$gȑOm%/k7lٓ]#9-[v=vѿOgG-eP&P梌2W_D ZLibA`9;8bϖUi? C؈,J#HzP9lLj3go}$oqp:\&˩.%*Ot EfػCךT*[ ^*2W:azx&$'S"~h;i9]7Eǽ1ȥ1lgc\޲2%5Ff46eunB-5,._![gXQSoE&W믝z%YYArْq(An+ۉIrRQ"E .JU8s{{k>o=]&J3@\0C0r:ka}We=ʙ^ƞVj)JJIybD'%*`Kh]k z ?Ri jIL.'SprvBzZ,.Y-3ϘLή?Y{ElbfYG|ݮ'zL'Q؛ѵ?4SMÅh@H8LC u:& $Ut%vH1t8=;˩\8|씬.(*M]@mtreJ`ݖ_mrQѡ].޷1&⤔,ua|/;~OVfem6Ϳee|׵:3Ȟ!8TRѵJGhy@_9)ޛZ|ݳƉ Pd\zA7:"'^mh&κ5`[Zk[Ze9* &ҩUro°~'3ʼ(Ns)d?/7SO'k0p9i)kſ ւߐDе&y]oK{L*Oneߛ8iffc6syBZVTUa899RT()Rc~Q2fOFZGY!fmcm8,L t"jU 1Nk*>U>o8 ;K4]?MFLT|C< :o:Q2[wOgr=CeUl4 qHrVF?BN`t0Q3MF^k) х}E {>@.K:[dY@G+q,ѝL{\;];4?[&/zGiй&<coރG1@e•Q³Y2'|Lyz8"(m.o8Qө|K =G*7((5^2)*wi9Y6&1n'DvN(AW}R,hlnNUFسiOB|q/ڡ_M(5t ]KLPX9eHS M $o56Qt?]|7BCZCTi$ndKVGH#`hFt ~ǘJBe"Bn;9sx{Nddh4Q&h$ h>(GO<=hTː:䈨<(JH)RvQ$n=)/}LgiFDEry^t. y}e#TIiYyIlsфVJKXfBy(wxm=3/:yݻt!E1MNF!ˇ x5jyZ|($sə&^ܹr)5j(Z6Dl)b1cmQ@AcmBeT Z/G,ZW7Z##>6OáP0-,@=tn n/0j9IKː4qOc1 DZz>tMM>ôK@сLfepD7nEJr* ̇ eJ Ͼy=@zZ:rʁ+0c1ѥg.\^9 m'"$, (SNJ^c1c95$c1c12c1˖8e1c1-qc1c,[‗1c1X/c1cl^c1c1c1Ʋ%xc1ceK2c1˖8e1c1-qc1c,[‗1c1X/c1cl^c1c1c1Ʋ%xc1ceK2c1˖8e1c1-qc1c,[RM*㒠25TX[^xD$?|(C dZ(_TGm35aw(Vs؛ij(W::SK-y=<`ijX -RaacsSn#e,@1>g3:(E:q21űWZ M7B˖R.'6Y_|B"lʼnYi3x V̛ Դ4aee)ko޹ Lu֨u08iTP\Tn S?CS#)% ަN<"ljPVAx-}VlhagʔEIkcKFr7CX{7'ꐒBOۣv*tܶDJ*ߠge`NUW_!e!s?CKV0W߱P].Kw=3]r0"ҡNKG:-EŜT >NPZ: *9,"ؿ?\hp?ۨ)ҩ>= &&LoŒw6leXw\F)"NV%r+R%Lb(S0 z;̡EJEEPZ vR  e<`ӉVSs{-XHTѼ𰳀V/lU#ە( k<11I?3;;_|r|\֯o*~|b]*i8Rѧ;30fx~7/7F+VprA>v$9V 1_.Re@ f1dFwƱ k,Q:IZSZZְֶuuJ=+vccLeaU|ӲϜ5} |CﻢϬxnX,\޷ Cs};n]A@pA,zwB+q?ely,_/eUѭ_wi9 I~ǯoACalbX\l\Z:I$1EGmn*L5Hn+Dtu񀏇>(Ow>^n"DxdV F :Gn Cu$9͠HQN8x(Wʔ(o3%4Vѹ}PLI)$'|)mMLɇppANlQ=7;+ ˗?C{tW\i|{S'4mᗭJ ETNS!oم"STir]1"oB8.Цi -eebGO4 ~`ܢ| a_HUrNl#VXD*:Й0F針A`#c-ˆ?~432U*Ma&xB~ Z$% I:p]&l͆Y)(tM7rʁ9sN#g}zT<0U_5ZQ”d|h|{ v>ѹ@t%iXy\ab*GP(ʠ퀁Ѿ%mdټfY׀4&'TE:&!نc2:^>?0w(9toc G&L<]1a X6.cwÆ (\0"""d|g+W6licdl4JDrqKáNԜvS< /Z\0 rݠCt^}-yѪ{g+ E\EW+n.Dh 8HSW i ChP6 瀓9Ti"-(P~~!7=<Q`U"J^RV"HWNOIJI|oB,,a)j S; 7S'v[\P:ޒ.4LCdBI7"== Ds$jTG1W%ú_eIG0 e,k1I0<`n݊e˖a@rvݻ|~(Q҈<޳g s% ulʵ .c S< k\GuFc?=hEi̢xl{Gs ^܏5hծrZ" Z ڄ'~PvC)ۻ0xsp1CzmjzEi|sv6R,ԨZ*2mr|>;)9QrF5]\D` wx{@MBpP8RL\7 T1LIBTLD Pеȗ=ȝ 0J (T8Eps|țN(4{BǑP;vpsBrc܉Kx'WN\cpFl{!Vhr5GFe,"r pj2u!_o qRAT1%N_Ey:~?rEl\ q)SE ¢M# ?:L"eY E=f0lS8*V*Wpe Nk<?&ZAGcYBG]ƍeVeRqơL207im?NǑЙX³P!T+[!(ܸpWbYHQT+W΢x.ؕFJye >.hQMuzx' AwqhnV Er$N] A uUF+0)X s#%Ѡnmg uj NQ*ᦧ^?Cf"5 ĭanf&pkS;V&HMJDll<S`jww+d!6+^1H/~kU`e8uBp+P24)p&`YMlatEepO#cvFUs[Yrֶ2")1yʡz/3"Ǒ5[iX^|}# :U*ZQԨaիW&Dܼ~j 'cY x\Z v 2431M\UJ/ZvBHXğz&&aȁ%@X>U =SoMVCPb}*'Q*=_^0:hR[^۫ }a4-GӵsM2FVՊ>Ìrc:zvG ]ggg,\ݿIVVtwG svtԊz)hq@WEI*-hhB*E|9?R? ȀdUj<4dnAV2 )M|v^4vc1|^fw"c&ZYYa޼y(U\>JNKKYqj0[x{zԔgb1cff. DcǎE52c1c]z]ftܹsp.rc1c}zo=Գ;gXZZT?)Strc1c1>ܹs O<ȑ# *ٳgsc1c *G#MI,c1ce<*:_hu:_QX)MxCޑg~CXr&nL1c1$XYcpӠ9:;*p "R9e1cei VVe-\yCPBoHj6o x9c1c1-qc1c,[‗1c1XwēV1c1Ʋ6j%giȨMZ4yTc1c쿃 c1ceK2c1˖8e1c1-qc1c,[‗1c1X/c1cl^c1c?hC`IENDB`backintime-1.5.2/common/doc-dev/2_to_weblate_03.png000066400000000000000000001053261465446530500220370ustar00rootroot00000000000000PNG  IHDRLtsRGBgAMA a pHYsMMgkIDATx^`Gwq!k u}}K[F]+--Nsgn$$vggw3AAݢ~AA IAD0  +еڡ  BGhY03Άa`5_GGΈ0+ $ nv ?LHNN3jiGm>]Yh/zNcKFxggX$``t9 nrc?]יAAa&;)L3 97mF#!Ί48kq`չH4/0hƎBbKL0Ϋ/*}p7=7Fӌ>sݬrt||DGތpgڛ  ; #xT\{ 6a5EE]8 ?<9FsнK<|aѰ:'̶0D٠ x`olUNG(Ds:`SDHDL{ 8+ϣ2x{qp6@n󣱮rx,å眆?wd}rcuaTsThEGgB Cx\hpʖ  3!!dXltHk`ZI4FMةg* ~vԵxduEVV6-%2 v!2%c72#F5`MLG\+P`\}Y>T#%ƀa6=6ϟ69#hys!`0ѰisLQ݆k VJو滯hY7v ʵ||ٗXW bT&AAد WTշ?ĸ>۟|Az\{+&M 5% ɉk"v3;4T惽 F;.x}^848=0ٷ?nƳ_/D , í7>MNc{[jÒ}>o|3 5W~Aj<4./|0 EpbFc>{eqU`q]$ Xz 6cт(5e8kQf GՇE?;{ .U4G~o}YW>~['  ,l)*ōw?f"-5 sȨ0tFלLY7/[R[Zޒ mvAi?ѻo7L[bmA;yaɼp#N©g^ Gm}#E /Mɦr2N>Pxɸ~q> h*]o~}'^`< nf"mz|n&\z xwpI}i2:}CAa$Cd4ɗ #%Sc%Mp XvFynJٻ4~6K]ocH 5<{rqƉ}TWs/ .OpF!gGue=H|~Vz.Hg ?%=AM35bᅗ^oBiGSjDB)#%P–^ਭFqIh|$%Fp5gdN  .7TQS_&;~2?2 6)QJ;e*L(p9o<{7/ A8m1EJ Ñq82HKqbDx,<c"ncH:*wT=b͕" (Ax`~xHh$%1еq AaE$|t=;@,Y /f1q(.}6 x/0ob &Q { 1;MF,bG^sD5uM$^i Fl\KoC„lETAӑ; \poǪM0x>SiƂ,6dQI=5 g92xkf}`^I'ЮAOKct*TW7?}~ :t |>69mLY%/%~|.m.B9 :2VT/u\V~K6չ@Ye)B6)n\}qy8g"܋D%&G'? z3*j=ga_ 7;6 Գ/]sG߂ᆣURHbf+JD8 $vbqG1w7_ EjJ2Frxw3Oj ɵݯ[J u :!Uu0Gfd\^Rx &vbG&#F@nёa8PWM~"b ylRsbcpdfyMJCMXi!AmVTֺH?6 9-F?`'>S$Se֑(Pi#1!D Nᡷ  51H}>ix8|OBbm|QAoѠS Ӡ'/>i"ű1_zϥ 4=V{䆁7vD0y.BM"?ZD,  nCymLs7&x';]]g) ¾ ɍR娵{s{Ʀ?`&'!'6]ekvFXT3AAw*/_~?~O ˋ괳{i⇵vs`ͲYrS;Bi5of  hU0fu=0s0%GjbljkנrN'{Ml܂T6(3./bqQԑ`Vg(۴6#F3"%[5SAAV?X1~xњI pZGr&m܇d?LW/BD } L{6~=OoҙPkŜcڪJxI??gk +l@.%' /u% І&[dEUCR|$}#+܌}sQk^. &WyO: G#7~L.1}@@ /+<q.srS$Sﬞq3]>v>ू  ) &סMMhh!>xm|nh|PQ]|o >H`=piD~A7}1 |򧽉 ~#L䲗WWpAALZLDKW>>so~a Dt <^j$ #1O+o_73~fEDx23`0}WSr_2cο&Z'7<HL5{g\ s?Ef$ DX0]z됿/bccd8$^1{oNr״&`z|+cɲPbI&ƫ/èҮ3:͒8 x18s-VGG}Wuf%v_}1_z7Ps-  X9.,_ フI|D_SfAá]~tz**lj\~WɿѸ9X螇KVhW9lk H3!fۄpە~E) qb=X]o?矺C8OWg7'矏H)S.` û+Q/~2_S(/McnKM#1Ĕ׎O+^{ &y^{3qҍbp/C?>eι /. m<>z7\yf|,xLz t8EO m8Sqewbš*edL8Ѹaܯʌݘ8碳1  ?pɬlVk6Nbur`'-Q~fN:~6MP@+WsW.>~fs"oǣCïėãߒ D194OS8u<;˃w>r~GL\ >jnw<-"ao7F jWb 2\[u,Ȏ.Ɩzi~u2:%ǝ4OWj^uLtj֕QSpw6_Faa}0$ׄe _C sv p-taiH>UeC}VuNA%F!n["4QxyMm-f#./ofq{{Q:Q0F :Rt?,ޚ3W 1d7ǃvzx@Rߓq'`x<8-h DIx'oQ "FNxHŨl? fo×#A|W[DupKߍ5+'?~8XF81z@,&nzme7|B|ͷxuڥ'eˊ ¾B$4]6znx o_1'?Fq:M0NXV7܆L4vn'MU><0yZ\5t+R? K3pAp4 I_kD-zc1y#K Ԭ gºzztOÜKEɸ\&AA1 Ȧv'zO28n)6~>?d5 |wSb® ➬typ쑇n]sv?7puڀMttO[oE|TDS>]q#65%ED#6*PXX\UXz:f{F<`˚p%B^#̃ "̈#LڊY’CM~/[M>rkZ (!6Dj+$G7"U NXb-*MDhkcu3YHEmwn^ޤ^M"iUkQZCdR*˄EEQ GWDz%PT`MA)]zh!Eᶗc޲}$gGm@f oqݸ0ulpj,$9$\\CB>x]:L!s,_~=R1b`^ ϕr=<\p)˵+NLv\ՅxDPAl)*!-5u5J`a1[0#mL~+Vc%;)ϥLKDdd"ާs rȵi  (|  ^+-U05GkgOONڄXAQbQDD0 ۱Sm( ;XzeƎOA=x}~w-NnlpZ|̯ު  Bnw q;ZLLUu j`D/ 3:DG 6&&hiU01  h4G$AAȲ   IAD0  aAr\.zjNhb;˝kB[ó'8]zյ{ `0bh&#IAKq:(++S hWxT' BvoMkSS"##H DZmrnL&϶⡺qqq&CCCCa!yۖsm`qo{$*ԇ!UACAAbccӁuILLLFEEն7aL7+WD߾}5?fY3mŋcРAQpxΛBt]3ib'{R&͛ÇkG{4I˴mSZ> \u]­~%EUIAAh;=Axp?bpXĝP8{B[ӱiIpA0m}^{蕤t44MhlG퓂bkM: w=*굏yQ[U ણ'w*W*4u[P  :KL0̿5ySǕ8x᭧y%npOby )uxG{ޫW>4.A^ƍg']>/?r38$zèT.?yGq$r.?)W濿t3ο!8(|NzAe(ZˎhN6/ 6TgUG? >q]3z ǣnuKMj,8{aY7\c۟C\sA(MJ6`obϢg<z]sqt/k=\| ~ZZѰkWAҴq: ,ڸ5Vډڅn+6fw>ע%s?|$,j<{~ݤQ}/&{v LөrVsԡx$ltz<)9շrɴ*X>߮u#myn`6a@\\gB70݆.{}7\>v8pt0 O( 93ÐdnNυFvzca 5(*o^ŝwމ; ~'$\eXjʛtYT 5KϠPHH͎Fuuતvp@Dc[k@ԄhǡGOB7K%" '1Q6` Zޑ?qEs5KW #bo g¤srt &R>'V5t{}p:D 2vyt~rvʓn׆AaGAXrWr8qM[n=u;Nl~|7o=RZM+&Ck5}3H1ECZGJGԽ?o`嗀_~y8"qf| T4jo$$> G.y7{@#1jH6ZuԇZTUV ]#Gee5jkjP]]&I]ф_o}uYx186x5چ9o lKðé_~\XÐX YhE"㓑+@gCf^.L$tpa՜7q-oЙى 4$e )]%%R{NAbJGap5ѿ{/$"hDt K5'|3̞7pQmXRMUZ.Oκf gČ31g:v ڼpecyKHbD RQ[]0G!3+ 6?l܈:},$j`߈9 6 K6PSRF#vAk <DG -DԠo~jg$ׯ3X2:4o Oij/t̡ܶlk~Ͷ AA4s͏?~r[=7 K$rY,iBCp״iVy_f@iv8(<]i̮4=f6+2 81wZݟzR@$ͯu'?y0 Bd4`UۧZVf&@nTiw"%-E5m6z@LL4|^j!EzA rS!5+W[v҅\.TVV">.VkVҍs^ a-Av)^~Q٭]=9]lڴ{tH޼H iݝ}j@Y}]Fz+ tDdM6dk槍جhpŢEھW񩶏ۧ]Ӧ{L {}v~Z#h?:wB⣿U؅F?[?Bݠtؕ]c˽:VkL`8f=. UĒFX8ρ BzP&.[B0ƝHJ(eiL)`  ވ!>.^M;A4Te^Z$&&FV4?k ZLz|dBWdg#O|"$X.msOusi  17t=;KmEO1JD-fCGকs1_N_$0&њ,6 CpIuxZK!4_Xn𛎼ԟ&$l'Ag2`#ަd`FXqB &.(}_^©9S'K|sAaPo%i TFgOo5Swvp!U+)vOzz_|NAvC` .X y}:ջ#ad WPqF"{t 9  >&X.DQяn &dCt ۤ#CtrxF㞣InLnoqa9 $G=e@"` &PyF.$b=asy . BՅگ  Ly0i]jf60&=X:MK1SBk3]I HQ7k) Oϯ֊WPJi'4 EI( '#3CAAJDVzH'4a OnZR?,//׎AAh?|FOh%Y.tAm&!sO? uH%W͹? ωhv"xȥ;nccv$ ΐYvL}xm?s/Fr wP*.iQՂ`+Z a]3Oㅉ(ųcRRq7Fҟ  aj#zLe!w4՗cը]AonM (c9 } W܃1Dn<t=ankzK4F_wP:c|+o1xs_ǯvm-AA: tj~ /#B3@o4ЛMй*OSF_ Zb\i=8qQv-^b\2xWš.m /G|'P_0_}<ƛ7[pc)])ݍC=<vb1_;b1'@C0ꦗ_0yikwG?dfnܯGi܎`Bs?zg=)l K|++Q6^{7xbO>%xЮr$|ũ8duBAAҨÆP(~󿆾3._14g/ Y8w=E´O›ú'E Q:$xDN/u8IpKOuaq)G`Bwær]^CtCтpT5ɝ2>7_y 6l(@ 2]  NNEvn5] {`Vpc!|OXl9ذ~??R6fűQ2DKOFtx: = HjnëM<#mhCcpRQn6d|zd" &DjE$㢛nro d&9ꁵ*zU2 ]Eaxy+s 쌛pxU_~UH;a4 |>wЍ:5)Gռm)޵hI@W(a2}j3f2tkzB`uԫ]^Mp;Фo:T?~_x =651HS$At ϫ.юeӾE;i.<\ ۵;ՀFr31eDl"7+ #ιo4{)ðmwGa82+)]q֕wo>{9dI~_hAtw{Շߙ!؞43j9۫wT\$C+3H9ywZ@>#i/)"@h@= M6{3O=)=y M$,Ǵ `NAa1/ Hp  nWoeѻG }~ӧļ{ڢb#"6u2ޙ3O_+|:ϙWdDcP2Ap`Ƥ׿cܵ>8 s0m*LU>d"U?8;phN.SsPp&f..EJaSI?_f.C^r56/]*} [Mǜpcym$N,@}l#ѸwhJ6 ̩}\k 1L@Xu28*b5(߄~OKdžsC{#]'Mkk{h_܂WM. =04㿞B7ˁt DpO|A!H?cd̆z=:m<L3: %~=>:،=)1$v~er$0 5p뱩p3 K{qMڅ_ 5 E(l8-(-Xkd7Zm~=[5(0 ,B5GZ\+cea ׌#F"'nVMqxa$ѽbMm݂Qy3X&FEKbf£1hwT$'F³fBY(Gq+PVR’Zaٔvdtš,[Ppbw_|A!_[ѤG@I>@BI>/1Lɿo/`A3ԁ9LLaA# wY !D0y AAh7"p x7#AAڋ&AAVI0bvAA좇Idzy&XA +!=(:Ҥ{IA2%U@mrR  K<〫z=^TӅz]H`9]׬dA”)S_#7'0[H"C'aG @h'$VY` O&Մ  ߙE_-܂s;ď`I&C'vDϽKϧW獮RJKAҥKq7gC=W]%q$ zQ>x##Ek0' c喛!4_qG CCRzQFdFF; ƌyU2oבe Klٰu  5fMBNDaa!t邑I0y'=J2H u<$QR;B|>~.x]I*O/)+߅T 2" VR[SL  YXFlV7a}QjpڎE#>:]&Koi|1mO*?hϽLe{?`" XV܈F]Tмrn  _ ,XX@MQ^H=HM4 0`iI<ҦWvC#p xH$ѯv/_Ѿ_SОIApx0`   %,P4}¢G-J9JzW j% %½K |9AB &abÚaqDZ~ɜ{|;JcCAA M@aĽH%+0`bk$XB &Q!%h (3\/ &NbAA}iV+01jI2UI5@"jg &Yb}$:k>' ¿a ;ѴPbm(J mKVPPW/4i_ Xg Ւ۝  t0u(- ӵc HbedBʢ\k^ٕ|'?vQIl6$'&XUR? &> m|!]t:?3<<\<6B4?4 ur'x{Z=;&i7BvB B'~6bJO5V&rm#6֦v-L0ACSۀ1p̙=uuA[MGp ޗȯ|w0aE%_0o5Ofh^}Q3ͷݝWS _‹ f~T}4逨]:? Wma܍W6|Z,] 䣸^7v]#l&lܛdE!K:Rby1E # ztCVJ$\^ & #ذكn]D#cнWWd&E#5/&Gf쁞IHMOE՛%VK $&${ȎcA%%鹹葙3i%o7ǻeժU5jӐmFNn3PHrbˢx'0qnvۏbn1P\:f~h Ks U[`cY t8pIC)IKլ[~zypgRd Vh;VW߀+֠p F=la6lutvyl&l:hyZ,X*aE ])j3 -^'@e,\W[oAd 4aeXքȌ2cXz3\hDpKboBT:>S+/!]dDX1w Vn-#S6Т`a!ǣ=#f|fm%#"QռuWŗ_kAS߾(.bPp &,λ2<$Tc~i_nށW_̶)%w?Wi9+}O%^FZTv:*5in|9}{!'+={[N6VUcSazKQAjImi~W-:|n49=t OUMԶE^ΆӭzUFm^T<=pMЛ]ˉn=1_$Du45֣сHDPc^Uހk$Զ<7P 8x#p6x)8/WE' ,r0j=F]p §>ڠ\cu }:7/kWf` X ;LVJ-sqS03/LacHv4*ǘ?, =#{QhUs߾>➱t$EG@ Gbr .\_e,}ȽKp? tfw/ 8S/}IYif*dG\h4lb6##=]RQ vv  k'i6PxB)/#V>q޼ue#8 o xe|cȦs 1I~M" ;GbKɫYKyܫ]T#Bd0Qmc4RGڷAa_­K%%KWڍƲB,[ /"2[1V1Ŋyn ږĆr뫰trlƆK1{M50[Pe#,GOHRɝ|kۋ(LE MnK_T kcYVxuӴgs- ѣGw̙31mڴmی30i$Ck0 . $w^쏒5dN_?=c<N?أ0#ގ MH;œn="*MCbK&S&D#Udr俅}c >=^-0pT$ VWrąR`$-Ɖ ==Iq625$&#)J &= )XעـiXu(KNW44!9V   KxMpU58Q^YZTo/SMN=:j.t]_WEvNa:/mTOv⍛vS }<_j(mnWUY7`uPZ?嵡=yKh"h4V+>hDFX=77  \0 9.1wF%a8H2#"`>(vbpJ@|cpLXTnunMg GZbwAZ T(q9k 8a05Q0%\8|vzt4;$DZr "bѵN12CvEKjz 5=KCr959 $ 9, YݻOlYNDR6Ƌ tj-8_)cZQl2#L[V`=76W\DGE#" ^i?>}Ө,:PQyM?_רQBf&,*~lp`%+5 X @iCRw}i`Z;LƂp]h4@rZ'o ˍ0}.:LFGi6;:1Xr 6)V{ BPre}v4U$*X$ԒP2O&)mDKgB)`~U=~JZv??~{0ӽD57z.7|+|>/l6+zsý.Nfx0b`l`˃JF+Vס*|ڸRElؽĽj-ԸrYZA8/釅0gߞ@<5{EZD3 %[^O8LlMyt? /H06{qxTYE(atr3SFig:)aj(br $g ' wXH>!p^r7O2NG1*~Kɳ3vq\nqJ aX,orw_}=J3mNz $훌A3a{ PZa px <2Нjjۯc|>~F쓬zlnH["ppy4=/Mp@T54$s0:MNܡďi w/N pН5pġ8!m^C}+wx9%jQq ^K|hpvwU86)Fԑw] ANXIZx J-Qt)>ŽYŹ-/[?sx;Xr]VzJ OOܩ6-x| SEv]^sBłC~qr}>O!g١Eiv mh(;F!{Ey U_6{lFǡqbΓo/Nn ݚROsj- 'xX$%7)J;~GPJ!Qkr *)u1$: 1Dw˶:rHP".0<ق5FV[$3`6ꐐdCMaq:+"%̳cP.w-2Yɔ7&'g0EH92I55FJi10RSCM~呿8+ FǪ]MFu4oص#ri >MoBc . TEFř+&+PxiP#Mf({|̫#GЯCbrov;tNF2g劖?+ٌ4a7TOs ;{XU(N/+ FVT9Qtw1ɮzX4VBRNO큯SqE63ZM4K5`J0Sx9}"XEqBq_|vN;osye+3.TV~2=K속6.C&򻽍7 :*)Cԉ=ǃk~q뼆Q= eP iB?$,S XP 4;|9;_f{I$+_6iXs~pQvǑ뤦v>| -azp#cC5G;5LEuxpQqݟ('~d8 =p1N Nhܺy /}(>e-||MmwV,z.Kw=_ޙ 4W='.W܄LF!y1R-fj<8)[;(lAD>w *}0gpz`wtz}xk8_jjVkŸɫ1K]h] ziv2]9_Ρ X}'yb+ Oi8dkr@/CV<)7O1gޏ9G`O#bϸ+1?OTy\sdp5{GK`N a%e\PlxqQj ]oud7 =1i: MTpzr A:Ucy^)*0S%,ɥ,OEQ9̼vTU}ƒkRZC FS _5+ 7:1'r#ŕf#]?o".᡺\#o5y]Soe4xt[F䷗?*!|o{8) R6u,m5;w#]nWS:<ڞI:ҳYBAq\;7JOʛӈz RЃho9h21Pr'WȽ9$ɿtMwJgOJ~jkrp:Xt Θk](m8dr ay4~=8(Ni^ ٝByitH* bʣmd :>/rK5NP-2DNg=9T''-퍛`P THT53iyh(+T`^ 4V3BK“yY5聁] ЛuVO N9#Ҷb̥۾@sq#r:*X}ňW㾋1C3F]pLo`ë?0ph6=wB7o?9>#A=-z?J|pqwbe~.FAbi=?ClJyAE-I8`H&^<: Vk}PޝQ /tG1@vpz 3QooQ?Ҵ.DcƆs-'.@ECc#ji~ZkpsxzREMzSewJ&ŕy O5)Up$D~$ZL>o#\hrk$١b_TCv,p[UMgRqx3Dtyr.FO}Rb4\CCGBWMwkBMߩQ-pR+ujɏjr/'u~foxD9khv?Yt\θD˶VG%!(O, ,TxFpͯ\S)YUPy,PM$ raNs+aL"-@e݊$b|]}5'}e=Q'ewtbbȡP+I'Uﬖ,Fxe?,v{4ZP~Ksp7=C9ٔՔW_:U] d}tNyF@Gaު8v+mPmbL@{u`iA"2{c˟aԏp8=Nu${5iN @R~ r{#" \@On]o^7U<׫ʨVOpzՌ`јg;wKNwEf;xkpCbo&apzEUggco`j5D wѸK^27<:q,4;ϖ㷷nm߮GTJO2`v%js?l>{jjFcmaUa$#~!߹KW}/Zz2Jqj^ׯU|3z=Ax8j$o،nC ^>x薇:NnjZ+*nNEr25@)q$#t(5NzJ|J9$bHxDi9?FIBKQDQ9c*)HC*DM\q=}L&{;tJa Hr7hˢI22_Je%'ZDJ qՍUI> &γyD%u聄W*o)_Uq T(/Mj`1OZ,$ hM|$Cr1qiuaFāpS⚓!JJqx `6p)|iQ<1H<8|amq< D&.]k_޸71xD,zbJwܼ|P]3;nӃ(M*N!UMGa{TgeA9Rl~8 {onr871>a{CNv>_zyxpgB[oJVc#%v3y&Hb!j[֢y80>k&NFy %%xkbz| x~s58xUsURgKc`ZltZ ]c0YxB2|' }Ǐ݅0SjHi XX:M(:o;7`wS? }; .57 Wݪ>bQq Oqy@cDN'#6CX%z Ӈ!jp=#{1Tn*#{͟/ Gе[ lxTfbţ߉k:^;Uc}>?6"i':WcџCnҋqoM` {tXPW^prDDW#{i/SyoI1,D.ntNDǜM\W܌Z̸GVCgR8#pߡ˸ᎤF?YK&hjE/A: ](t+l5=鸟6qtAK)TL\OK\x6m"( JV+)Wy<5}DJu)HScNZ8o9s_L roRm@rj_Aϫ{^7r+pDtByvt2C?,P,}֌{WX|wd iZ2 Q )}ͼxsas%t?r~ ɤ䞩At~tO SCpFEɔyT8B&?RŏsHiMvC{iOawp /mOTl„Iէ/t햾:Lnϧ ڇ؜%7cU((rLa|!V%+؋@n{J6㋉ae&[ )9=1g2j++t;3Oy[֗ !32X_gBuR£sbXTP8%uVZEX&z8ū0ir^ C#F/X&lɊ֢t~_ u^80Ixď( <߲fk8bI_lx"[_欃C=ҹ"̜aŠ`2T6GEVΚsWaUV,GI8r`UUX|%9ʉvߴ阸hvұyR+1~i֖7sq,58$Jk5O&jL:򓇷X@f :rew DnD45H H~Q= !y?S+;?𛙜U>V*clWt=95!eZLa曜ǂHXt<ɜԽ]As)m9^ҜՐ=~ Mk.tQKt--+UMs Ғ.p8Йj*@]u5nX67G!JJefjpjY74ōb&~M~. DĘ(/QktWXpZdsTF@S<. ZSlwujD :ZmJ7*H(-xIAx]vS:U k(n)) e[)|X13@"QG m^c#XW&٣y+{6nW8ÓSsZH/9n20 j>oKICM*Plqzk12?^Q$cw pZ!1n8(9-8'yȄ\T)3ڸ2Frvp9dҞwiwBI$VrT }>p5//'Oefklj{sF.7.2yN;E~sR~9BFƪw_ EM(tg8ܨ'T"j+  j r:8'48 ^]I 'C% g7V9 VeڶS\>*! ؍X`mU5]EWB<_[PL< [0H \%q7PS|rGdi{^R?;t?kpb؃DڪVX+Ǖ 4O9U":#c7>ڧ_VA.tݔ-t=ۥ}֪[{ǗWׅ}T(.y%!E}Cqfv:VMM$(}K}:$f9 u[IX+L/!T@q[9 i!%6!IUI0=:Wu\8NGa?/ J.\#%?f[ҦҐm魙lΟq:wi9#t]G`=en`y^Pq#`C<7]JAÍ!Í(>L6^x56C2f M {U!ktT,1Box褽h} ~j3s`2K<_EXּSIɍ.{v)7o<$=aDGXLiҚ ; noW{.Lg. ;;K[C]C6$$;x~9}sx,$VobᡬL~Mb'>{ ƯO+ Is鰳?<a_>f|B,8;BB3lvg/[[G_JE,)ۯ 55"dCqGhmתɍ+na?*w~"_e  wǥ3E3bӆ W<\;(~nY4Q+{҈?M:o\| HMI U̲XWCeAm]= q=sx pђⵟ¢*x#gjf)úPeDJuRwvo? Nb%R)?y-!Li#Ȍ 15IO!\߅oƵw)ᯥٔ|Y騩So ^e[1kNBHKBߺ>`b_ Pi3bcp/*<&{uy > Qp.2{j2չ;wYdbɊk&s ۓ<6 Jӊqۃ$PaK<I|/|eE@LVb/ǟ}¢bDGGb6G.}J0xBPݻGwgY8ѽkv^{*-OY3'7n\yut;/+'Ns΋LָǑZz@ۄᚗkwo%ڀ`F裱{=|FR/34Y'] =i` ㉿[1aoX%ύZM[fkN&CFCZrbH uOexhoܘr:sT9J O$xNQ ro@un9w Ɓ&c) topp /6^ bg,}6qdY|?ÂIA>|vAA{ IAD0  &AAV$  "AAZA  B+`AAhL  -?O9{xIENDB`backintime-1.5.2/common/doc-dev/2_weblate_setup_01.png000066400000000000000000004133711465446530500225550ustar00rootroot00000000000000PNG  IHDRE#sRGBgAMA a pHYsMMgIDATx^`ƟK.WB`WT@ibAA!"*Ҥ7{{O}w5ONۙ@ Į7} ~u7rs =ސ̮@ @ ,@ Y @ 3 D@ @ g@f@ @ ΀@ @ !@ @ 8B4 @ @ph@  ,@ Y @ 3 D@ @ g@f@ @ ΀@ @ !@ @ 8hv80ٜ~@ @  W6fUF+oѥ,tϱofNJ I h;SyJGDy5؆ޡ6Cp&YZ~( ?s6HmR@ 3!: T:VchN=dW,J"[ . $',z}UgA ;½АX.5ɐU#âL(r]oqR;݇o̷L(W@ Qʡ>9#zai5~ͱ@l@ NǮ7} ~u7rs =ސ̮¾$.nJT[^R*P[^J(>ЄB ,No05sD"O u4HrhfaJn1c@ >JXRph@&Z98l@K^dea64q\:Eu6Tl45F+Jjms٭%a<ŽH{mpr\qU&_AIz):Ԛ3x{mqI:L9\<X(lB.Jqn8(8p&ggbC)[]_CH9XoPRge[xOS"6_tQd=ܗl qa؄r:e{ͣƤwrNpOvTQRF埌 ?5_JeY&T(\<8Jh*R+<+).&d$"{ &U5Z 0voǷӦc]GrI۷-q} -v[W+#&6s伬k\f6z9[Yqm Y>ԅ9t)7 3g\ҹ-@l3,5C;a|FH-J#22tF"ЛjI( AÂ}R0r)޲QKy!,IbլК ,GH;uP`VRҐҝ<$ f@'nZwq fߗ,>;SCi㣒!\ߠPkٗ^%:{2:}wg*:`~5K/,,K@ ;Z@N/՟/\7TFv!;mI LVHo9n݆7&H6[4y_vZjqj2Q՟;%Y&0N1o0[w"o;J۬Y,+dI_>GncNu(mHXQthIt}kp! hI},Z 53@_\ieP%, sUУ%8s%'9ҟzzBܼe0fޝUc[aVX7%~-!5]_<@[g-g<~a~`5K5*!ko5r:Ɔq8<Q@;qi)6/ APi"ǀtu0MqBqV+w&aaaDP}D;{=6Ppw&YTGOn9Q 4ԘMZVF\™)xT*5R3zTWW#""BqRITXr%ooo`QNK<.8-cIkiXSJ=LYw$X njV)*L=FV4??sn4w*UCK%7mR9j+ݠ(TZPE䩠 -MؔM/h6m(صUmgZR:z̊: J\a#6^)F+]a2zV9<*a'U~Ҫiթ1L "| ĻZ`ah';x+8μD"SԠ=Glbt a"|{ zcf_JzNE2D^>^-/(^?9ڇ`(8V-µH SKtazw_exs}1ĸ\)ea6qw^C\\K&@ :*ix ֭Cnn${聶mۢYf$a4%1b00sLVjށD?7N)s+3*,^@9g6i$r9$q:[)mN0/BVlάaaN`PY`DkWCΡ^j)*i!u^Z}OT%ǶbCm`]*^&`fx$Qbl|'Su0L56ou$`\ u$$LBx|ZtAƸ1Xxw"f^,Y|̨Ûw&amzd,nP@6pIǤuxsu&n0Lhwy#HNKl$+,(2#6C$k,9-lA>t>_'&uYYtӥSLVUrSTSFVzϡ<$稟W ?ܟKRXƠӡ`R|PVeYikGbpa0WZ(vt08hE+iK( < 2#5҉+'~Kq*$kt":^w(IF#=rBrCa2=\_x$G5ݣ>i0O5ѻrM-Lݛ20l~(rp]\\L*K]eO.C&N|qeUf_#(v쾎ʻrsV?RxF dطo_iH2*\.RH/ ذaVZ_~߿?z-4L&I8߬,,ZJQt;bXphf⹉\E-2ȥZvz<3 "<₍GQiGbpmCv>6E̅VtTG@YN 5QڔywCXz lLI;L2W^8Ϧ@4R`u/5Q<Ј48ELХ>ϼRva(<ppf ouGp5jxbD{,RL_.όr  *=j,V5\{Ë=Kmj-mN;m'GG*T#Rxz{ r,2hVPQYM|TPвy Ò! |O^Fc;n>QY™=k/Ry}36Y0rryݯv EtQAi*ϘS$? ,bqKY03\U=Tɥw}eeeNiѢ.Fv0h <ѲeKHCٗ.]*K!!\ܳ}}`)XumF ߃Vhf<=B p<=Pm;6yZ&NW>5:%,sjҽYd_JNI+C`[-xYC>J;z!"툉'jJ 9OȮw;j`#x̧@ކXtks\Kͫ5h#Vo &52fPf}m yUC,+C; fFS-XCneQJi":7$}Чw4]jLkW@)6XBP!-F2D_"ll&ǾZlW[ pȂk9W*3b/wEKz6t#muqe2KyzvUO oItHO+t}U-Ȃ (LV`rd1nD 0a\<<Cs+0X[, .L&%#E:f_g,e>+Jy1.)ۊfpv|uxx 0߲Bl.7̄u~e j0Cz2)8x^Lr`ݡb) g6")Uonv7\;}cCPvS(RT,<`{Q-T(ZWY2{S3\K]hfjd_m/ ZBC|%,ǖUzT- Kb` r(ca-cT_<nxo,=8Dz #[UU Ou5$#_4*++%uEeNDuTTn& <_=BCCQ^^. @l ׍Eu%?;T5U"[lrTD_;@nȯm .t{8;@xc$;/P55x/0s})03$|"%TI6oΟJr>32 *roqERȟ*Q~G:ZWߧ]2Ls~Rg_x+pלߥ7ט@ܢTIzFߊR<0O/H]SpdILO{YQ + /ӓq׼\hAa͌KOb K%#Nc)xhiF-F)%*?֙m.S3q=Hw=Bܣ dࡴeY?//m z`r|O&F8!?c4 6I@a9N=(l9Ƅ+ ayiy] u×)# tx(OJ0N{ƽO _-KPVO0A]NNac?M#YrfY~XEྮ)xlEQaEQ<al s1tN]'bYxetCC-ޝ~#e*!9 »؜;y4,t 7CMm_  /)_-a}~LCgGO1:6ԗgu+vxlq&^Y**Uh‰z dSu`Gd”#xѻn>&E (??]GCGJ0_d\ޕrXq1^5_n)ks,M! qog-jfSl3JDxqo 21ic!^w0й',S^|rKi /Tgqz4[PDhSHBOxl| .,ٔ21|^~T%Uk = #|J Fzn&(o R bxdܺHj3Â9!@I74Sz$4g!e繱:T=mp ,4[n-͍~饗/o裏"$$?\u=aNLLĒ%Kرc5k`ŊoF# y] 'W `|E/K8ۚᤧN><ބ;lF$ZNߣZX4y<ք$[tu1Gj cG7k̏Ъѝҿ=tU8jU 1[* riҔ@>^J4`~`|rO^jEeU#%iu==BNۮDɣh)|JK7ep|6< D:ǟS@Eǃ rOR0AS騒I/7&/譴S<1Qh\6aƂ&4Z`#EZ&-kMrn3Y/F"UR ")qV-K, z/\0xm]'k/.EʌZ*)eѓSi|Z󾑎|pv#!5]ow-T*)Q[yvWHql/_x^PK18 ,c99^ F):8wxDf im5xLsHU6aQuMPXeC.oN!R3cQC֏' 措l=j7? L/cZ`}1&=rTI'   enKv[ /p/WQaAXL7n¸n_Ejpn I:N Oh {5VKeTfUHg96҃{(#p.?&[x!ҁrJP&PZ _4oP%϶ SZ{ <0~y";آruN[C |: :JK~|4OWPl{v{[ nr9C t{9G>P]D3;Fɀ֧EY?? >?Ok]jB @1Gp h__g\T"W$f8.Pցax+=Q57ݱ}t,ƶ#g=5+I8&ZUI_5l4S>!FW/OJ$]qpa^ĵ-#1}R29:Pܕ5!**;m+JPp.9S"䔞˳0qm먓7i:BFcby G<|.{ӭzE xNs Ȕ9]Aq/}A|[yS;PJ #ߕ@ܿU7d/-Q=Ki u8{rl0:R% ;!͕/ƫs]G{>^%5ppÐ@ hz믿W^yM8Qs=x7q/1j^9܋_~qu /ozk>T7_n ǨQ^򫮺 wun$$$H=_ ?|}o4"(ꓤAZʽ p9|,nJLz'(Dz/"<|EGCGh1co4T o=WZDWTj=C\>؟[Tr+ȡ8=r}  IuqbC>@4DMڎ Nڒq#ZK*t  %ޮnܯW2 :닑[p[LZLgW {sj)ҽ fsG%}:N*2< -m֬YX~,yqKY| UTyG;<_G獏q2d3999X4󭪤i1^[{y«#bsZ@mWkL@\ JR"s -YXlد6qLA6<0eVz' A'+RoaóG]OeٲuZ $SpXymN5S-ces1;5R",rY5* p@d˓1h7bu!"c -ľ8|$Hs?a,,Q{ !m1Щ9p9)Omi8Pg+=&f˔ 1 Sw=z#OQY@clYuT]IyNMfIy?BVRmF]:JQQi%HRdT*"@$DF|PIWy:+QI?A# 0:XTE EȠ.ma䮌^ˢ}Њ^씌"UF:ОK pL.@9(q9x2 mmM^PWi҂|gئ])tizcHNy .K6VWB*l̨lJĖL# Z;zO&\A%nd 8tOXyv~^zPXE3n?Cсޙ”r| :ES6*D?$\18%e%(T8Z͜_h۔B%ۄ:("CDʂ9e5Ȣ_ށ FO8s:#fb*}> K]1,|B@WTSzWP~ρՒ@ pn nȚu^^-:ɑez56TaO17ɰ jA-WL!d[.\Q~vU!J=B@j CJ+BG:[5w,V;%")v λP1MehN:s ?C~1OI~ aÐZdQx4' H)3S8C:~A8pKJ»0kdKy^n!nDWK 51X2u]bd\K|w](bD K₴?Rx%CN/8Hع}YF1V@  |JrO%>n<6G5ÝI>xf|E=^zdSވ^-K*?1s[ΆjS-9Liq|ͱlP4l/6%$sSo9Ei/c65 b0<col)<_y|FV5;2=';꾶{7'6lUq"Z'`шh_#mTL["lvh0kQؒT͈ʊ:/gsj aE$b(h]LeEoL0EcXk= '-v*#n勞aDxa` +jJ,Ȥw$e> \UF"G%_#oX{e0ny5,]AeA]qW~ ?g;ouc|{?\9Ƿ] k]qt3n+cl@OLv)hr*f\T(/]Wohţ[ͫc08޹h!ps*wwe`7WcXlb-` Tmە\* ޔ U26 t#]IbJR| 6јԂH,.NŰ.!Ӂ :H5ŸܸS*<C 4%X8VܛxE 8^o"pmMM W/Tbo=Ζzyx97j*iyټ6{v_q 矡%^s<-xss B݊MkSCM7yʠC7V!2ߎW` mԼj\:2|Ҍ*d\Gi?7ףOniǯ5L(WipMzQy-']vB[,: I"7Om|a), t 0`ޡW+UaPWKкU "ש1P-d@);Ij+É fMƵ0s0w @PILҜ?M !2УW1J3X١ЧzSӻLFLqi=PlXq1pe/n-c 쭵Ԁsl<=AQd#}Er,,utnOe76 `x N8Y-& .q )yhPi1F--޸ҏ##N:L{%"gѭ*9(Jo?vS8j1 䃫)|g{0QIý<*3(#ID^r.w<_G;=Ŋ}}/0d{XGuF(݆Z;Г;쨨uHTkKzg9 i7eu tIBeb2kTv@o C^]*)-~D~Qi)X e(+'wz3vkt{Ue s裏pG]ΫwFnAqQrWxgFQPY2w.^  :uh7YPHn:\yR/Lgc0!E3C6$&RNۗ(N( f/UnE ;hhR $ڌ.$~$x%4!jsZ5iN.v7D YV~HcLXWR5&cx6T5-]BuEÂdͬj'#F|.D KY%~RU͵PU ,Irddn2ɑ1%CQf9ZCv-pyŕuX~ @GiY.ZX ą>H"lVabl/"C@68/A{_RI `gf ~1IsפiByv?OB &s^idr$~lR/cg+r=|/fx*}O+2 2 @赡y`m|F>純* Wܨ«JS*~*h:x3Oq:S). T\}G{T>IB4d#Os;Sq*q㯟w*=8, P,>a^: _ޭR*;ò(GjPE嘟7vC#?>lTj ?,~! ,=X6(4jl>r4(7#Xjͼf]Uiq/hf=¹_/Ue}ɷmۆŋK"?qqqX^xjPVk]2'쥳ofg:yD;$xĐCxGpk4gnt'i7šޔcj,R›~7pe):?򊼍cKbg8z75 ߧEM %r}F,_vW2􎸜 βhK;(-}ҦIJ8x.*@6+U sWbPJr+/ /h,Ͽ%p捅cS̅ MO*j-(.)zyh67"DFFJZrfD&u5΍%J?g8φ-HK=na™W*͸9& k8f7p!,Xg@ Y΋aJ!>N̟g1;r iƣfxiĎi#* 7tzk@` ÿ)/.hf^m֌_v-C(Oj@(+-%m..J;܃%yq^g{‘9NT%lHt12i߇'Uc+TI%G@p \=@MJ#J'Gڹ7^B7zX k<˛*(gcBo}p!V-Y\t :| /^p(V$*tDr`?TXdU{=jJlGpܜ~8.!QH'TzX3̆Xo;UdȬV`MBߥFc!@ i#pȔPMSⅽG9bήDY\ v:+8.hfx{.yY4$Z:8;7r^ @ 4-h#hf"νVyT}Aaìyq^ԙ?Hٍ9ǹYf@ @ hKQ1w:#hss@ @ 4,@ @ @ !@ @ 8B4 @ 4Clbm3.Ah@ƌMl繹+Cf@ Omb|c\Cf@ >n!Bٓƌ&F&g#Cf@ 6nC7!&x4, B4 @ ti(OֿlMlb 2=3B8_'%=b1 D]N >ͽv23 ,)i"Lؕ:K ų\.),(jzAnvQ#@ .-<{pN|u\<'.ghv99kPj+͂]G4YoZ$Y4\GdB87id^ %߆}C&c|x4B gN/u%ME8Z|ձ!h3@͢iS@ KlNKݦJ6JÂX:- !#g,u㴢 ȩа¦s1wCgscf2*@ %Aá<gfd"eKmV:*,V`| fl&<2 ~pgYvo7,9߂D4[6e~s4nQL"CZ'5V@ [4iy e{D]BeQYY)TJ)?7)irS wEfv.UJ 2<m[%S6ж%"#}vCv^VTfX~kL =%M2+NɌ2 @ 'n|& А XV8lםBuU%*k3RE wÄaM3ЇiN t}H)Pal؄E֡t&LnD5X+:W2lapN 逜R@νJ9Tr_4ޜ~M Gø!wg#K}Aₕ58z⤴B\pP ڵH /1; ]{vN!5=S>_ʷL ōCqoY. dk,3Ac}xa* ]@ \ xlzsߑ2gCq2_^rk~ jG56-6BBqVkumC0!exu:gr5*-dS!Q8d"u=Z*͋Bpmߜ-09.To<ݗ o.cU5&咠rAhՆԌLFx{}$輼si8p(KK ?iXPR*)!IL&\1HLi>V͛e'aPV-y_0sؔ,܅ȪIv@oI Ǎ v jA傼E%$˥֔^ݺB:wp}LIIኡ]VwZyӖ+V{!P/~x`JfG~ɪs9tb.ÇQ$疻z`BCر  eNf&c#8Q(ퟆLYKqa=kq!9t%Ս, G|~EVz^Ch٩gkt#i j%/<:)%s 94rqcC@ ?_r Z/u^9TAKf* tБ;0溜<QzA筃!r#{,ʕh AZnQ78ư}FnmjmtߋjJ 3lp9u+rJZIR=G p3.>聾# xvs*9.U}\pZj8T:)لVwOG5},FW/ⱁKTqp:5j"Ň aאZG:rzQ:㗏_R,|vvR:LtPr-T^p^r2˙bs#-h(^#\ nEuM b"$ VE. CCH8*&PeF|\fLfyDZ9Q㝗^ƛ?Ɔ-`*S#br@ \xߞys wwQ.ߍO?قآ WkD*|`ߏb͖#D!f~V@@Gד @qïl :l{eD 62~ۊ@Bh@˵[lzDbBv̅jVxEu f1˗oA"ߦ +dtlWw-۰hjCX-"`): {V` G#.*ںl[mӡ NDTZ)+Kލ]9HEiͫApd4 iʠD6[/Up$->,Lض p=%|Biq %ذ rtH#6Ԕ=DÚV?D6{'? Mpr[T$m'҅Xa'e6[itV=jB߿ Ď#yGFֆ s6AaHوb48'?G-?a֒ 2X܀#a骵~( fIn,Îߖ"菰@o)=e tZٶP,wD UXbm>k=6q pi8.- 2J:C4յ-o͓-+Sҥʧ}ۖP*8|r $-&=3eT #.&J$;7"(0iSa=bv.E:% 1x暖t uxQ3z4鍏AO.}{f᳟w\Bo0SU#cM nj#oBB"L_.??D; {bĠ(kBOcL_&LBq1ZtWsNQHUchuHu'v;:T ' 'yLh}^51'0_s;N~n]-سn2̝hE ^Q}[4+4$rɻ}0~ܵ.ُk@hl$ ?'Ű{|uE KrN,~ <-產"m'2n~o}h&acWoaC^̜e W,XaTp-v(}Q{OpFEq ? ǕqYCKpU48,? c&6!!3} :B8_cJxqlOBmY*m[00ދ6̳h =z#|1 HCqy9Nn?l)D\>håQ7~?r`9X]}ZAPBU} Sz߮+Ghy sYwH!UJ(ʏ⓷ǤoWyG7aႅ8p֊Cؾi5Qs*p3I*U [מxPE",Q^BRK퍊-o [OlD^A2~=s x5UR Wm0UN;+7 -?6._ uW+1,@GZLxc!j4hץ;4wG7!G?m*:.:^}WNQPȔP;OwjoZ9  'A]<4>yʌ=~D;ag0mS)8{+֤֠shb@^pԘhcn 9wxa/̽ǷsnE //7/;xFNkC~ @kf)Sµf/Vb7/Bt>P"v1)+sQLs(̚ } ͳfҵא Q|{)_˯aIۂ\[& h?)Sy?cDL1c%x& =ڝqZ|l%|2 ;>70o0m } o6 'WK1Fm-,ȫqƻxgO4>6} =G_oox[31W|j%KLzg^{}~5tiEsy)Dzz7eCE+,1OI*7}{tC^{}s f ?>cDaQ4zfr..CmHt&/ݎdv]ķ=H 珆i8ͳpauqŭ!9W6 ,3onh9x.jڥk1!h{@ D~6G6㩡; !ԚDFQ!o2|xo(fl6 p =`́ё{~VwĠH̗aŌ+`7!]Fa璽%Jp&C*q~Pzr͔=c- -J2Qh'jGHd7@& @+d3I2pDn\kR^HȢN7ݫ2¤S3O^ONGiE);JQ~蟠D593ygIOjn<[4{{Bh 5$$8HZ d^ztmsh^saRqd76mU` XIdƗWƽ7G.-O'83n4|uw,fM|UΝ *" *JJ!z,,@EY&(A7{iטCѺWGzT# UV[@ _dܹe2oݎ5k`m(tt6f[ FJO=L^K"{>"+XI3d$L$-:ʞ=ѭ{tKng`;%LA%3%T:TYT24ÀN>c**$o/;HߟB^B= K\ ͨu+ S/\KPp Vx|ʸ.HGw"v QgMĕgskЮC_D]cL~]E}@ <'|n:{ }>ޙ&>&"6+UG#80'rQP@u:" R܂j E6墲wsRc7l ć`\s9w wwވ-(2>Ve[ȽѾM4Nxd7h:GWг^o3YR'h瓍yOOFy[1Ъ5=g=G*$v~ [_ǴmC1ʹ! |etmV1!%3W89ŝN9%(1J+sxbIQh;PJR!XQ攦F o'dXlF?a^Y|ujP>{KO }z|p8 Xu5q3"8CD_W1dԇĜ^+`3A:ߎldz2pCbI53y"@n!p&m+E{<\|/<:A}t$0wo:^be0 > J/Y;GsqDv/D{C@ǠG7Q0w؝Y;cm("ek1k>ٕcE*8Y6!!fї̔8wz֏( O{|rŚ%DX֙͢a&fXj5R fW!2 T[J_$[[BWv;d![_! #Y5v_*Xt^&$vjҎ]AwUi9$vJCHnHSnɃ{qdA"Хo$RUػy3҂/ǘ>Q,(H%!$*2+zoyx3> }{",EGDtH"!_ʎ"߶ɨ49 B>}"@)-ߛ6&t/˔v Ƕm%'9wR }\׸U~3F=2 6^kcI -E˻!L-_F@Qmw#v ]K Cx=rr5Ž]{_%K]H3t q~J)TZ-nAU[Pӥ?fq@0#9 .]/끄`^eE~`*0d^1- Wnw "BON;"*2B}.*.3 '?gia!́hBsщ͛In@ ' 3mBv^XICr=% &lGrYjY`tl:+@4M,h\+{˦/['G7w3J kP>|k:ֆk!/-.h*$".&jg;S๷S6Ҿ@ @ hpݪB< Y+ȋS8:E>@ ivS\R,c^Z-!,(r @ giCٕ @N J[ F-ZF^˟4iGD3÷-)-GVnʝs xܶ #"4QQNe@ EfO{S7o wNDžŭ]!1 HH DXuDd9)|wǺ ׾@,@a_vW`S:($J-]MvP̝W EkEUOa٬py눠凰]_xp@ pz_I/87P]]p)*vᣏb3/!6oVeE˚C:Ɩ~>B;$ڷaJzDz̬X!Qzt5:`Ԁӱb_q4bןqHNr9sSs| 2WlA<㖬kAnŴo/^!ϟte,B=VnOGhn m؆Ӿ+\_csrI$tFE0>X5j恔ж,c|;o 5Ịp7Y{|(f^qh _;}[,ۑ ExKk]H۹;r퇝?gbZ4 ΠzaU y T\(jka'KJ)%)itJ%.3&hyaQX[NyBH\@ drv=ddmvy㐑fIX;0[.5benP뻸ir &_sth!gFa带7@Fu̞nC7u$ tH۰uuʍⰕѠ>P_n)ߏuB6!PE\Jvіc5o:='qxӽmkg>R!k5Ə+&NØnNL;:VOc;a?>Njw+KѼMG0: amchں~` nIg>W6(MYf_4ӕaM4 /6I+o5;;p||f־&lx{)ǟĢr\1'ad$<3K>w+Б63|@;3'gD(Nb8{ҽǂ%o+J׌ǵo`=iط|o~vg흶%JwEY{~D| G5>?fk.8%;x)xzGg]sa$A-8\+km?sjj95Z: =;ģ̮B(ivNwT|8|It=yOW`5U`pw 휜~G"Nq`qpQ%2ns9 HPi%w ;h6 0PʹI Nfpv Sig~V%5 X O` tyu 0bC8S|j1Зc/L[8)OF˦cExFL!>zv,{߼叢^xOo KpKoG(Sg Շ0.Qr7/aƄiv^_ak}k* }xpx|%g{|T pDZ3%]$\DC%fo%1wo!QHR맇pӇWKn[o[;•H Ȅ6wHE3ꐝmAL̩^պߞE1$+oGaďGP ɉDILLa2V”zw;"|6FLO@ґߟSk1{l+WsbL:y̓oѮ맸ŘtS{X 8c_<C8*Ob^b~ fܥk؏'Fw5(snnsQ7K4ow''#ij;arY\uzL:cx]Xw~ei$gX46?r"N7|fWKD?Dq7,5zYgp˛1txJ#Wc2{h+<ʳMNdDH}WD3#eR$#ds={Y1#͈Tz`-T  zܿvq2I=_a/>ieWHR N[mQrJ03rsWY/5F3a5k=+QBb0h0îarwŢl&dHפa?z2p$MݷvhD00X0艞a4bܫyf`6T!T]-D!We!DHθ 8-݃Jn)lFN /A )è҆iZ0j`kiI(Fs-"~ EVL[03ʶ`o5wbl#mԊ+=Z/+bDd*6<1=zS03^h@}/P ^guGnS ^q#Hظn3^037>Uc֒L͊ f&3䊧UmAhE[CRI2vrfƓ.̨u֯5Պjׁ.{WcN;//9o\YWR{Zk@ IQnK+aSbonvۇBɅE(iCpVϏH9|az4k\vE̴QQwwˆc1o jɔOw4뱟cvl0UA֌)EtЈsѢS8gB-1k܋}_8-VEv$^q˼VgCB@C1dJgy}xo=0[UPW SO@/H'N /xDcvvor hD2YY8)3.zj\\&C9Mf9=N< tKPNRVh"uM0کP7 lfw+/@3[-0a]R>M{~ v$}w/2 ƢR 8K[4[&}@ %PH.6,^ɋ6`DE' ))s@AfK}Dv&lGݗ_s>i{0uB4GӪQJ/xY8~ 8H_x`ny| īWŐlL aC= ?"~e 6۾zW $'^~|Oxc& : 34LJ,6PIО^"%SapVqtaFT7줖+ ]E9?νRk*;lfXJ-f [)Tt'ryS`yxKR}gK`/p{3^A\6[!W sgaK7Ĥ`қoON6|W(;# L9vEG:"!S0!FEg``{.|ZAnNZ"~e$\qy2/:Dze+qh Bⅿ?g f<=oѡ$ZU!H\>E,u&:OǿUQ ݏWTxKȎckӞR{l|3gkHk2kZ%:;-'"ʥ %8*}`<?%G΅۶BdIL^ nǺ] "4TdĄ>Rz+: 4T+.myМi6q8}:Y [,"8nepՠF-FFLg, ` 3wMIsJv-i8 ŘLdR\"U'VO٨oXGв()٥m'#ѣR4K 5(1ީh1´I 6 M19NaB ;B9)qd<~8WG(j =UĽwO[PY^.-v n~q$E  Ǝ}WCS^үc5) UU0X~ ⻉bч~ +N<\x!W\R}1 xzl&w)Q=} 1 OY-vP^Q KD 17jy^ {]_^z]+gx:%&>> ooC jPi<=sVw]l> |u/Sv}_vPVUWD[FyµϴQIx{9N|>uB hT^e2&ק@ }f "G&)MvUN}rZ eBtrup"ΞLª Y+YU(hG_jXh2 zk D]n<8@\ qؚ,d啡$bNfeL0'.GQa͚:f" }4ڶ\ w[; Ov?[D EfF [@TDn [r ?5܊\dgAfлiEΉJBg* 1-$wwOHZ.K[R(..llu|Mv7B;+{^{:tСC:t3e¨.KuB':tСC:tv5>ySھ%NuСC:ta_:t|If:tСC:ta.:i֡C:tСC ^ڦ:i֡C:t|oQS:t:i֡C:t|ѬT':tС3NuСC_:(ҬC:t|I:tСC:tI:tСjb[ٜ67WB':tСC/7Qnh+ A>~աC&cNQY!"vOlD_x| ՆYhbXOY?S`4c6B0hKGWF;&3vާcQ|tlM`6t)'Y҉{o40vUQ(~W21KP!֯!8wbB*DP&=Hg^Nogف3x΁`=3>R#avd庵8$?_u&H>;ޛE7-[0ehapy1]rBdiLpgdȍUGt?)q+>ɢ)7JUퟠ!|gkg:t,MCkv}eq>nhrSUBmK8n{'Yr.(-aڕHHbܟ8-7-Y#~OɆU&؄({8s90oMA`߅rϮ;4[?i#z?N]MʽrTRSBI^\e.jK)) Oam\E§eyj0Gॱ϶> V0` v? ^l?6fۿ{>tLjIrsO&N:՜0ZGX)9<rF"0YLT^;/i|`+=oI ㋉N\banOy,:t|.`$iG ak`WSYw_lDkWJb8ţb=`\^f{+~763s$ N6h|akkFsug/,!vLmˀfh ♇XU\"ug!+onЇiq2&3¬SE~4#7k;Gr`;(DF~vWǶS;xbb}Noa푭` 9w6}Hפ vS#mXsHy,U:87Zqy94*:q9hqGo__WWًKBniGO&>1զ!m"A m/@}+&݆vLMtA>J3h91Ź7aGWԃo~N >{ֿv5f?s^[?Y:1C9VY[qa•i w}e%j0-wQD!.K.i=ȟd3B-6,JUѪp&M7p{*9꠻Qi:Zͷw/=4;Z/)b{,Uf,;\@ٿ᮹SI QpnZNzR2vQ[QeS2r{ܸ/!%VYO"xz6 2J8dW5RWI SB#rq]Zv%eQ 3^CIuHK&v9KY%}e}Z{h+?$<_LK{{$ EٲQ,Ʀ_Vfp 9 /$璴QHT3Bћ6&_fT[L@4LyPqcnPfU{H8Ȯթڱk`E|q;>fHڽ, D6ad!*!aT^Q~/$鸴~,bcU;SRX(yr;i}f;sRjTy@jpN9f]iW6I4%P6:N9?^}Ftb% y!O3С /Ai?K.V? %66VlԶSNk/TC#r)ҬtG/>WlKܹ*~a\ɻHSh;Yxo$U`G<'Z&dɨ7"8[4R5oCrKd`?͋2YC,&ELH͘DOEvo[wĹngʼnXmb09wr;rc֜-IL4cG2AtӴ?#Gn'NbԬ?3ˊ#0ͣ{+gaYL?-OU [b4YV[HfLPCڵxq ;㥎l4u%B}>T{EI#E_),O4FSbSΧs;ٜ]{V^!7]_i{t6Jqh=di'K8BټARARnqiؾM*A ]s`9=_' M@*N&m@p0fO(!Y):Ӟ>L>@ȳG)?FMҝ O aCm|cd4D2iTbCa!>L;Yڛ+h{+N+.G6J{ 8B6&)Sb,B/tF'/t$ F`U!ׄ0qp2q'*YںY[ELn7C2t&5&)gG1yt*6bk\{f.2 .2T&ȳXEj=)eS{a.>tg/&HiaJFDٵ,)["FMl~Ĩoð0@l"mDL s|VL+ݎ}؀5D]F:6Ɉkf N?u4[B?)u:u&'JqF_榇r?_5;1vgS$:R)Jكu_+ ޏs' yaqYI|olB`t}AtO(vƨg]5\DN 쭥l[̈́b#ݡ~`6bK9oVV?CU_ʡj䎭\vPB;g+ 9'vFI)r\H.5}4B1VeEbNgW׶`+uGv^ )ѣb`.&6\ep7 nji.bt9CJcz**FH =u'9UicA[jaظqv4SUŮ}X3br0d@EQx^?k7*yڻ_JQ_Bc{(%DY)r,~b#9@/d}Y.bt<[`u ,v3zLʳvGy̘cE:pz&Ex}M>j{2eT*f !>#5l1Hz<:W#,OtxSܽp)⼉xh"6h}t)kNwh1g:f:NF;%9sk5[mtycF X|xUf{p2҆cF $IJ(2V$qe꬇hQ!s0&0d{lHl5irxwc`؅a Ś$ǪHDvX_gB]ټ 0z"]Ŵ֯~T2wo(?=hbAR3m mO4|C ^`%TBlIoϞ{ŭ&r~z!qIQ hމ<\@e Jo ' 㚩_$l 5e1M|ih$<{䔹 )6Σx+Xl'ryq53\I^D0HZYaz8rx~BFK۳iʎ/姙0oqo{rx浗(N=v ޾WןVn[?i"+Xg? fٜ:OX1k˟Aĕk LN,=]J#I ԪVAG !BH'Xgz=rZlHbƠd ӗ@jgMf( yKY˓K|0d~6e4 k%&U٬߳ 鞈Ilv;ԞEUAW,TCqeVX֠??[kHK՞,\xdWm5Dij>[Jػ cdJʽܳ/RV0x=FJlhY|g 4@:Q~\X;FR]JYĉ<ʫ[<,]̈́%mJxVm%}jm)/gGu"8-5=A8O72s0,>26_ɪp"Y®1J?yyW*2%Վo"]=o`L"bjĄiHTvn5Kv:k; Nao lȱ3mLԀϦ̭$'EzHHNqǰC>: sA@ۡ}_A-ײwT i.LCØǚ\ߧ⬴gHx V93wpZ<&Bs}Ozk2֖J+ys7yG;ܫ-t:S~"p 9-ziUe \ yq~Wf8cHퟢ-eU\wXw{\A8]s;^G`ɮĞ3XN4ZB*Z9U^I恻̈xf/;\9V>@!- #P,JBJrf-~6*ep٫F_Ŕ\~P--x;@Uܓǿ'F5Zj Qd'ғt?ƊonC~wOpĽ;r^]yvffzxôꃯ36jwtR 5,LtnardC:Q[0[s'6?IyhB:rym!|{w6fumFīFWʗ 7[z#NșUpbCvFMhq%eœ~]rWn$6ȀFo7b39|Htn{8+0ZȨA쇍E{[{v8y߸>X f ,nKc!o<";ƢP?ן%m/ht]/J|X6_0w& @l(ɫS,tpƌSg'j>g"W&0wK^#7{,U6^c"Z+^W zp6`~_aI^=6|_g~&,֥\̌}s{P5TjM^! ⩯ҖW}?sJ$<ԍ6 Mתb2iNɤVH:a֡>Wy*b$A4lR8N68]b;[hub՗Mɣ5YYJo%ai3%ZP_ =r)il=%1cj((̠Uuh˰4F:D-eѣsҰ r o]=AHv+g/݀y;NK둆%eYP3--9Zg!tJ'qbp ',]67_Y S`&'K=*Fvqwo^Nl)Iz7Q-T}F3[$N!)fuqV ҷp.KSM8_uԶ1e`94y  )iu8 EG ӁJDFFI=.RG}+4KZnG[8=u.NjSU8㨪]Q{Q/i:O+WПF!?8n9*o%I=^]nC{im(8Vs>iz}>p#'jS_+u`%{?bOrm9Yu B}_P4'yyȮh eV{BWGCKd,a>CU#GS~(Щ)%S%EYCIk{վ;AuSJ-K9I}EH}P3vg d&Cږ>pji\od:/T]u dr3%tC:渾Ht7YȒB*Dԙ\>4s't}ԓqx]5ݷc/cdR(eYif:-::S+uܖL?HȲ˾p }}k>>d`34%Kڒ _19^J)9ͣK{s:pbIͫ X,-l`#gZ)bg\ڣSH NNbZimnq>Cަ (gcH_TmZ $K٫Chi l5RMx,/=BkEբ%lWcGযN&*'ƿ{ŞZڔdeWoblfOT}qH %qؔ 緐ڝ`br3jZ@vYɭo.?P#&GG2S~DPK{mfߩrRuP)q0{ I7Ֆ5 ym,0({3& *ĽU^tl^jZv(Q4n8vV8w\b_Ktץ;HWv]n|%NKvtT:|kFy+ݒ(-)~zv  7yw$5{(q[]MDDcap7Ploo=ƃo?_^;mn٦0 .)N'"Rq_j䖺Kjxk`yrEâ ,P.mZq(u2M:}86,^a)-9ĦM|O8'-N]fx$Rj"7Ījw!pnt$_FW1 QoT~(iȥҍXBbg$SH"tJ@r3եyzzyhb5"YUE?, qR9#yw=wOXɱ27o%"V,'4/-wfYLCWh (SFy-d^o=RGq?nMas8? )skX_s8^y'.#L5PWuv(1;Y~"mm'hڰvnI;9D!S+y9D$b=褲jir-v5+B~ EMqj:@(26;odobߡ$vT @옚M<Ų;lD_-l/ɡ^h}<$-qΪ'ZN[&TJ Gձ\i՜YIE"y])-}_.޵ԥ v)Q6D#ʬJۗ/߻@v:>?|b_?錤NK&Duud77%Apqo$3\F'H:{MkXn1SԮ/{_+?K"i{-⬞JC̀e~!.vwWU(oD F5a fԫfy̛tw\K~~T¬>$\Or:I,* uWʛ=`CsDBLi~qrfq@IU넎%ꀗY\?}VGWOg=1W9`tH>8N*-ygs◗O w P91}fp ~ ReW̷n&\3inasO}l- y"s5CS?޻Ly3~~=o:Dw,`)4p6OkIso OQV:փµ}Tz.|`.B/.󥠖~]3/ĺҕ]4cu;~+'ȫʥ Pdci$3oΞD\/@5Q d^JN46H$p9P}o*ͶhRC ]J|ʙZ1e*jT'w &Sʮ:ELrs>冹qӬ]0q׌"};" s/皯xnWNx&ymR5ػsP}X#=]1!V!s-V1NR~EPj6iLg楻7Vܯ+|1XE0T\ɊՌx_|9t IzpJVim#DjJ;ZElZ~(p[yTww]BլRzRi8ڬDUUX[j[P"I= 6ITX5H算___]lI꯲aPV)[MSG$uO3Kꎲo~;~"|̈́E& !fl0<b0"ɮǨjJ`Hp53&siL1 C2ax&AЀ7uy]9GS1 QPF~_ zpZK`y482UyT$$j^Pc8z#F3}DɫX&ϠXkm:C5.9I# 4@Bt|2 d7;%UԛIJ8qi)31|j1`!Ϊ+A/@k|O)4^Be)Z$ݴUPYP jVSBdzhuBS:b܅z>`8HuA"yZ`m(xea%1! K%N3QfX.W[n]{;o~ny~b}w~*FL#E".TjZʘ7{1eb>ݼM_C^y/gw1*Vn:))$Fk>j1DVԟ]TZFnΦ'z"qU/ U9ս@dRqn)).k uo5p4 ?=p3 K?E!}e ۥt _?|Qjl+p~W>m_kvHEh6H35:*|HSDD*qGhzD(Jf.2q kˤ`ɰM<&kZ!0o'',QF~*9ۂ[XsZ*8qZ/#g}ZV#!q:v )a$ P˧#5[ ּ̾0P֠^G9,kS E5ujJAb&x%ȭ q>˫#W|X_5[@x+<&r*q9=u` Ae!̞]l5VK0"Vl]HwO53b.;NS%Y[YK\Fqo.c1n:@I2j_ 1u)fZlZ`n4O8C.g|RxN6JMfLRdlWUfHB\_.FO8W~]Ϫ (y&QCSM-!I5G-i);ŎSw~p7p݄ TdNR#gcHM}kՌ{ &BM4S NdO`qص{X(P}iYe*3Bn8k1;BjesMiav1/M \LT,AgvsVgtJ%6-"g16Υ)bx$3 q `s{(Ԝy;(*>uxҙ ompa,d+0t*̃tǀ!̝+mgxVQAma?YhCYs:y6nKj rQЃ;~JEt:tΠn8[ws>% (MMމ?%%ۗ&;g t eϡӻ(ot6}`:U p#y}=h{{.%'޳opȅ;~߹:_c̶ -l}w)#'10 WJldVo/l5_yݔ9D?L*9C谦 B< ՜9rfĕ} w3e(BB2aV"_ymKh˳>ՙȩrk6钐Z=MpL*rCD&b[@r<5y{m)6-m+m4Y 6|5υXr, btkzWMݤ&YkT R7[iQv؈6)X3`2#3y}: =b"RN;C= C&үz9AӬs9SND - ٟ[%mQ@K%WK}$S{;å%:{Z|u6J"% m] wa>i"=k(BH[)>'(hViWN̫_wIRNfw}{~ۥ)vFh fJS68u|r*6+BN} +i©5[R{[IWG %,dG͎D&3,:_d !a`Xuj ]iNVY;~4Q6e(C\OKrvOBȐ\J;>ݣ9eq<G7H݇#Z& BCi85gOBx'-!KxiI Jj NZFsw'XTI6upɑYq 'r.'xFvbԭ?c79gټc#WD!n7YEqv6GuIK\/\,ݾdf mgx=ԫl+Xz 3Ar <>o䖝aѵ[H)UĢu 9=.5fF>8çNv:‘40 X*oG.k ~,F$f] i--E#-l޽9:ZC\4ׇ3q"EJv"7 +1߻;ב:N\βe6,suJ65_ q9EA 9nreѳ4"%z؍b^[زs8Y?TD1؃غ#q=9[) bRxuysG+_7hiK:]ܡ,ؿSX}`'-qW&h'hE'xcVrJNP:虷WsUZKؙ}REl76/ZOnadhQP3DE?䕵9Zx#{Ye#7s,y\>f(Fn%:vCzt7{x^1A)afsvaȔkWNݹ!3XBՊ gvd1gLؼ;eyQ"?Z ]bwHbFց?NvAv+h7Q^Wǐq2joutռ^~HfC|*bҭ%#$W\fpرk!sr9r|?kc̎2+Ǐ$R O]:v&m匊v׵ _AᵼfN?ɕK`0 ûj6m <網xvNdR!9;+TXbX B$Ɣ~.kCPc|l~$q._|3&]6Ia<-p$c#ܭdeql6V.كGld:# Ԅ֤qV}kvn=wE2|@է]q"vWOLZ4ڇGȗb݂vο~a^\~AMnνQI ٳkpx^m6(ܟ~1 '^n\eQzF46Vdz'/:irLPq( &>"AV+V|fb妭Y2kh*= +sE~OnݱeaqO$>!1s8?Rl͈2$&Mȫ%mHsV<8zjɐ1Џ1EfRԢ-aƒ܇)!Mg(ʼnSڛ2gee3VMۗq$F*|uECjBA+[?4ql۲ vRI^U#isrk c5]$c4HVTP g@xI[3P?}lP[\*V7YdW1Hǀ JK J&\ iH t/><;s)=@-5)*˧Xں"둢_Eg-B#*ih`{A+9]맇j&5Ho0Tfڠm9YB}z-$lP3R^5!l!uR [b,}UEVQ#~c-<=cAdX)VpRI VmD󪄬'(>Me{l"H (f)kVUbѣwJʖsRwS+~k-wJ?&į:;< ;1)M&uX[U˥ч1(>IPeS: bO 9{x>GtS:.{gć">(TE~IuG8p !Dk`@]~"HMMr E\KC>ջ!qIAnl{OeCDLl/$i 2{,ȦE5},3qVbɯ@TJ[Kx ItpMn. E v:c6vv^Q:#49Uh:-q69HOvu%ߎPXrkۯڃjToJ5qߔ0unv'V<̯E%Bd]=jumpA%NٍK52I;/M$=.JCXl=ԟ8/tcw[ln+O~\fu-IOjX^t?V1E]ܺ@+1(Ttój4?l|_ e+I40g?Z@[Ҡ䠚<&(HsDԬNA-nUx v;dvmkj,. ua=Wn/"g}b7maԭ _S';w-6":ɻ 'r-OtOtN v5hqWڰQV.F4, B5TTz~WQair* ~>oO8G((f4!tX٘dX´cip>/˳PJv갵ۯ.߻>ۖV ymvu|cZ)hxE4.% t[ zɴCs\@0Y}g`r8Lgq$g$aRڞ Kvɺ\\_]e5UOZ:u vRyNi_xk^Խ1uJy쥂~][{!K!GcH[̈́سh'Fk3ng+綳hAbG^ǼqK;! d*ohО8lpm?h<ӵm^2.a~RpC[_~ ݯqBn*އ׿70/A<]"SϪ (pf6(Pq\Vr΋Og%u3A^m 8Ki~1įRo۽vGT#T ~iePa%. `~~+L[l\jPݻR1CYJ 3ӬCj6*k>pPEم~2zz1ˬ 9mn U-ABT"2u QB;M}Y; _ez AD lu6nfe",̂;8i7_!gN!MAAXa1[q f E|;:iwHʇHqW{Pn[P@]ЕW©k6=_,YǗjQOiU%Վz+j;?GoS%ՕԹ][BO )& &7ƒLڬ IU%TT6![NեlIv\ꞎ/tҬj8PNtOC;]7/ ЍҲ@衦r jn-mJM}TK~o*BWg}Y[knTtChT%p=6v)u|af:tСCǗQWGtRC'C] h+)tn.V:i֡C:t|Q3.PGWH7]#:th7ewB_C:tR9Uj~K:(Q63:tСC)D;F-  3 & 6R &3V`f=҇>fI8/UVK`{]tۢC:>=}hdYeOq'obkNGg=S[xoaJi!唿m> fϺ؊07aSـj,Nv I JДPg6}+\[ɩ"4;uFN_Fdb׊K? eyv`Ɩ '67xc^ʚ_lL4aCTJ~OAt A_fC:>S| ;]}6lC_˞M֙f.EL6X?s9==ь8|,xh V'+Ɋ9?o#çhJǔf;-҆]TmâD{Wyusn?נb^{hOHS^iya҂@* {6 7xl?T)!p@Qjg2AN<-QӗRV3{O2vJ\e_cB̼E5 +0H v|a6٧m+tСC'g/w =_?7앻?on;'RܜAП߹fk?=Gcwr:\s]X|4 )j$e%ϼ$Z8m>- B Żw\DKe.7?-B+s-ㇿ~wb6n<)gݏ<#Ey7Pcz~ ig w}[wsmJ&>?~&R0>9A2}~: /i۸ywY%cZMd5Y쟼7$u'ȁߦ*݈Ὓ(r?ڕ&Cfe|Fϻu ĩgc0pJ(w5m7uСCOݿ4gv51`X<,#}߭Zđ $76_jRXj/ړpn.툋&^n":؊EȞJWR{l엚9S/NUϩ0j?%L]#y}"4vRiiypG-ft Xs .5R'^IȖ6Km_QeU0,!nנM^Z=]B v~\y B篟aqO]U,_~bO7!+A&T]]+B8ɧkQ5Iy?6M%ɗJ.$ԩJ-P똖{Z.zha.K8niG#`޽-;ۏc2VZӿ%MОV[lbRk_С-(B`U>+\ xf%jGg^#6Mu* @uNb|)}OKO=>[e"(ȿkDuصܚ<$ǍŮ~.*sxٱG6yâ6_o* uSP>%κtQ} >hc86}qpos\9[b1ĩr ]j]ڇ}RabS?+g5w.ۿ޳hzޮT5qq!M*mv׮l"2|/jJnݽ{(q|oC iv'URvS$=['u ySynyyUT۵tVwۏVKױ׆~s9-5c{q"/?5oϭL$ϫ>K~^u.&q}:t^AOFg8rja^JzMɳgRfZ] [ӻYqdjj RO=cөCdJՇWN{7Sp7iwKP'b c'!6̃[)*d)wZ Vtlۻ-NRX'1!`58M7=,vzwOtv3./,-'FEa8MB+ryxēNIX~ڊx9vl'Gl 0~`6LI-nlHDFJ6A)0Jafɤ8yxӏPTc$!: ʁqVdr@/81ez#I%c%Jc;i{lfE֐-4"XAΰy&vm7G`&97auǪ9|CdIۦ,ęYj/D&%[p@:x Elz5* &">oe*JK9c6_#)m˅Sc"׍Sפd&8wx?uEyw5gmM&!^ŁYT+G2B6n+$iP7'Yd+E$%nɈrN^J_3iD.u)[11~!e9ݸۏU Zd>-8DT LqRvfq^3q6vm[jڠэbRaeulq&{JJ 6V/gͦST8- Q ty8u;7xz$zڤ=ԕs|.v/&8*s98bHP~:fz$8tێWc.,$Ww ٽe?A4biy'*t t7C3!716A  8a !-@M=CԜ:nl*ݡ+u_zǹ9vk7IR8_޹+Wk )5PP+1Kٲ?{j!JSlMOI71yhpؙOrYx?,$("0LSQ68Muc(RV+B9EhJX, ) 85P 1F EGŎ60cLvW`I<::ۏ`O1r* 11iژ[Jr0AIYD/}v,z)P!ĶZ=b sW6=9ec6j}2%Gi gtIQ"9+:B *feѝ[w]ɭ#R U5؝WGldgU&\;zT7$FK֖rG.ԃ0_ |cm[cۙ-m$*Z7;|Gq M~M*i R 6|tFHJS;Xwb0YHR6K6!̮:;?FmO?NUs8RŖ 2 džٙq |:t߇/i>Pf Uj9FF rVzl>Iٝs gBMn`"ƏNtfGfhlVTqBkd_& NfqНHwtƗ>L1c9/3*N~UNifena)Bl۝vB,a4!ƦQzz-x-x8zl5ԟq::9NRAJ_[sÈd<+֚Ӽ;_lijvO޶E\ Fg$LÀ&,[{ P/ig؞l%%gC+xc[>f>`=G)coEəu0}&+q/^$t͌J5)uo+ 'e=p򟏲l!BKa^:^ȹ=YJߔ=x|q? ň{153[Zٿc1\,y:t/dy[9TN^[8e8t7LȌ(ȣǂΔZJYި{~ʴrHZ4[Gs^4+O3(fVy35%M/S/r=ӆ`%O?V77|Fql˳_ E3 }FNѼ̩W3Xjg5I8ˣjYUͯ~*?f2#Ł:?؟rwpߝ^aiq*7Lq47};Acnx8\ 䕿cpˈ\?f1t,!\^yUw2X͊OC/^cgUL> 5{Hg-p\螑VZx/8GX-k7rԹJhmG5! xZy{)]ͯ3sd.m(ɸD Ӗr܍gXI_{cQ0/1f̗gv5b RB6U/w7ϘT.5s=hʟL%Jg O-iOuWư/?aR Y^O&s̆oङ)cu'x55,xv3 #\Bi8fQ'c̙7gyl:̛? Ŭ..dL̺r}u-kVni7nݛ̋gIDATl@+K[-+/=^#26NLLskY7YO%dgd<2G&q0+(z}}?j945v/+c7?ϴ=,ï9{#y臢RsѸo Or܁}2󝳙ro^%#v oS-QkrW̛CXRv7•O{S,b7&#Z7%4aisŰS8qۍ3*Yd B\L .eh\NJwޢ~w}t<5|뽅 q ]ظE1oVrO#2fOgz^>-3:[x_cƤ+-˻bI)f\> FϾ[Ocy\ӭg7l"$uAH::7t&Yβ0n9d{ _I|9'1kPv-&#xcSl-E0\n{%s&ϖ~5#?.e@>E9Ywq7_i2sgȫ`7~꒼Yi8.Us1nW:M|k3YSt` ი1HKivvԄqD}GU'x2>b`kK$l__}ä5ϱ<E1y'135 8_G %!LmUzLcbD$yNlzN[sf3{4;O]$J`FƦnc4]~9ﳩ<em`sxG20JȬ֡Cz|Ƥ0[8{Ur-'gZJض?hmM`etٖQ7]FljSAՑ axjٴy>>3ur/&9ͷ-Ϊ%>l?|Uxё 6L mvt8&Mcvt#{eǫ#>0S=v⸪ev5t(׎GՋsCMn mW[tg fSa\[wNjSW8]}LH:DJtkR7RPK|~I5t _7Y[;!VuUlnc19hiqbʹ43G R#8,'5-!>}X\R6 ɹvFS;7[} =tÿt@G)談6U7Կ耣O.p.|¤E3yllG2ȫune"!Zg!rppY BS n'Y_{34YYvSi&'6ƍ9bٜ(h} O`!sǧaTN iԁBrwS"KUew8Ո 駾5j& O!!mJIY#bS0dD!V#U8\D%֓W#A\Dv-Vv؅ll~g$cI J(Xz)>M;B黍~gBҼQ 5僰;Q>ck:V^FH 1}tRfs_tM|{ZWr؍hsl$GN52? $CF$dXz>I߉i ([h!`1`xŝ] [{s:j+i8uH#ğfvX6o^9@+4qΡS)Uu{ mHim1UȮԕ耵HjhlMdX|sT%LqU.f|Ȋ̭0c;9y;>֜5ƴH2;:F2_$2Tv7*a *V+-dW7=W`@XyT6L~5ю%9 v-s[0 ]+iB[h:n?6ywF^W~8*NJ}up(JN`&8H T_XFGm{(a joڞU$R>Ŭz c3WӋΤ W3" wHvv1lgMdfLfԃӡdj"4$fz+ar]ʶСC6^g8=\n'73F:#٧;O؂gxyfq*uDx'yygxyxo-N!%PC[W~,GԜMCC֯zQ_ 5k5^{<̓<ŶF7F]4-}aYx2FBr^7JTT8a#+6] 0\E{0Sre{\4%2&q.)"kr99{Pۏ]!H,A yPNFX j(cK}5Y6 :.`S8qUF!dLw!A2DHƂBZR"H R$By֊%Mo`.?<ƣ}'{~[ TpNrIW="XuԵVOeyOKtW+-1Zrk>!o}LƹBj  '; aAAP #<,Bҕ:o(_^īϿkK SiA4=^7*xHdG)oFH2Q^|q'^%K՞z ̙ +yo{k9ߊ)MϴQrQmGWWRCGxgy^ѧ\7!84IB֡&<"O?*?kWa[@1ʺF+7[Iҧ?*Nq =[ Kת "k6A/PJ/;@H 1>GT;AHƐ pDڃUzW Rه`&CZ7q<[kXE:tС_ҬF郢8Pݠr f؀nHcwh& ør ɸǯNOt^nK2eqG0q׹3lK߁+9CR3H7ZKo젡̜}? !e. k!xoƄ/qQW(g)ZfyMMu⸈9_vX>(q\Ul%nq3tRFkr{=gFb%ګ\{Ϸ{a6#5Z'Á_khmjKɯ:ٳ?ӧMk̸!\s|^XĩH")n4= k 1LnkL6~|h"%!ewsǯ&Ňy~1~x3$OS uVPq}6F̙5w|2J=T$V$*~,~1[½w˅8w7@̽FnƵ I ")*I%d,bԱ\M?aބx "O"zJ2+fZ݋3ugƼwp털Ne8t$53sWRۊѭAhPĹ=ZF͜UaR<@BEvB 1 58!ebGN)#G#-DPy&{)O0 hZ[^.!+υ6dkbۏb{Mf]KoG[$ 0FCi1*[MN0FHG2jd&h}c+I*o[vb?ydNQC$[t P!L9oꃨ)LB{2j\~SxJ#/BYYERgn<o;f2zdzb|Y 霪hz%3jU\yT5b7捦w:3ԩK֓9L0ią[ηi Ziki D_?4}B3/րR3.Clxȯll$4Ԇ+U=XN-Uuj5RqD?_[03&Ϥ$o3yü18@Sx0Ӕ쨓ٵg0QUZHkdv{ ԊWMNr.x1+'n4{=9عV(BoG)WgT{ 1Q^\QbM#)㹺oSPi}8 9m{lKx¤;'J@i-`"$^_p)"Oml&Ke:R\uIFU?ZY7plzЧq3?I̶kdry{Pp%wcʫ5_T!64w-e8MYvzPϩRSl'A| Kt Gᇿxbl0KYMζ:t m -;fp")d2fm&!ux8UޤS٪-ɢY j'!3.<;;P{|S{Ld|,Y*>lԌ7#g2ȾEySs& 2\܁1aUTkZ}ML=(n1l5єN8jFH 2ICx"rk]Bm5Z pqѬ6L~ErP? yyDLߺc∈(*.ީNq:MA [ۃ.iԖK䏃PAmujSg(A-CÈT`M~& [^A,޼EJӤiWhk+ڏtsGȨiH꧂"xfHƖyOKy~5#у rb,X.9ڞ@H;l+$ #e'YwE/I! tDD̮2,~D.tСCl㇗iAWNu|& 񱩄u[)h(##sk7':)pZB+w)l8BU}glf}dp_{-Lͬ-&FHm1P6ˆ`wLak#AH$b(96O:˭| );84-^dJ8pxkw]@偵,9sR֟;.gsf%`c^Q`xwr'\;4VS[ɳێQY-ﱼ,|S8<bL\Æh'ssQ53`4\qG9n娊a Dcn>LQEUOy}DpŴ I^}JmFhYH %513{IDmdzLbkKLڢ;'-;(m,Y1LFQða[,,Ԧ弹+~}NL MBj3[eϡ eLY'3rNbPbx)ZBa? oU8} K^~ksH Q܄={rN{ a2صfyar j￵S'ٳi7{!==Jo1\vT6-ZӽfqǸ g{[9l!eS:~QrfվbϞԈ#Xw7ޡgy%] =iL SM<f},7mύ̼fFu#v;7e cpWfdq|!754W`7iC㴁(9VF"t{y+Yl?RHq9v._˾l/'&Qg8dqj7}VŕLFjezfhqV+!:,\2raqIψ$Gm,\w3')6$1wֳ=~jxsR>jaw 0O%+hj3`dv߃AwyO:0 qsE6fO^HbW]a S^c޽0K'o2,'Ա M0 0#\_xlGC% a2(<{>KNhEcy>'JilG:՝Yđlk%w%qwww ! !h6LI6{Wr3U-)k# 02#CȌD r崅mkdxN8 ot_ s3 ._=h;meӐ.c-Sۻbߍ?vGD<3 MY\ʠB $B*cT4޻}q2(n`FB3laqD7h+XY -11! }?!4)%\y-B#ET0i8|Ic0_ ?ʔ.rÔ]%Є}GDF,{>ZYdIߚ0|xŌxQai[ r?uNIdZIrsW(TjC,,o\Ƣh0ah"GG!9mlhaH KD,o/o7(YDkc!0&E'X^`l%JBvsw qc2.rQ#gť3!_ʯA\"Ux{7}7MEs[ Cgj6%P ij+=3.֢(`$]E'8; [bP9<RejH 7~_"r>$c#PPV>#2Nn`yf[sK] 1#kHD\ [ȿ#071g!^ ,(Z$ 2< ?8'\w(Xd302[^VbЋ˗ 7VӡwQ*d̗ꗟ7܀Id~LMr]I kGΧ>25m&c&{GXH3)40g>AGڻP.X<x]KsDFeS Qp62kx{_=tqӵ&?MwYrн[o:{ q=G(YKr1%+ᨦNGW󛥅kiŁ'ӐNJarcgex1ueܷH_2x/Xcs<򞗣4X?b+w G0?>l;9,ôZyDKc9&&I s'qcVFLA&NL/߮~FsЪ#|[BDP BbpfDU-kX#~XHg4AĿ 23`dƅp&Jya\v(fL32NG)`UpSlr[A!(aΔИ,hЯ ꖴW >~MMB!Tz3į#gn߇~(Xzo|*q#c \UW6m01ÕAOr^^R+;WAq?lo-J _ܲdυ L&. &/" r",ևĨȞ3;3.d@Bd0b.nC  #d4[NSaddXϴ}To~Vw÷m?=|?<Aof   >TOAAADLAAA@F3AAAAdAAA d4AAAD&LAAA@F3AAAAdAAA d4AAAD&LAAA@F3AAAAdAAA d4AAAD&LAAAf/AAAA7e$;DEAAA=dCtCDb !)>AAA h`I@63AAAjm    )F3AAAAONAAAD&LAAA@F3AAAAdAAA d4AAAD&LAAA@F3AAAAdAAA d4AAAD&LAAA@F3AAAAdAAA d4AAAD&LAAA@F3AAAAdAAA d4AAAD&LAAA@F3AAAAdAAA d4AAAD&LAAA@F3AAAAdAAA 2!%TIIxB£_% T.P~-QXIAA_2bb}"<{!LC7AC.G0{L -!  ?d470 ^ d4L$5,1}:JW  婪]'gXCPL1BiĚ'(]!  ◍fGOX&1EdLCAA_/ZrҵhPOE"0mtnRBAĿ2A *IqI&rRA~)4_%hHFͪL,k˔X9f&0Rs@GFy||?dʊDV^J'0}~stCF3A<^>6a&>y_f0'ûU.7rوjKW 7iV' 6<шSuy:HfLjFG1}3q~~n0+QROڳC\lŸ̅TMpk$ԊGb%%54Aqklna#]VޱHP *f$7>B #$lӛOa]urs gA 5CХKϩA`.߸ eF Ӯ89:v3󰴳Bf Sc( 0Y.r,-LżgK~ <ۋ菡ELx0#%Cӛa̦t%1!_3z^>U/__4=zt%Ͽ$%%I09~@P`VޣR%~4bȂ  p`w9#F3B?`ٮ1=?w+C'O4ۨC=pnFXwGVIsgc ~tnf9!x^L$_ѬdUˣ^㦘^0/ijb:S ޢfX$ >лCԭUٳ'+K[Ifƛ؍*Ӈ`üV8M ӏ$XiԺ#.GGwOh ɼ`$wpe_݌Է3_Bݦy[ B|憻 *wC< O|SKo: i8))>&pI+٨(4j*|O'ξeV|oqzSL !xDGGA%3QgsѦq,~/ޠ6v ?1$;m L.P\~@~ =bڔa@GMq;D(,Pi e<~tƲF3 qqH`ib* Z]8ʕEff99,_Ak3 SETmZBPEޮX4uSʐX<= KOċ?oaп7ס]V7/qF lRf@+1w&Aɳ`3u~^y3g/_3oUm<7 UK{Xg5:& Q D-,(VBl8{Vk\ ea0?=I q,0b mH8"TdZ 3#LEtG`qMg|:OG|< Ga+YYzbB>8'a],q rD|0KWbjyb}׃Bٻ!. ZfGiR,LT'!NVd, g(*`l(֪ٻXGŒ&!2Iz jBl./XzcؿAJiN^ Yz'R9Fw]{`ز^a(?"WAz+6oڄM`ƨ^ΙF0eegAa_fe0uV&Hp{mJ'$ Q 9-Js ШM{;GQ\h?`֮Xmkc)W/;b $077NS0̎`hw^>f7mX]djw^#pnk6bذa=OųԠ @xLk NC'ȩ8~_:HOq;<}iAt5cփbCk+ Uzq=~*Nܐ(7q&fwF֨z6}Aݰo=j 3fFaFZ I;YM+֋D0C69baY=/,̍e_|uz.ƅ55av9Zi:6{VNƚr(Uq]3+DvCp3L, Ȍ\UcfQ8;T2ڠW YŒuZ1%Tٌ͇^̐?/C=n;TQco7Q3VF)\y%e$f4bfjfw >p\.C"xb]D1zV͋\ 5oǘB'pާ\/pXF65`J #vkVѭNAd1>=Xtm\6Ux4i DF|5 y 1p'BD.+(cQdliYێkXߣ*Y SJ Tf90gx5cD { r(q#|ޢ>2٥*7E{̜AjoxxrTCOdrw;pffr؛M(uXZZVW|+[pIt5rx ǯ!XLiȌǰrE+`1f UB,TnjœS6 &9dAkhT񈲨yC'qBpyo8, Pk[FC۵X} 2(ZǏD,?']CI. tq#v_zD W[#xEBݽpIJkxg0vE㐶T5x};w\J̫Mݾ6źFʅ=LKt *6<,~zD˒Xz7#s&[5Ǝ[5Db`i$G )T  H:g 現4`4TSeOHPE7@D2Ò'#vTh2cd\,dyݶ#J0e;rcʡr(a^ZǎQDA̙lˢ(M^5ߺV<(/?JؖEЊ)q$T^,i_ 2:*UfbP̈BBpSEXXԌR z *T+*#W(2ePHdPM1Pe,ԭXx#+T39AJQ**QՔeoSWǕ)mPx!`Sdi=קBL,4IIY J vY*7KOT gE1kVF,=]o5Yz ʖA18oN%%Kp`h\-Mj ]d`` dS<)d!h.Ե1_#zx*|A6N:+Wo╛InѩPjj0cgq5\{! ,Ms>g?ƾ0]@ţ1l!&D {;0`a٭x$\Շ-$_/>GbH{Ԅc62p+0p] Oa$ Bnūx7 x~s6 s"*8:ᳯ7>αhg?wCpGzN,z!Ry>؂}s0-M>4Ƶg,ʤ08_Sx4{Z {p#}maVXyJaäa.>ãXb3^}/JwSH}fgV À/X$h=myΏdplz, u~7n`!$>FB71i6xrVg!AO`nflQLIqTR%Pdq)YBtJ32?rdgR~zek0sV^f昇fȑW1~)l#G2rGsS=]FA$i(S[J^vw J̿ !9N*=Fc}^k7!hdʶaz(:7o-LR:mFf=h=Jry>[DuQpͫՁqe 8zRXP(0~H`ϩ?= auDFBL Knq{lۏD͉v0\˕L%ؘ3%0ZvVcʓ8rtkYUsImX v5Bj6hnu*gҜ" x05Ui>y(JwױXAJwRCڎ[q {_2F/01R0)@bTV:g~Z]4 RQn k֕`.RA-9WȘR~d >~fe$ LLB7Op^;XYWII6y60Vh! V&Q4ZqO,߯1 Vs6~p=&L^;BPǺG>b -kX~mWvB@;hzrqͅ[x`wsE_ۢ_9{gxǑ;;ѪP0v*ɔ1~"p\Z\nmacxTh;0ؚe}-&m` ^ã篰{zoqP00ns/;Xն쌾 棇b296‹Q=Fr B?Lxb/C,Q8}`~3#ݸ&];xvp6j#w}`U=/Ɩ;lp;/1K%{U{ Xr/ޜxqE^Pueu8cP+6O].3 ZD9xt4K7|] \I8{0v:_ȜA닞H5w\/_曤f,hu'8_7ak".xx#yMb2EQZ;v_+_I1}<{,͟޽{'$w%0cDl3[[0N_+~r]cSp8<=Fh=e>fm8;Qn] cSX0sLnĆ9O0yXh9ѓرr !DUw؁Ǐ<}Mz_(A9u*7I>i٥=:FJ>?k43+Aq^▰2ǐR|TS?!YIfS1Ty{1YnP{L5;1tth]FQX:;JgsjdF\6xfK|[_'B?Cx)/+ `bS ra,Ӣ&.\~ RFrY[8`wn\B8m*|)IwPrV{8xF@X 7?xSh\BdQA9 ymS fNS,ܶbav?De_LU1y:SFy[>(N StM=qh >Vb($ׅ%{agq)4Adx_;፷ JNJn4X`*B1x3zOmMQ' yKsHy q؄)J'iRLPXL{N8!"_i,rU"jX)ZwIdcEp/Xd:t<)#0b|~uaQ ibUbFti,)u|"A[/6Mȝ'NN\1_<fˡ?L ),Y7k9`p X28ZF`eg0bi% NJGE૏X`)ʠe6)KEhtkV\k"IƠFy%?Eqn]֑p~ek2u5b@R:Qı6K`l+Oi'6*89 H6͡ΒC`q93r>T($pHbj<~ K$oq|@ݥXx1Ç ԩS4ƨeVZ13#6͙c'c5"p)rETҥn 6ፗhX&"43SFHLʃcmԑ+sP8t(wA0{ES3(nm#LDFGwHh|H$Ob Qz5 7LLD7`O~_142%bpO_5EfuQ^ 2%)k:P ]L+Vk]3#+{p=mY Q05e Jv͐3 xƕL6J^s*2^mi SC,yu u@N]x~$^ Le0c6 G<,2+c eJ6I Œ%qALX oocӼxǗ%8g)4L[ Ɉܕm֖蔻W'p%;3J #X2yGap>5jW1z Y5FryKqxңNC1`>#Mbb O%D|)4j5A?\ٷD6V ӵ'Y1<&3SWYkezAƗ]ªiuȆGVba?i9x~װh\_f Ƹp%Q)IJDgHREQ"3_ E uYAk 3eNVs^3c'Wŧ?\gTb1;N.؟'#ruڔ:uc}cju1&V!_ K@Yf$yㆽ+Q/͒y*gg?iŎBgMDOdku+(O}zcơgŌTmۆeˊ9gώ%KgϞ]a[& ƜU!x'z ⁈`(X6Cco7btØPo#z[A5vV|h/|p=k$ZhQF8"._Um%eHJC'/AxOBо9uJfk$:ͻ>h Gu;]MAx`T?3VĘ8-nmVxrXpO13F%/%,ʚ4z:Vt*`Z%a _p(vzB7E WȐ3oō~u-L67h?1E:T wu:s7"aCc(Gw>߷i3eSU*P s`s"S f,)IcOoIT* -H buQgRb2Dcqt\}cG7HhnL$ƉA?iad´'Q~H̩"޹07YzBNh>4#bz ;l {3N!R3in YuIVcmlĪ X>"]^;rHD7/BZjG(7`5 pʎ<uptI?cNPLDYfGvfnkcزa38n3u0=܍f4xq "c)~%-7c8~a&wnbL@t\BS n>=xlH*mM|Qp#޾x AÔ ^Lc¸E[{cc8B.;<E_ÙGL1`V<2/~e^EaGpMr`n%_O3޹ʎ& h0f2=&&AL n~$$ڢr5aw{O+>֔APCskF͚= X* & '2MOv lTn4FL> cJ*~  YbZh!]a`kb]XҦL}pڑ)@V46c*?Lm޶m\zci>5ox}E(iw` 21KpXʔ4A_B|||.{|FxxDn27O޾"247Bk|]-0@~a)+ˇu˺3/"Vmj=A10fn g^ qa1PD"F&e#Gzg§Pc0t a6qo/r%PXmr(ɆKSd4bcUǬX,hXO+kyPW,;maΌ no\"]uuKuFKV& 쌆%uK6]=N]te74,MyJc]KQtL{5aaQ-Q99T!.P|J93&B&tC̠UX7PA&H9Ⱥ2b搮0qo2w`]'4MS/Lqx"oۉ7̆1(!MbMD SxFeAX+WDPJ'3H%nR6f TDI>\'5KDv`J~uw{ZXtÃFD'C-1z3i"xEd7xR>vJ?.)l ä`M{[߸3';C| k0@nEKɘ! Ga*O89ILv*Kbۄ>{<'S*M*vX**M;b x"F'{Rn@TYnDae)]x3H"իQNAWܻ~T2ce!s(5&AJS]ךfGB 0kyΒ?5@d rAƂrtIeJB> "sW3xu2F +i:9ػk],OWqKR)p|g_4/錝' u?RRI0+Yz.Kh%KO;l 0W5N\xĬEP\nVWui?&MsVa]سw?XV*ky An،b8b:{.qTxŒ?< D$Fkkx#|thѧ'ƪs%\rG-2/N6Οێɛp@UJq9b66`wDžvLT΄~(+͍ӱp \|n-QcQ$׏Z앳0o6<6"]LQ ۘݙ8y;KS06%, o)ܸt;^O?̂I_ &?cίhsEќ|$ݧ4O<7G,JWk8}7df!s0_s;٫pO'Hwm\=?^vxI\rw{>YO}3?;rʓ9,~تFA!"{+L92S0O}_P$bi3|hT] &Ei5&|b ⑛_x-o;0@l l<8>܂3{x-(;3 J>a 6k6o߃={`Xwr7ˏF| .^#7`[:)[6Sr/a˸zz[fi_(f6cޢ8鱽b.x ɆmO!Yen2Ut'~f%'2˕3LɃŖ _&HbڭC2̝)L9t*u0cr? o-ouzsZ2;j5oiCzcyp6O,P=MaM 3%5 dL776}kPO 34Av Y+SjoCϙD`6U8weŹus#72кWOF>}ӕ#Q߽g\Z#2aqlLCD9Á,%t1Vw#XILl0,N?Y`*+caBO4B{LIBХ&`}wCܸ| ؀y1L8:S7A"F60歉#xYV=p qZ̒t(x7Y-e=r5+LZά5Rw4|,fҞX0cdYa٤7| 4@Rz&D@}yJ>H!>P-1-\.[絀CBt,9to,[ŋcՎPUFL]ƕkkC;oɃ#{#d0FNx;v<&MY'cܸ1| >Э*6xŽ1qTL>'Ø1U2KDX:3mf9am0JX䆹5|0[_sa֪(o0,ݺ?/SWcoV1c㎕a baO̅+q#{[V3=lX2Smr4j4{CГ#9~G .=M@rfViq `j+V7 O}Bn=Z0i ;F4)ל+ 1,Mሓf+b쒥Y3ӱ7 [5+vBX <2>uK6 aA cn"(-ǗMƨaC0l<܏iC,/?UuWL8z" rF_ povuM@Cv`4-ocL S' $$a1){U CR&Zu"YE3 ۼxxh&!#b׋tem?;LhV'/"a\=5WcI 0385E,OpɨX dmE:YǮ_7wøsGX<~'Y`> foj_,˓%J^ XIآaB0 GV PlYT/ >On{^ A8>ywJf%}f{rqoRݧ3|gi1 w̿2YbC1/YD+} *Ir#~Z$%cc 'l47Ée SDł2V ۪.XVa7b2~=[6w{qYQdcbY}6n $2e= &lDIx&S`dFD` xzMuiHGl&F5G@a^HMZ¶4:!f\Y%!'2#G Sޒ2E Iac&~8IJ6lljڽ+hԈgѲ6h`7<՛v"㳓~efk:e929rTZ6_:%4 r&g$~ÂRT~ޞye027I}6!\+ G5必(XV2]0CZٷrTBl&?nߙ,O LSA}>3⺽6=|>];b$Cugn4sR RW3#YgP~]A&I2+fx/ssRtl\!9EA\\ɭX: }#An H-^%F"QFUT\arȎZ+= @ Qٙi&|yʀNޢ~bgzitnq9xjKdYEuP2P*U\:wWo_ lEPAC4m5Jٴc?l3ͺz">!ukUǡI>:]ŹqِhiT0 {ɮhqXP6_j"~o ѥ}+Z< Ѻ3vX 2 "|Iзuħ?|Eێ3 cXnLM~8;gVUضnKA<{M⧧ϙ"~h ?熰q9_bJAo] ͅf8G.ZIAAػ+e~ B^_ j$h7["0e"qwed4kb>.Ǡ;qj*& ,QkEVll>zASD-'Hn1pbN-,9H{Kn$Voo>rCd/h ڎt7Q_=+(E&S5'O" 3D!Զ O&H|<s°KO;hg~oI^_7ϒgw%fQ_6-#3 3oup*+fvEr$!?;"]JUv߃" wn}@/Qc&07B$O{| gߩ#D4 ܻ~?XNA1epL"-0O}IӖt2FFPFFPhxw~5F/;Z\cM?5&wK 51al nU /Cw>w-eFh<\aAR*\]{!4@n p\'Tn&p{g1jd >?fhW|dPH; WEErg€g`:vѢVJL>s=w1߷c6@",:cWϟXEVmQtO"3'Ǫ醚oAtHcڳ{QfA#1oIo$%Ǩ2Y}e6џbSehm#⾪ێsbXq#, V*'9?f%LLanml?#(`cD)ѣz?*%YID Tr-JnB L^X[F5Z ?Ce0/XNEN1}/VH6LJ>"E0x C01R2&ǟ ΆPbxHsXPC6R]Ey9!ǺU a.drgi Z̭LJtl Uڎ,&Vy`aݠ9*w v8Q;lYf(׸taKVzOL…-#SQpط瞕:cԃ`62beu.ȏEZ{.:'/k AFbYx.]!mۢlҮpar/vdoa9l%%JeCPFK5çÀQ1l%]ka [N7iy߃a};bfh3q>|]J+кI TjC' ѹ4K[кY3ThϽ|SQ~Sk cvpL|A;1|VKnΧ;;ѮesTlcwc5L_5cv?:^BMU5x_71L`S7]GO+U6Р;k(įj:ƿ?~][|u:i}lNZbݔ3XyIZRWg7cw-J5[$VX ~v# GaaK& %Y nT3 jNӷc"pby21= [Rۦč =W[ރYuR3kT/v0o,5 zuCE:0f˕1\e̲ՁX=>MMhӬ%*49dw [1@QA>hCs@ Pk#}ˎI\4clNr6B~j|.ހM< =e[vqfry'gN賝צlkFI19#nv7#-,nCmPa';T,Lfn34k0z4r4Ow=p?mrM`'i8Qsª(Yo<<ŦIQziۇ`עj [_ҕg;^K^oL?8%N0nZL} :fO쉣nMM/k}]ۣ||G=QqE_1Ծع|,I1)C a{iO1hwwdia޸^%.+ƕF0tb}t͞q}Zoނ~&Dk`YwOa!-rAG@|¾壘ԂX*d=agC^F~ෛ))h԰9J5h{%2}`SVtVEFLd:78㭅qg@7ԽϰmV8qs#ty}̍BqM qNdYOpڵmVh5v%^3bbNWP['m6(t~kӷx]3 '{, wf4:H6L_hw8FL#`;)sܿg>s\\ M6A6,}a%f6i L相Q.Kӵ,%3>lzD7<݌֢/kfm0L-R3 <숊Li5e қH7bpq~t\BpylnL;4vTi${`SO W`3)qJ:&W(\߶s7jFΞ}!iLYL_ +_|XY ډɎXr)}1p\95C^qW!ϱebolK/SJ&2,yULjXI9^h]4ZI}9('#a1\9ǰJ& C݁;KuEO{PEsatV^K^ K3|.v/l+oFq %W\[Ņdz"- H> #3_^Lޘ>7!Rר n5w5L==c!8"5> 2&016=Io1M;L`]R#LFo'ؿvߍZm1y8Yaa 5Ǎ;ma} YKBۆE` +i<;!<C+}R-n 3^b~]{ƽW63dV]qRzu<>|;P9vEd ۮh5&FbްH5ٛ΀kU-nU7}pc{v([<SJ 2.p}d\ '+p3D.7 &4m2j fߒ+)\ j`4Z YpzbLyĻ<^*mC#cVL2\(cʢ#&MBWT7u _0hYa@+a 'Oj3LU%x TdQo&g[,9"ch'æ5a#ˋEu2" A~X GbBY,=='`x+vL4 ڮwAscrC<[6]o4zqm(_sodp&;P|+iS#fkd?:Ymᶵ? ܙ9 Qz%O,GN|fpĖ͓ OѸdM*5:I8<>#$#b^nFCaoP gEb0MS J_#pE+8WfBawNcOa\/;>9]޳FOZ%j[l% pƱCO`[7C^{!.Y8MPmB^JNyæbT'gu뮽nvXwEKez i+T?Gև ? xh~P2z)DcК;™]]FYV@8~^?ŞrgLyk05^bJ*LNFF<&!OA٩(7ksO8h[~AΘ=t 17t덅wNhʑ+ mz}xR.I1jwg򑷛e_ҡufFwx\q癝ϯQ@dd%.`*.PlRFT ض 0LwWY*<*upo,~%ڡhsWWC1~,]V:11.Mm>Up{/A7PK Z> J)&02&d %Ģ{3 BSsg;'?ZAvQ FѵX涭S kQNN~@Jfqt7v%Z귶wƨ^``vXTb:<3 U_Л@RC\1&Y.ޭ(R0 ڴ;=fh콑w:a5G\qJkp[t>N[\`۲ wˠEx\H@!h۶=nXӯmqEkaD9<_эɓa85k {pam/a͝o'x"=?k_$H "! .nsZ  sh+,OkX- u,<"Hxĺ(T<>3ZlE|?&E*eELn=sCؓ"./Gzh <5>ޥ, "Q7NlTiXN{ ϒpDY# {É(g7 Ĭ9=xM!ܦa:ܦUO5붻S.v5b.@PWLnmӆcTY ~Zb o\-fHL9(JA4n7 "Jc~È˄Z7*"%|sLhW} ɇy%[Ehw>X"VW4l}5[([kp}pL[A$lJ(4^ޭ0rΝ; '%N}FɭBZCk_tnCS '~AW0)D=7+4zV8{{=-$Z#D 17¦> ϭ-C*S ;#*j+I<!\+VUrзt Bx';>pFxyxP*W#^f' Zny-tV Uunϧe yq~i ?_G2A+,X $tτA[ +2*|>y/Iz/LhQUh!V(;zg%kJP]襘,/ ]+Ϟ!XWWha먦-|A-2L9C.I #۔:/uk.Xp_r3b y+7>c] :IjΎuaJrտ]ɑLOmY taE/,JV^O9jߛBXK10}3Qg[$N88jɭOpi!gi^ SE[+\]lpSJ^ c:4/V84b{-Wv̸+;%[ =$0U[a/KsRit|P`!판cBMV/KABrc^zvvᐫ$ Uv[^ ;N'4VmG |;.7;' s^$Тbua }ALpofvYɝ€z~  -7r1~\OԼD: {{&IѠv^a94F(t vởSAQ][ n{N{X':u^"B;u ډBגH|gPjC>6 5跭B3ޙ@[/lJhZ~la⁂m50h LG+]*I|0yuue`N_6Cz(s%XJeHɈ%TuDIB\Bj^Z.3KpqCBӪͅ>ɝw;Њ"i$IN Kn͂e;G  Z 'Ffgrl~5 4,V8qtPocKzɨ!"}lncKV#tYv;i\Ӧ}-):3P|8aW: ^RG )z_׹0Hr_.#^|]r'NSBr#LkQChFYvzPầ.7c TZ.6Vn"Z#jWavfB.s ˌ&M(q+M=4VO.HaYRB !WFľ*իn"bMI(a+>1ލ ~ yvnU_#<]1D(yx"^Z7ZO?^DJK9B2='%)8-II{MYʀMKJ%S V#U6^yP6r v!t,r6DlnT5@2ƈEHp0"(]%<_ޥAM(fغ&VQt؎FREI%ί_ /29<*k)hĩ5ЪRi;d-T 2ư-ڶ8 &ӪdScH"3>vq&(0lH8Ka FuѶP P"*T>uL~dH뇟æX2C '4<BAe>qgr8$dGD \^%τ|7/QcoD,`LCu?f%*.X~yʪdlRaL2RDmD|ZN9¬dm7C8Okh("cDhf0c|#б{CCGFPMO\hѮ>|/RѼnk H(SQy=`e)Blx!yTr8@OdOo"M=R*`s/U^xr+f ,<ַU-Yoދ X}۬98IgA޲9ZomK~"0{A|e8eCl";Yu}:o¶ShUQ{,)zN :i&4臺rg"Kz %MmKkY6O/a{KBlNj"#KiS\lbu=, [d ֲJ _ib ă3p|`6I_ˋY"Ӭlo|GC:|e1ES3wʷ~&$% ta5xQWc>?z hP|yU.2K?abLGP:Bꎾx<QV 0A T5KtHLSgBQrI(YٲzX-!bUDg/ѿwIi(rV0͐_|-TK80ə@q^iљ +l(?pRȝ(_l0SkQfǀayX me[ Ś{ e2<6/ϣ}ΨҤ3j42G`/AʊO>0G">0oY4k_{>;#|c4Ы<yJ(/@|el6PYO,[L^rC2+Lٔ fu@*z]H^8x*Qj du~Џl(VZe7Sժ^yKHʗ/*y#gs[2% uBj01C/-[tGz_1D^X6[A`D"r.~ OEيj& Ow eAFX䲄EBb;N\"kZۤI:FcpK("  V/vѬncǟ9#4J t# 5ؔ/W8d/&qPYo{lOO86ժ4Fk UIn{uci*$ YNн~!2?3Gy방e[&_H+4,6r䍄'k+% &ߔ B+>lS3NYݕM`g2 R$ < BB0:Jh 3VbWפ g%,zpRC*XmCl H|}o;ONCJe%A3UbD DWGUwzkVE e}mߴmW0!0|z̭[Eñ2$0!c?ccZ 8~a;eѿb$V[dYs3_aV?C -%/n U\  ,ӇuU1݁W_Eo?G ^}|o,MJ+tMLXLgWbԬʖ ۡɈͰ{+į-,7GkRP}A}=?w&F:(h* @O]1<9X50U"4YW-ժ(< lBl gwF}oX hQT|tA>Uw -и^ =}Xf"Т,U(e"ѧp1nz+AHہ]}/07c[ :ߍԨ>6.oYE{ 6e\CߋW@[yjuFԩ=-^]qI86x#)0%Q 3<ēCYƣN8ہIA 4[Fݤ9<L}Pq7`FÖaJ;ʏKc2,T=j)ql~z 9&*Z:ԉ& "w, %3fm㶰 §O8w=r@OLH(Z7#}?^˾\9"W|B22mA뜲_i s6;Ԉm㍮=<+wscmd7RaRyxMp\{l&)ab*^zy'?D:}^FIC[Y֓IN{¦Fi˫T1hWnñ3DŜFŻdgA֡yT nM.) p~;and[钊 ,WotCwhA޻Y<0JBtH/7vO aj3b`{^ZY:>Bl?kCil7VM{ I g5֥Ia\F.?FMSQ 8֫,`_j_{X@hϘ~r vVѽηzP /N5P,n9$ "/lP WUUDJ{2AmҨYg}ƻ׭lj](?o7n߆`w IV!HyQtV;8z h\F*h6+{^<#m" `ZD~@Fw֞ #XKcF=lh13YcAFzDaUWk%lSE}c[aҫBah[[ZLS>&Xt>+ҕwfIK09z.z<ġ1po~w4\g;( L 7:Œ3FVl+kf&J~wb"RS"c2\Јo V,2i>v/ͮ0{[f: fF筏 vqLk T>i/ܟ lZ[fT'`N(ܬF׌C1iLt2nhʌ'>o v5wYCQ-RȲǘlYt6ㇶ'~@׳2IR[O;C>ADf'f} v8ҿ2M&#yOiuiϽ_w#g}JGn=60 Q K51-Q)>u6#ᐜSEa ϘS/ EQӓ`m+cCX6c1zTB\96:,~DiZ3+t[wV\ɪyY'/ cJu/oY+J 7&(_>7"P\MPD 59d1 1* wQ\k X("{5K[[]Ć 4^v;;I~yo}}̜9syfΜiWV}&kշ(HkBp*`0Sx麸?l3_ኯH,B9/q24-m7 j2=zSF&\ml}pw?g RXCF4A]a@?~R9+s9Ɏ5?e7|yVK 0k!i8s|6t6+l[ڢz3|hCSZȻ;l3= ݳqO _- e :6; d@5K:zIf|wtNs5oX,`|wvAsTx%l9liW^`QJU[;iWIOA3,+cm#ĿxrbܽC0WoVS̚ܕ."Q.kPeOYx We`؛GsXCPtQ)ȿ^*''ĢpNUJĊ? -7 ~ߠ3E(nܰ /Q@}1h64,S_Ě /0Dqa|=#Xn?]ɣP&|ElQa,J\w;%Y'*̅GBu2\X,72buX}.H^Ϋh`Ӑ_lb, fho+t'J\+ Sq]蓸ګN-~O*@ {`mo=k-n½5 Y^k?~n㴤!+SJ|6epy'#zt%\*gACC x=x?A]s<Cj Ƙ:)w9*|x{2tm|_Vdu: /KfaI;XC|L-ag(ƻ`N%P>&Eb1M>kʯiXLSf qã?6V)z7̂g=UB xTd@‚?AVrSl[Ic8 r^}J@vz>x_s]5+ uZ=dRl}^I.<}1, n"&Y`ڭx/َ+`$\;JZf>ŏ-JHHG͆I}ЅeYs3^QK{N3t6JݏfҺJ k3GkrϋWyD/p3 Z,7RG&5Y Bن"vSD9 IBu7>ZAw|%訫-d$#%8 jqhzE-;vLSo+EEs*r)32!j&#?-؂'PW'F4 R2g S_FqCL>SȏoDzb=)ZQxZo֝0 s]Cq.p*}G]> ~:O],O6f s'SP K[q"++Y[utfU8ۛ#AT5 zŊs>.J/mj.>yX6F8v%>Pl}!_e:{{Yu̥ۭ/w"}t w0va(~m,U tj8~+zȺ}|]/!caeYrq13 >>'I+E\IxwL_`ٸm$~~SQFZfOyLac ;ucې>PkLu~(n܁s>%!KuwL馋 BC+^WK!j;i,:p1 E\e'7Ս?࠷,rc0=J*pZ㑫W^nNo\#w_BYծmus'oTg/ _v`k%~9mpeO ,yyqw"(j޸9+K_܍P6j\ȖÆ_'&-9i'GW`c}L~ܟpZ>X7т\jt`uٿuС;όjB;<~/yl<q"ri+fAq4tn6bqAKUGX {7bsmR'A1/=Zh߻7\c¼]M*ĉ8t֫cԩ[ "" )(@\7rTU4a_Z:E6^_ݏ-CPX ?%].6kוt :B$ݔ4QZux{%ftJ19nmX= E`P}d6<)w{dIS9B,# w`ײ{Ypໍx]& mY~MrTE=x|rْEyYߕ1j &;ŁuX;7[u :fc&?؅ì\"a'[׫&ήY^Bv"Pt{$BT0;h] S'&ᥗ߉znbІ,DBd(b`TMX%? o鋺a܂=.6fGc]f ըB]3 eo=lم#A&e1X諌,ny)kyĜ_qp)``H\E}y?﹅XN*e쟤W1ԙ'ѩЬ6x}8gۢcÒ{5ab/al$u yȽE k7TiP ł;\Cna߿xqbТȿwFT>_CPx4jFcHm<ܷ_ꃘWW藫Hb _436α(r߉^=¡}t McaWY\&D#'5 )rgNqy)N N0o5nCplD}aYު/0K9wFuOb*l.LH9, ^anu +ؕ ~cWo-zE]`˯"Y*bm84;v4wp@;VںrbqRRY(sML0w6SGsz#]?'$g hF`6Ծ=av~{RgdbU_`aplדǞt nu|X^Bv`VY~](zH?MppI_kUKHݡ\#^V[ߝBaC~\vҍ[vY1ޘާ,cɛ:غi*jE%@a [^({Тi}dť$KKUUS->`nvl'1b4Y^ #5M_iav+X=OOUv0 'b+?W:2sJCII/6Ǻٮxt6ELVי.Fd~)ұaW|)>ñVS ؄nCFmaΛe8wf Z8䦧Qx޸-oe;yKNb-SێC4YcXcV(ɬ,̮E_9/v}NU3[~["#N8g9jDݥqNM6ܔ5B _LՕwVvovXhl U1W"6WKQhGݭ یEV/i5K#J4fe0L綡}e]":k(̛|[MB\U| [\p0iZ[wأo!'yꭰvXBνXe:g*oΡ6[i>:LQUK_{LUضo)OnZv.`YT aŊѢm]¹_Ranw9–_9.v^yzñj* 6p\;\F{ 5G* C_#Z=f~4F[cްfb$ 8&" "6+rK zhۮ*.|` {COhgV'kςۤctkF,R2xl-{Q^WvXA6҇m]aVLuFb܌dr,I!h]x۴uA'A~PʋdUhrj:¼&Fukv[:e4NA t2{M=obx?;\ۏvVJ&]t9co~Ta3i+~=6ǻ/;18njJR)i)WK΃,YA!|6gݤV4o;I??RX9\P\v4 C]qwزrӪX frؗ^mKڥgk3}F;mtqBŇǞ|L0旌9+czzQ{ffǥlěpu0"1E}4p6p ll,*l//Ʋ аYg8tE!՚R4O IX*:tC[ UT,K_Hl ˖QҋINho0k/صGֆlҼ3BGӰ}.0.dFcT,FhɘѓEp7I;Ԭ1hȷ!c\QGbY-p0*/y۾+"`P Y1X~tCqzQIXڰem"(,,wIaCE:;X7GgXd Xd=Y9Qo!2,_0Ulz` #\X96N0@ X4Tq2ұ[Rt:ݎFF%Z Ka HQi BbNPI)i4м@t¸,>8vc˅~ ;}!Ū! V$#B)2Kc"X 'WUs8G۹{4Pu0p&|,+&zC=D}WMIyJ\Ň)G p ˏl+GcxGTv? 6w\C)t+??@_`>lNRTn^ݑg0|30g5ͤ_B!okp"#̓saxt|Xfr=BB9`P|}@̞ Ì`6Z< q#vp yUJ5 W.}[kr3nӫ%[w⧟|tH WHLo8+ck](W7* DD SK1ՆFZ5Z=4`oY W!JOGZb0gdϾS+ºs$x^ŒegP| Na=&\kǾw8EE/N,-y ʴ&nYiB!Jz,8g[Z@3X2!Äw~u:(6Tds F/EdМʥ ^0 MJ!$=X9+v?Ƈh4FSv1jە?(Ƈ''KCXMZ4EC1SST{l[(L|-?^Q7 `@6~UAVtj?v*qF0,ly 5TՑ7@De*=UE+LBOI}p3 *UQb yW`8r$gjD~B!Bȿu1`6:& kOHUB@FǵS [Gb+T Ò3nm+,CS nn[PWEIHVs31ԹtEo݀e>wVC^4.ق1S> zǍ`-ؽN> GFP*HCf [)tP];5zc*tI;kKeO1t;~a+MB!B!*_4'5+fMuM~4u\{A> wA \FڃEpǏb6TYpLwGס:`V0ςAVlp?0 3ݍq`<4BqVـؾqbLC@_WǼsm=,tطr>JNN&(K6wֳpmzSJ4`ZkcD@815XjB!BmD1rx4cM-k#C]+#V%V-:ҀQsOl m]hhw0RnabVb=JJ8 ȱֲ'P邙m2ɯ 8t*,ܩ6ԡzTQ iw/,CIMIxvZ 0B!BȿW?˱H$LG$a/ vbHk,𕊎Q>ul$Te#m|6#2Q~gXy+oٍ_GeR#>"ѤӔ}bs*6ςSIt<[O݂gx!B!e"E~]b0+́X"lQ8z0ZF_z:ٰ/,CLNarT3v``GD='Χ‡B!Bm>hVRrQ3cb)j)LW3F ue 3Jb&v5#hɊ\߭&VK!1}Ϛ(7wxRLcP@D~[r\ި!z ]/ z5^ B!BW|n?=} E#CU6GNX3 ?Wן#(.bNV-ۡ05gLª\5BrX:ީ5T)[AG/\BJ ]YsIFmkup>B:4=߯- 3}kyc!fڡLB!B!u4xX̿o FݔAɭZbB!B!feMԯ[ Z_?R_b#1dՄ!B!-Jox j׷Cu-a޷W_!FdQF"B!_uA3!B!?XM!B!OB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BQfGRD$e 3pd$#!=bNw#4CD-<{"ab{W0" /CH0YOy#O&"z0O~XD~<|a)((u|y7SX x ʶ XȈOG鿏␑1'.@RT8YI!BW"?´L[7&b?OX<9 qa8-xq1vgksY)H(I *&H\U'bd !>I I"׺e$~?>٩ H4E|!i´LGO/!Gҕx%LV"oX!*9*"%=_}rgZ>M~/ _(;~O}6n=tG|Ԩ@;nݿto^~B 4c!V$=W/ǶSۼHNvO6+3saÝY^}iEHKJGW"V&d'~,vMvퟡ8w=g~1']$Mq^Y;?i%ÓȐk%Ewȕ۠b] mJe8)U'[bB'Agߎ94 S^ U2Q2L4u=^%_ Ӽ =kv]F0Egzoml2*\,Vb0 =4ŒdYhh]GFiЫM[t tF=ѽs -,-谵Xo<ʵX9I.paud>߄{_ Ӽ,0gͱb\| "֨‚GEJ:{g{w_^b循(苽?m';0 tYqMXrCgXf/Pbb1+_e5봆+LʦJy:‘ eh#}+:0wv V6 _IWZ!(<bZ7Wt ?O ,l[kgbO4O6Mc?]݊/`ޒ_}޿sz©~%,v}m(&,k;KN]eBɷtSʂژ1h _±DTQ@9?YE\Ǽ?CݭHCU83ڶm&&Z}$a)WT|ǡX$XRvM_|`ˈD()b_♲weU\X>✤;JHJ?caq15Tm`)zb~˔lQ6in!L# /^OHEuʝlyiHάRa4.5ubd!b-"ji_ e]mkaf*WeLKEz_) o#P_M¡$+ZVùҳ ڣxwӭhn;WwNliY#,͔UTJ2誸Y)Y,% r _`hPaSZ,QxmԳʈmNɬШ(_X~ɩxI~)kBʶ/r4#f2בE_SREHOI(IloQ\TT#-# .XeVfq<80Y ~J+qN$fjT壢JˣBr0W(AJj"Z\XXV~)feldQ2pl=i}9e%dՅREHKMFPHrW+؈ q ZzXx%vu^&JXO,m/bi"\y˖`Ov')`靌,d#7'[(O$RݿXT&ax 5~Ǭ)?"[pElRV(/#zQyY"vKTQHc`^srP>pl}'_?`+f<&mrb}`{\]?Uu*LhYnF>S2?:dKls>O0绹Xv'=1U܉X"dTz<9iV"O%HEyl2+ EGNV[!)󩔈k7YZK)_ ;=Y{o7B*f5+'E·K.޿C-'u!;/K)~ViHl9iPVC復ފعxib,O Me #MIH P.TeJ"ki֬ C5LJB$VȈ`` e֐ z6koJ4hU-"d P*$,DGD 8<q|wV upž>a,BFPKCDh(NXH_*|Ŷ!+> C/E,/PC*,7->P$3"bB)7gm d CBj#%Tu$8{ smV>ق0-"[e=4y8EVOކolAGq&#at ᅦf=X˖|=14J򿃣O囸y: an N=CTT"^E=C苓wK<{EiU~N1 .b,|VR:hԨ1if} . Q9eg$z#J̨^ p ]i~nh±OǙ+wAVvB3i8}!=71@cGshJ?,ϯT$ۏ,0+|}~`6E]ٷy*\8q &4/أ2!-Rp,A醉 vgp;vPCYд;'qjNsd" L`c[5|Rs 攗s? -k'Os~GM˟5">?chKo} QZ(n}7XUVl[`ܧ-V6x]G|aNNHN;ƭ` WOđx:5  E{_xᙻD&MP#NCzw֧xU@ F=O+pUMݣl[/⑻x5c[k> p2V>WabY_O`סxz%~{fv+pn~dڶA}=a$Wlo98:0OF]{rf?n+UlQKV 8v<\s9ñUCV!Vf{DyOp#ƽ3Gp\}ĤlM4z w^<ã[ϐe3߳ani*_w}{>10G Nȷj2'lL^R&ťEg0:<~ l4pLH|GA? ùVT&gq}~Pg jާp'4'~݇UamY |X솺]cq'7b8ʟyC ;6PG |~/6HN-발g`x'ީFd37@+j x^mW%U%|xz:X w4tq;ƴbe$NF\WoNfm1oհU Ĺx.ki!;ΜdegS +qj\ ㈁`u$;OƩqU=Bf 4cز4>""(}ߡSKTW/#زxyгsFmM䟢z"vnI B2.Y:Ѥ-E"eV8#.! E81mdW ^> {Df-ԇJZ V"!10k;Ve#,0q"C4jbՑ]1KoH,&UP,*VxE=Uaۈ `p*:+ewm Sö_OBaV.( }M\vm$GJןv0c?s0G g̚8 c󹒾J(˅s_hs"?/o]$(0pQ0grmF-`.j5׮ -FNqթqe+}nu”!M/}yDxi9@?Y`Hj0Eq{&]UUugDׁ1obWn'?>ǖp>'Oaxy- }rH<>ul:p ̛vD.MQK_ATF . #ewhX ̐>\ #edǂ"H|#V|^1i$RzMkOCv\ bQ!^>z3y67Ö!;q=g4=cQ;ۣ2٣Sp_Hű5+ #O~pf#N~>]Gf遵+OB|nb\{ƭZ”SRi`&6{,Y>ݾ9Vn ៞:5{#˖b)Fvd?G s]V0a*u4'ҀV5u| zq?Ʊ$\e8#} 7]w?.Me%rnO|14Kmt錄Y Ǿ_ʹ?Ro䦄pO^qK.4.CcOBaۦ?wpcX.X)Wͅ|faʖHy\.΋"JzsET1͹D `Í;q6psg=ٕ؝&]%C/i?!S7iO\tʋkה')~̭>*t[n~_;?0/L߈p$L⸔kr4jn\0]*!7С7w36uvI?1wp߸W]5[rD~'^&y^a*3{ӝ,ٌ2q.K؞q\Օ\Q gБsf\\Vҏd|}$Ҥ)w0YSrÎ2[Ѿ/w<n:\^\y9YVO6q<0\n\_wi&7rH?$q8pˎKNMZ 92rbs S,oߥ3E+/s85 7x[aJ^.w}JG/+ur#-sU\(x 2%ͫzk9b5K^6wspn[ҩzۿ/7==nݤi\@p7mRgȓptZ._ߍ=R~ڌK' >r?8ZYyŧdrmW? r워ϖmז#+-r~b%8gFͲ]BrtN=6k;z^ k1h%vTܻ3Qc %JOsv$zO﹜_$Ωfl糇8rƟ?ܶM[n\%mVnɭT/ ǽΑMÎsJ/Ȼ=gq.Lxivvq\ٙ\aKqm z.OQE(}.BzԆЛvIvջR*1kdԡ \UIz-HBb~[V+t 95Gȉ+ZTj5B'$CN']ֻ,Rm+tb,Y sGR$J.^֔b [z HnI3{c K;һs⢲V`XRz{Gl=G/|2S6./-Ņ?Sgpx&w:Yxr $xƫ7+Ns%wv9-ru@!Xm:Mt+J7B~*?A.?n54 TlLOay 7 8Ⱥ漃[j1lŊx,[HH㯩+cBS+S ,)}FJfsgmw, ]}R?Xv/!B~Yn=G(\68g%XֽlO 4& `Z6qHW~I ! WYc0|y|R{V)B3+~^fbɆZFíH"85m#(ä~<^ݵ1mSpHµûhh _}l"()3Uѡlr4Э_OX gZձrZZ<ƶYJz{cDAQI#B$;_HHF-% 1YڪC!1}v.DTx,W~0zrYCCD#,[H"р^U| T$lI>}"(]h(LY,.B0H4^eYl~24s? C+H-po+iW&vCK$G"O7`͵F/*Frd X63;?Q1H׫W瑞XRr,h֋ p DqlH<}?.CY`;Q$Q(P$f#X.M%X )YؓV陡Z4EaUKVcȗu*|)}*JȊ-D^b.QX Z\|4pC }jaRkTG5`V4( %%ʚ0n ccwjԄym|dK~Ÿ|d&6ʈfMr:WRVU5JIx?-5YYIUͭaanfn& Cm %rrX[ blru;bᎣc0r ңo&*![ 5cǁ߫%)&|/mN1^߃+ HGDTzؙ#Xb0 .ZNԉn;^Jkaa]GBcǖoׄkJ!xUDUL6?KHG+!ku6f+ ~[ i\.LY hm+4% ZfbfV?dJ3 >W-QNkHe+9J56_Ai{|(LRaq(Ʀ! Ю?m*g!)BW -㺰[Y=FYYXt3އ5\h--E ljIDATOdş%q]k/!׺n?˫y^r㏏X@,kUT) [F>}|DD}Y-q2_ YJIO ֦G[_Rg'G`jbƚ{4ObI/k&/z u%ѠB^-BMd}4D5Vl|L A;86=>nڎ!xEW^cj:V8a6zTa;ϗrrXЫ#kx}~!N=CpNtu-Pe>YP-eu{ڶp\*>6CQ3qb3^U=d%gUr铙<%uH/iA6s/.\U5)ArL(w?RSe!KSY:*ājh,yǏTF6S!Ag5Gn\]gc5C{MP)EC676AJgCN蝻1ke[,Pb{Ō/,(Y!m,;|%aεՐWƌ,4L?]qL}gm1upS`z"l^\~Ɗ]QG0*-\X#5ҕw8|6_e5']?lXÒ?ժn*<J_tW^5Ax|%JLN@Pz46gI5G!B==UMuBϚ"wSbc!Q~{gG.=jMka7Ź,jV0ɿa(eO*VMQaJYpv/Vv5ٹpK| Ц^7d WN5G߀Z=p/NM Q.b"ZٮַG@GU%Yyȏ/:Œ_? lhڵj~ &: EYȘ)qRًi=&|[vXYUuʼno OPqoԭ|`ÿkrоhꊔIOUPW-YKA"_u4.4J])J|zCS1qc6eR?鍅rQUU%u4@Ake# w`c&L3&5ih"j, ~_ZANKL7S6k3Ц>[B: 44JkN~7h"|ۑëH?6OGz,t4X~/TRH.Prm+ӃpP>l&M heyKɮTTVEڦȎOpˊ@tQ`AO?QEK Iq4%:uQOO1% x!m$_]SAڨW>76 \+Q>cIa>B1Z%2?çT1!OSEYI}*w040,a C~g8p+ plg~CMX sQctyC7Oң}_ڱQ*?- D=RG@o' k6FNnpWyyx ,4di4h0aƱrۨTtP N!7´v[A}75j5{NHG}@hP"㳐a,?"UTd ,M*›'Pj &R gnKXrcv< o&KiQ€U@#yo|O =Ò6> za+f`OJӺjj&²qIY8nб,T;nHNx~e" ٲNĸH]ew MĿ:тHd.`ؼЕ/f9}dk1=Ŭy%!>L3Si1wA x(wdWK7K AGx'SW8]Y]aAfܽtQM`y@:GFLJ*E.kxK$/T&yV}FFl2@BNPVV4ܽJ' ݱlaiЖx fCew.r|xVR[v?g)͈cɨ PE&p_Vx\[<1xd_Y]`XFHJeǷE,SV ›&7O Ӄo`BLvd'm6{:4ofBl1'{0MYw1Ckl|2I|O ?mbӢX_GTKEؗ31D^zueYYEc%YOQ/(xW>Zqϝ/:s+6### /?bؒh[[l}<,bNc^YG~U= "*aHGl~9|Q 5?%X@bw9 mE*&]·t仇.&ӕPu1o3x]BAV ~oۼ+k$: -{OK.h@M3ʖd1RJ.ª.@0~LH3&8N\=% Ӂ6Ptic3h'|Q[ KB@մXET*m e6 U U7(k|@?*I l%u-tk¦lct Ѥ jiXᔖMʨZ!;> O+Aݺ5aXv9jZ035@qG[Q#Xʍve:(`d,DRFo uvr.- N.%͚3éQX4vn-phB]ޤ$^ɇ|c'g!m4sngq>t&Fz)^S,;u =ah;W4k!]/6wie*!+ w7iq2c=bp|wRPԂE}#!xz\A>Х3 ʳ= ďIUchN0*-; j ggv!`i j c[8fWY ptݏuS6Sө˳%/ l}C=Xpu!T׶v}&-I,:]aDk\_~KSJH`)G֜y₡Dse?b den7FXp:hȎ,4ܟ"˟S58=1i f̙3a*كH?qCRdj{ \DVЬ9:84znXYz^ WN|[Qm3~yQ_QC}<)Aߤ>jkP{m3t6a N܉v9+, ݃@ ѿk mA! 0elM:]T 6M`Zz @Hڰ1Frw#vرk>'> W(̇bҞxat,w.2X- 졌mOI3p>\13ZKiT]Ѱ[hk ,g#3[ay\/BnR)N*'UfI+kHd VVJij9qK J_OoW ms !g8}Wa-&Y@ ^Ed}5h2(waL0μWv;? =]\#B֟.a7 y,+3O]_yPpD$k_J ޘ`/|*_J IAmنwfMmfX?[{ {k̄BY(h&D8 ^χTl =:EgD癳aa Ի3-|'8!ab [a#1fDAg<%:-xOu!Cݳ !4S"!B7FA3!B!]'B!BB!BQfB!B!D !B!(h&B!BB!BQfB!B!D !B!(h&B!BB!BӢ`Cv"IENDB`backintime-1.5.2/common/doc-dev/2_weblate_setup_02.png000066400000000000000000001755741465446530500225700ustar00rootroot00000000000000PNG  IHDRU;MsRGBgAMA a pHYsMMgIDATx^`F $! Bw t4E# қ(RDzキ=wwgv y{;wsgB!_B!J!D !b( Bl%B W("B_ NT '$;BKC!WHc!RaRqq7&F rfA( J%&BKI 55q ))*5|b?-=&8;;d@x*oB @FFb!#3K r2 &ŊTp O@LloPO3x9z]ǽ,|xޔb30x"4NtD!u}RRA?rL&@Q-]R: }b#'WJ?T4mh,wGo Z+5eQGd;MG`XDNήşG_Ln$`3%0xz7'[ݢ@ILn6$+Qn'X?XqBi8p5JK".G}}QZ1Km.ē,rS`/;d%@UfyM*xu نKKe'0y8FIGDݾ#0yP+{0i2ijA¬JfZ\H5AV37By} 8x${(w;y9:7oIJIe]v9ΜIJsrq\c{a`:N,{.ѿj33bnsLiuzq̵5ǰFf3 *3. [4K`$/[00^"ϭb-+ue3]c,IacӇdfZY]X8yCx㓮ʄeee1cv֢pMUc(|weWbL+956%jL2]E6Y-VM,V\ʷbܒ%f.a7rs)ޭ۱{a}}դBv#E \nKcݽג^u%d$ !ϼpW 5BX(kŸpV@7ū8{2;jUjU*h0sHNIO;d/p `/!T). ? ri:`pupHމ+ۢBAčqCߪ^WGQO78e03/4o;-WoY ?uǬq]`M8 /h?zmn;LV ~nN|h1Nú.kxlNMM=5խ>m-^Q28O. ͇~5"]LzΌ,Q -R yx~Fp6PQ2-m.5!wVG!Y666Ă6 Qv)##ΊSPYhޫ~Ѵ?|=O B>Tjx)BbqvLKv }"KGw˙1kW=HSX)LC3O[0(!ۿvjeoTYx>OSFZDkZ g0k:5+Wg^*ttd-HOá711.܇Jhɔj83 %K*J7h&AR&5ҕCLč#8N6(ǔ_)痎5cP^EJv'rr5Z }:1oQHW!-_ߤ R'\<Ȑ$sB^~@#QxRh] s~..(퉊aexZz:222c5n/n޺C6aΒqF۲ 6ڠ(sE|v.b/ːQ{7f.ù شse`74m>J{BK8톺Uˣ .ۣ˼aU(ڹ#JTlcHdjr)*=ufoťGqOwjQ *Q'o+ˠp-f@3-߰ܙg$fက2eQz9xm,.4@(#ӣqiJ́[xUy8c!V 'tb'?_:8ΏB5xggQHMI,&ʄT6X? -q*v-u;#D*#bVWWԫSHIKDZgq)X_AづTt> B9rwCm> FYeup T4B۝[BJRXYx{NFbb2>{`0{^I)i8}nލ; /ZU+R>DR +2S-`u7闒 !KOIO'%A^. v!!bK$3B!H o!k !؊&OB!9!B^sB!6B!Q@! J!D !b( Bl%B BAB!6B!Q@! J!D !b( Bl%B BA2IՌs6`)H#!'Tb0>aJX !6iK_L7T*%~B\7/xECi !V=wp 457&R)!MQt 9[+"Blsظ0r:(m7Z+7q-b;fdb6KC7L&EEF+Bls'rp!/{L{wB !D !b( Bl%B BAB!6B!Q@! J!%ft:LfT3 z=4Z= bV9lL⓳^?JxvEM;mѽq8$sf9̟3TL&bwY0riH { ,axYwX8:g ^kڡI^060rsL BʔD٢Pެ$@ Sjq}r|1-#ؽ<.~E2 + ƐQ'"C*-)6~;+ER7>-10J`Q@)/=[|\e?m7gGwR/lާOk'lQO#.Z$-_/"_AU|% SdT=,Z8{}yp1:eP^g~Ͻ.usz?/EѢNpC*_wIM:w||/&`^~|: x G>`\͐v}FvigHفahu!.n_̧Jhe$_KJzG\}p&%d ݆.!!eW WA-FBPK% ' B!3o?FV-}3xI: *pUsff%tOa:=r{!A\O&hE+N0_zY\"—Yu4i!'zCg,xyhE΅ֺz>;f÷ϳY|Z|y|U ?cqq5}ALng*~~A\-l0Qߡ{Qiw= Guf_| 2¶|yri`<5dIB|B"27\7dDh^V+PTx_}fK  >[5]|G U[*{HIG|z)&p뿴   >xrƩ1$J:ƂבcC!<06UfЩ~-Ԫ\rg4}|֝qykPM (W 4T.r\ڽn:b 놣E;"PYh[KJ4J~V2Ѽf-|N#ws ƻV9YH@D&P|0J=#?Ļ|hQ"  2Bh9`o_ou5"Ό!\ s3q' ;5Be#rd*T1)W3ժQxEju S;1<Vp4.g2&[<8CrZ*r|;DC胆k a3n\nbL,8C=H S¿dY4/f ,%TF{ كZxT*{HWDӊ-䆘tM<9QhU)4) _w7 ɸd䟝p7:A0dZ#tj!< 1%:~sx|P%̨@`rh_96Q$x6: $*Z-'M ЫS)ᎃg#W;LhZ-;FIWx9y?h`|.S  |=hPZ֮wZ+_ ixL 'z/8GUPASnTn.nPf'L]-uӥ#&ްd!IiE_tlBǃcc$PEg+j']<# {8AQ=}I:x#q ,)1.^D lj>y /F">O_o=-L18{<.]9$…pԛ8y(.h=Q]]qm8VD U6Rrep+`n@bln߀wX 9N NЦFʹX( ).?9*\pW/GƭH59ÖŐк>gb?_] '+c1˩qGa(PVG_ qܽ W cqf Zqr,˿Hy17qz$t3[;8}DFTl\/w0$ıIp/ {"zPSCn_zou ar wcͿCf#g(spO1#8woGy:[*w&jv"]u8}AOc(;'?Vγ0\9뎟,hl\PsٗmȜ6'SMcS dySo;2kΜ*4c:/9B<4$eߧ4b-YUXX!eW?P:2ic"[+ǖo%{( vK͙tMV|DQ)Ɍ`>..+R<5KeD `#gf4,,q'|:% |v: 20{b1^3c~4,N'Mӗ9lTڲΜPkɺ ;LӷfU?\ǎ'zeX\Z'I%|soo杙tɼsoۀ;!q օ4cy3SVgaUn݊{M~zv|w 2ƬwHvq\v!o,|Sw9RYl޷C0EɺRe?/3n l{um$aeϿsl5[xAlٵg*Hb?P&>(^s4iYpx+G]CY!تE3Y%YYذՇXa,00VamxN?ƺ}'،jř?+S%|vﻐ}Q{V9uJ.*05VqM{ vdᳮRY:uXUX2Ykuȓ|>Y*]6 ^U.|Gl16^9ϦvoJxzRndzw`|->bw3с{Yߐ&llX"h`Vj6| q݋U gUaY؅la~\M;/Ʌ{-kEQ?3مLa 6eVj-Vv-VZeV2^#Lؔ^RlY$j7ٳ R:~?tvcmY`o}}YY&Ƨ\y6OV}|dVLQb#fﳸϾ,U 7)Y?Vb+Uv/ e|Joua7^GW3rZ{XH1>ߢtՆl#L'C}76l _/{rv%J*|S:V ۍ|m[] A-|~.~!ј6;w>58rB4>N?eH^.|0ao;/-bӊcЫ}Iu1/>Lw9nQ2>mX9+j(qjTkzzp-ú&&\kG{ dac-BZu* yu+HM^ɻ#V[ß3e+0jЁS04Qa쬹f|QDC_b։$^Q}O, /V!FЭSyӣFw 90t)l~ ٙF C䯙lV_Ѥa/XtR\NZ=1m2]'xkrbz2LZ3r9¤f jv߶-3{ULja}P1E ܷ;4[[i;%a2(ęu%K#*o3?%j8K(Fk禊|Ū_aa!N@ըz_}ށB7X}ֵɡ0,Qy> ި]>y>Ƿ.eޙ_Ưגx,:".!8f-[o~CrƑMsl3KAyoTR%VU]qcTl(g|f?.9'phؼd vϱNڄ'hXCYa# A⇡pH} A;kA_u1aP3Da30 cV[7f ݾyB]X8%<+W#mq~("cU7L$X:UajDc ]eƥ5k˺ChIڷw,':tn>(Sݱظ$.^> X _AHW;`F3/酳VnlS 33/׉o dItJ$T^} 7Vz?X8[ vDž+)P/y}3Ԑspq-t'-ylv!YFb˸1bȣ^Ƅ~e9~9{t0o \9w3/3cEw0ܛ̙رpz<`oQ i51.[pz]dqwE/4.rE^&@NL::m@*~o1=0W2u FYዙy_QrSw`}G3gc䟱{mW1'XewaدqL},|P>ߚHDŽ7,bm'a͊`Cd5SjfrHp?,܉l9?ȠS>q17U'~źT-_ѓ`wy __ t{ݕkq<LCx(>G Ko_I+cPa(ͷע쀥8z c~(W6Z|`3"OB~ 1\:Ktnn\ݎ=?8*|e\Sı$ٽ c~UJlxȌ粵UUoŸ'.'cF鎰E_Ï!p^ұX|PVGbTJ@?mtrlY  Onvu{7e`#/L׋Xo* |<Phѩꕎ=±Ȏ~IaMgڢe)ac)QF98ˠӺr`*s=uJ /'t)H m7@x6Pv0Oc=J[w[⎻o].v<.&VܓN;G^Ϟy>m.\@Z4Y,Ya`k[> [c9Z_ ;Tˤ?^,JXaR~Jƭxނuo yH&ܭ W. jG8^jYb,0g8:AwPyt0w"q|g &x U|/7:AhRikW/C0_kvLȌڏ=.7- g@rrⴝJi]v~аsɎ B%a(Gu!G89D;p4oxp}'(p^Ti#{|Ðc4kë-_kܻv\;_f.mF\WWp]Vؘ<_6s&V?06y j {YQ0׎'Ύ3TvvpLTj`gu?vT;7~lpG`Xs}zyGQOw#Q %PN8PZ8?bzti&L:#@z$]~ aiyCwd\lſsTQA=ڡMXګ 2-ߗi,8cN.o5B.&G. 9Ҧ7  \ghA\}Sasrb=W-Kv2X< b91E™7 V|}TCXh H֤"ژ~}48; qg"\Yu.' Ɩ|g^O2KvBMg>9c0wx pjgo zKGsIw{gC0)u  KZhs2sbA 4džx'-{r]D}15ߗxT ~<Ӓ1JH-Ǿ gbO1kS5ϧ,Ae<>~ 1i6q(*ôQC0pw$UxB 6M!#xzs̝Er<(yVf/Hc>.G[g)PM&?N~u!^+@x g+I|OaqO @(:t:y aF>fs4z;>2&5zWm: y4 :AHqzjzw Mu  OЈ{mwn ^p͗G%ZY'yB ˼rm0j1*vi 33{Z.(3 WQItx DpicLr哻 =9n<՛ݢfAM&+CBw#yE?QGjy7]zD=^Т-Fz"rX㙭]BȷoUMwS5)̼S',Pxc5o-'6Q5aBnv.k#H uJ;KHZ}àIr^$ {x~CY+ty@cg>?9pSoe+ؒI%e,'n?T|3=,JjsZ?|{I=z#?@YrX'^A^e$y0kw2o=~&$&S^!X=im=u_JEծ pT|+P`rLOGw?q7uIEv aM?_|QĨ//vx  .ɷY\z<$THD!{p1-u1c`Hpbp3r_=%Ç%`ߩq5cW/´ةf"mFcE׊p , E",wNsbɰ!AV 2}.R/lP؟pwO"2/ ynmG>\qc*ã ΃;vIzp 6AY.=H|Vj};o3 &R,3ec1w.4:xy9A}W5 2oz|zu ^/3Mۭ]+\/•wT9`d>ObLx'x( 'uzN8vaN,bHp?kn/ovP#v)l{_9C@ީ.~981Fbp`xbo1j|[` J-Mg?LYgX uzX°q /QTf>MX?{.V'uwVmOѷuZϦ߳p{8T_=1+Cf>19SE;>6a*PU9O^wpǰ1xx-6FI)=ܑ Jt)8h2VTn7owC/g’g6,j;8仟V)=qz+a!y| ./lL:l5 >Bs1Kb2N²BV®B:O|};&p(Qk׬F8=>ZJup֣Wq(Kߗ#a2L§=oAU絋p/D?0rms x ++GzAvVr%-S1&AR'.G^xң*-.MQ~4^ݪ1~lQ?r5qW )ktGPn@~kr,#Cҳ쓮cl٠>}##=KllգoXkq&h~5Ɗ<}|u.Y#jE,P;{GYڨӸ3~/ZN>L(Bt:e ު]:)a⟇N%⍹Q=꜆vk= MbO&Eha;gV.X(Ads)P;<՛}J^ 3h}?B.͐ԩUիĤrt5 =ɡx;|27*N`hڨ׼O.>oȫf>‹mZ؅ @[#+&[c9_yKb\AlP d>7bMto`L `2.oRt j2[kTCl;8w|֟ʀOtT%]8\O0ѧRNc8h= HRZu5oIg,h\8w+^3Qf5)煬+X:z 'ujBQX{ 9Jow2P~[t%Yu'F;H-Fa.[TBÒˀ[gcy\g0"5,ٰ 'ogYX5{'NĵTdr|PQt޹Y oE:^ņcJǛH߷ϣ \żڂ؋p""Q|gHc]z;*廊qރ'Rr7l0G5qԬ*%c%+'O]yeto&cԋP9 uƍMgfñpkV#yt8zJN [^2%CЮaK/INq$ k(YN_BmPri<܀ GpZˠۿ|:(SX-i8SsAXs)[,INi>jϻM$$<W԰W+a1D3h;3Uvj x}#ϸM[vU o=NH/<VJ/&ᑬu`&hx -Q! A\vkZ5r9_6mZ>=:rB+l .vb B÷B!J"Ϸ"Ѵr\|ʒee|(,W\G"@x:8bpS_xĮO_8)lO3_?v[BLZea ۃ'JJTÑOWX'#AHyT|VZ k:BP)JLyΗ]y:-o,@ q7(;b?_W5T?+a#ܮ(S(x!HFusr|tZPƗ_ mmx<@(y"d'x Iyă'Ly_~B]#AZ?aX{Ux ^/<Ӟ={=<:)"y::X5 ' |ZxC'u'}N.j<, >vyݧ](_ IIH>u~/nWrtrB!,\p`W|oK--|yE5_>ax!lTp޼[V\%|䒏DI|ӯX<1Py&?'ߧ\yy qAl~Go{_JC8*xyizV2 @h^\kuSy~и~Of玊5CJ/e0ok2wn=zS.؂Wz W͐{QCn6R1r p+܀.M=?sy#/gRlwmzw X2R/7  )ؾk?8#ц>bd عFpIJTJ(/7!)( Bl%B BAB!6B!Q@! J!D !b;xBpBs'?@y _lG!=wP&T*5%Lf3<<)Bls'CAlP"@DLfrV}4YYCUs,%"n ;#oSք\Bn<7AIU/:=?Ĕ4By1?OzեRB{B!o@!J!D !b( Bl%B BAB!6B!Q@! J!D !b( Bl%B BAB!6B!Q@! J!D !b( Bl%B BAB!6B!Q@!ؠ7`Y8uDTD!C8OlruUWu%J9]?7G !LLdeav(ogTeXV F B {{;8;;IE!ĖXpi*~zޮj1X, 2Ξhc F *sqF둠MC/i?ŞS4p&_N*ow$B7d22J*n.(T ;!؂`ݘ{L rzd%"Lm枓*sqΗ$9H=ayoFNN.<¯y2`FBR2\D_B! U\1h,bUq5r.iqĥEVH87X.'K3WQ|%[8y-FJW gM8Q0e(HvF( )꟰XJ=[=ÊNzUh5TSu\>O!V ҕ½눌KAf.Pÿlt$_8hp8q*dWuj% zjO?ԨVE Tݳ8 #TsBZPD%|̍[wwr9|y*[*ddfBV iu7 ٹP*T1 n! s;c?O ݳ vdDŽۛYצřv(rk[Sv#~ceJgN~e[P V!lLq ,lҚ-.29{kV&s eCWlЂ],(UNviˊȜ0"!erk5s (+4bek6ee2{0ְ2vOz#zsFߴlXP ̫TUXC} c@֬a}Q sc%kr-?b+ΧZ0 =j .> *<+3E>eC+2*̫\#sfX;eѪ._PϽlC֐|̓RR'َYR).؟#'N\XKWٮ}ٱSg2B!}.*ZRc[܄.^ Ĉ>?z4+fs jysc%W]=o>ꏋnnpwuOxn ށ+z?t=2T} ~,VczKY MwA6yۭ롴jcǭѺ~Ҍm+~)c|PEeNG` OBD3'rdFǨ[)  .hvGd0o**vhvpR{fI3N9 ,Ϋ9{x.nO՘g:m߱+jV ?v=ҥߕk7خ}XuYB!m/~@>VM:o7^BkaTm;Gu*0hhJÂ訫paP'Etg A[cW`1a@2 \M8m o H8eJw|rv'WϤ3db.jƓ&Y.)2bKՀR'@dYx2Rg;%_aY`go̧/W%A/I1P;Tֻ]HԨ`ʹTVbF\!TjGO嫼N*RԐdH%B^<ALg_?ߏŬIcÔoԏQB|,SxP$ȇB> ARPl(ꄗEx/pӈܟֽ>*0\X_K/LBџYX}#N=[kQ<$Ѵ "RXBry ­&!0Ñwez AeQjYe{W N`瓟[97 !7ڋ'<<Ѡݻӳ+zw}U3x+\7uv"2o,HN>7ʑ B;],ÚK1[mj` 1/VFH&eЅbvLO\ >?vޅ&9~Z:v}G!I\BvJ5ܽ}φYXsb\]?@Ƴ;; n!xR! ω@!6Qx1><x=‘;z j#!iƁmp#لVc_elP),nZ;_b-H⩓/ANn3gcgp5*o`k8b"gФiTVN_ x}XTG1*<@NT2%iB׋%&,2s4 `f#43<i/ځHܳ-E{Q(/Xo/lL\HEQ)33:za-\~}yDE&A3dOW/9hOx, &hJ#>@aeš&1d푝{xvlXgu9BYV.v&,i7|>5|ohǪX!Lo/OUJ$"9Qhdr8d{htb:Bڋ=( j G.6glFVFaiC I7adho <> FG] _/-@6? 1us3JCݲX#D[ Z&JY"ĉ[0-p/Qo5? ӛbA!5ѨKQWcm5,֠ȍ/!MV< q:mO8(KE~@{{'V˄B+غ=8Nު9*yeD:9}2>!h^0 qAϏouRxB`gF!d(g@PB2|y@!b +sa(k6U[ l6x$>NpB?+'hu:q|(OBlkoF#q@ {f@魟}7Kbp cR*.L!Knoø [?LRX'/b6#95B*pAɛ !,z-vB7B!F !b( Bl%B BAB!6B!Q@!ؠ~Pzz:+!B>W&@!Z&cб'Zh_by/LFbNJ.,U3TX/$`q(,B!W@&eGeǵ;y8v8.NQ*ϢMA38v8_A*w.X\=;WwR5أP@)t5N. daɽ_33̘>N% 3rxsv6P`=Ec1qҏyI>;Jor񪋴e 9S`(ު?͙߆]2|w t 4+~X5FA~ ^;09RB!)^Yr(gw AXj>W= #IRҘ`9'Q*[1RIu5BjApwDp)K!vIx:±XStT'.-8h1EzP8Aem?@6o$!yE l7;WZ]_>_Fuunٳdֹشj!M-mgG֐yiBPg֗ɕsX[W,d<ƔP#5|GRqC8gQJ%~y\xT8wAnbeX`-ZEw!6-Yt Bȳ@cĕ;p[Wl -݁BzЫ_+\x _*xJ;oYMA.dKc_w9A`1~M[D AOxvj8z~[SG!<&'[*'j< nƼh4 MPl\L0 bR⑚%<̒xW7ƶs OUo=?[K* !@cL'WpB ᛯk/җ@y6=ـl$0hڶ-jyyo^}Uъ#( Bl%B BAB!6B!Q@! J!D !b( Bls'r`B!;prrBvv4D!7s'EAbb"B!Fׯ_'%BlFzz:z= BP/B!GB!6B!Q@! J!D !b( Bl%B BAo\`ka0Y!B!/XlЋzH+م-hQhB!|p$&~<J ,p-\ {pIՐH֜A3T1|X]=RB!<};NC[F#EW4W4t2.^\/-[r0 B JG'UAaR z=+Ai,l(_&vҰ.72+QVm.s1z-s3H%V&}.O3j|d6`Ft>] Xf#7/o'fc5\> _lͣuIͧS#B^{߮ a \*Whi, {|Zu[]{u0r%id~!.9cf(~.A:Oni,tyF0d1I rviO27ta+ ˾@?`8X6=Ö@đ]T -{a{R%N;G:aR.xzxE#VVC֜A?~o5GHxSj b , Iv_Nh4l]'!/.TYQqA*āT @%0CƁ KpT7rxȡ0cׂ)ؔT OǦu c[|HlM+=U3Z ]e#[Xy(V_yW/‰k]o"ePp29Laֶxg4vfaȑpGð`|l_9mQېwaOV.p|xr*v$cX;#Ҷ ͅнx+z9qJEDIB? t|~XRM؈i#YǧPKu/X8dfg bEN {-DBj>s/H&6mUeJ,lِF]L+ }281m]Nl՝,$\~Z/m:̑߰bcb 93>;q4>菛0dÛVd'M1# :'jև|B zIgDb'9mj?PXzOe[`>@h8 ёב('S3dgGmǨitl!5O#7C6Ed,JYO;HwK "$bOР L=DStoȃQP<$ I>ZD(BနӻXb,_)#V}8ZB!KJb}YXDYa?aߺ?c~)0i;xI*)ȀCQNXeV~?횶BN0ˣ]=wl~Gz=V/̤y},Lčq }5ڄ?n瑷z_[&[?!aI5t%\4y2v nCfc{cìfy ՀM(ܾ5|FIRb^o. 8T@\o^Ct l9f31濖,0G=F|V>1;j7TAp.Ɵ?G?ߑB!b'~D_Iʌ<ףrXt-ձҥG|7Zu e!,0?e Daq;ipeV vԹ37-.p^29|JbqwTSb]t(M_QQ9, 9'+r}d/?{кy R$}:n[ՃOKUBn39 S>eJ7h/%LS3ڨHka1(neF"axjQ!ҵ*.\νyQKć@'=B?oIƯ~nȜF*{’ՕB!6%uz!/r=f̻~(B!IePsˊێ#t}+>/- Blkr @OᲾ:T/&H=suQ-Y) BlkB!Uz}.B!䕡BAB!6B!Q@! J!D !b( Bl%B BAB!6B!Q@! J!D !b( Bl%B BAB!6B!Q@! J!D !b( Bl%B 1NzbBʭk~/2'OU4U6md!:x vq Jbhټ B^(\',!B3\;-S`WZ2hBɚMp:ܴ?"F .n[1oHU8QvLݘ8 x{j^UAjQt0L3Rp-yB@!/V8Pz {r{OGH_#BMՆhm59˜_k?6]xWCASnp/YtmeQB=ԃo5# Ɉ'FPB K8r/PkdG \%By^Mc[&aL6@kc5Z|T[ Mj#n:*NxO쐗W&ˇ 1ې98o5F pQxwD(^H!X,x5E1V´P.vJKh !W_O % ?0gl?-Wjt\A/X`&V%sf>| OK-^rD@BԷ)p"'OL5kwNIGdf1x,lh}$'7!%R(_6NB_+ww7AP~B!?Hп$W~'sVCdevmH^;JqSiQS፪NiL5~~b'w8`1F#7e=~:.NP*RUvN.a6>tweHBȫb cOd33:ԭ -oHU.(W,԰vz`F+x&P8= ZmNnހ=;h/'+JU ܛ/ᄺn{`q'NCͤ ?3oxW A\}܍F'u:( ^J!W/_8A\df`osPi2Qr,];Wu:K'bKRdr5//pA/#f$ aE A:=\1ome_h}Y/ /EÛe`ĩ%sdJi͗[oTyn0G|S 0sBb @h s}ly);k'b LAd2.4t|1a0$q):>u9g۾Xi=lފl%gFG/APGMF`XpEQI[B!;xF#|}}_dB!i;}6%Bys=y|Zm-\ Bț.S'B'!b( Bl%B BAB!6B!Q@!ؠW^߁ǑdToaXYOL>Aq~Rw|TIF`LTk!v%1q,[=Ԣ0`tG"6|;p8afEF}d-v x߼Æt g#*;PLXʗ( pBÜm#ŧܣaԏ?cɄV>鎊:d6Lᇣ1Ozg|BD*BSdd$ʒ^LlpwRYf-asv:[_Jcv߷/WbfkcLlIfgġ۾aߟ."c{J%l¡i xT==uۗ];־.KVϚ^_gdG}Z~V3Ěv$KWfZg{-^ezaI !]dCX)7iتjj1 Wo~Cqy4:t \6yP u#9.$H%c0ݚt Mƪ"7-0@D Zŭ8BJSOOąDLdZ9 s6ǣyߏ(T* Go᤽),-.Ca[ .ng"c8/ `6+ko k}DH&h3>}}?i@vH"5/A8!<e叕2^iȩ@4V35Ɋ=@̿eB!6՜0`&-;ah.bft6:ٌEC=.5Wk匝@ΝEHtR~=y)BÜcNI[7syJ#B\&cg(AOEFE)|7<,==^Jr *^^S*إe ;tFϡq8WHU!@& K7oބ/\\\B!^aB!.( Bl%B BAB!6B!Q@! J!D !b( Bl%B zz^"Bț///` C5'~)B &qX5g18t:$>,X(i#7XJΑ :ë*o݇^'~ш[㯧aA  ?-XY?-f w\DTjT3kpqr;~ oBzN{/\d¤1qhKap_SNЊUlܺ{cWLbl$ n-hc`ĕz *%Tglw$X*aԴeD6?0jQiXݳ~Ĕ)`& rfzX Lbk[ȯ3\ 5rTM ܸ{FPj)~!*VɡP>k" ~EQ߄khʕvprrOS=3 |дs_B*vpqrK~' pqTKT* t ƌQ#cOЀmgC#*>ߓ^NNRŪ. ੦b;^,`QE8,8ǘu3?B&MT!_di|#> 3иvKϦw֣C(Q3xci}QN#6OW?mGkl' V~ól9u4KTo6"V/pn4o!S'<~C>Fju3l>L ٳG/# ?C@)!FzgXz/QWEZpf,r̼ybbn VO5LvdV2}7 Tv}T4R݊#uye+ENñ9 n힌-!f{92;>}/c ǒ/iiH׶LF+ ] X<'jݮ;R|ϺúlZ.2LPF|8{+^IN_t3áCQZm0ZSCKNedʩz(ߦ'Y໓C{C*QO8Bl `0b@+L* 2+bkX0}\ ‚[φ& 8}Kgq8:O/Zƚ0x@a6øk1e:} I?Hcų & 0#]ܺR*1] ;X_뿃yw4X1ޞ{M}-K?ztpn3o1f=>&a{S*JVR^BŐ˿F`fqѢoȕj 2Jz% 󅣰_aN|)|ݪU|]?|Sʍ?GØ=pgÐʹf'Xq'cHƱuKqOqx'Ghx;E}WC!^iG |ΎDMA ;xDrp庄d _EUm?~Upۣ}27-{~Zm1wZx>[K9 0|9 WNEY&F}6܆t`x 8k/0 z "wG,.I.qP _uZۅ Gص}#fu-[6l",GY۰le,kYp8*L8$l$j؁m} Y@-yfJX9ֿF6d)Kг=T``'~|`R(y9R۝) w7VBY4,2)m.c08FWeFCO+Q+[( X6YάTAl{I#٧? Fj9#$5#z1mD6_{E%{BRDaE"ERl * "w=$7{r:ކ_s{;̖Κ~gg0ر_͎}>y@i:?cؕ[Wم=1>f[ofٯtb}faZ!ӣ-zH҄={{ fJֲUSXooِKXAlS4u׀ '%C2 )WQArwت") ?Xi5Q{:.HuکYJӒ "S߾*5*ܣSlW֔`02]q `Q[g>3YX[zq հ߶[Z֪i{aQ^1톭_=+kR.M f?b!qd{ j: !f GLv/}=w}[X؀Wހ 9yb2`G@CrTA.%Anj>Hz[{^m&?w|7 E5PӐ+nP||h;~KߖJpPC-o]Q#׏IZo5%x2-?c^ߊe03hق QC^h_ +>ӸU|7kHxѸIwweP|*hB,T=t|Pɠ;~ۯP|ӌڲ @ զ/1iz]t{ x9z }>I2XLnC8K>?Xe|3 .n2\acg+DѨlԚvcìi= QD-iy(C =S0ic cVwY^ك8M?sK:OƍsGuG|n|F#z\"Th*PYBg0w+tMy*덝_šIqMk /7B᧏ac0zG뵰,XφGz螤Rƞ;e߲{XbO[pz7mD?) xmn`g0w{hO#޽/ kͺEQ=ԋFT|n7/ vWfg>[zu+`sї>}+r50[%S uscUt] <*-)Rn7!1 {U~F;?'=qnhKqאX捯"eǶ?JvK R$+{i[!H)쨀cߩqDrRU~TTw#?bbph Ƿ@S[iPXTuc:Mat$InR<Coi9JhZ [ECе:I)rcO ^jR {aӖ,Boc]FEȢ1O0|GTU YCws\T/6ٚ7g[czPO0KsBwn6g"U_Fz֐,8}?y[/yN!ron1 [.Ƕm;#xa &b /.9?L}7yxo+noWʶ5N;;zeh/zՕ2*]㧼QLaery0݋Gϡq% Y8$n`ϱXTr/0 ׏d·ec i|ۥIv}fmfB+a?x~5r)]ܗwXYh YHy8x}./[lAWdC+=VcK 1)By 2I5u:uP*diJ;[F⍉fQ~\Ѻ ۮ?>Y D1JX4Pt> ;oJi$49}&B.G<=g[ a\ ,D4,:ƚ[yg 7zb @~҄τ<B;-*WCzAEQ2_=;nSz(ONRAhu0ϒ+bPRY@(Ց" ztF(lLi #3 B.fB&^&|fʓ%E!yIS~m=`w͗&%bv)Fۻ yʈ<k@V}[w3Wpx[P&<6;wbaS (;vvZE)Fvz ֭cG_19lÚ?]/0D^ĺnɪ4z|0ǀ̵FKe, fu2;ZL^U>~'>>SbvGIζʬ|Mx[V~hҵ#\zkZ?0 v62+**ӴZ;u.D~ y<BOrEI[[K2 ު' |}7.ƌ!/Tҏ)+p**&cY7򒣰7qP\(JK%ey&cBLbꑅo#b͘0wM hc0^Pr/9jQ\\ V0kxo*VC}xVrQV \8j#2$拼 ^h~|!V⋙p-GU W_1ZABa5Y-&[Nَcrp& j| ѢSg8 Vpp5 "FY.V+f›װfmt%: g7b;q KF9P)-PϗM#<`4H y<¼BoOԦXu& zC<\BI:Ϣ|uӻVaWEM #zزr :5gC X*)\8B﾿ :vz t{sV#41 iGrNؼW [6Ib>bO~fpVQV͆hfi<5"#;mꡉ2#clUUԯVÿe zq/6}wEcgl;!ctrb\ &<M}!+G3P0|򲳑ǘ0w4P{| Nbأ{a[ GVF]-*WjQ/ a adeB ^R*`iDuB!00/!ۓ2iرt!_FA] }?/,B$E@Sub\La 'xs%`kkk+оf2( T 9AIt^^\M8 mg0q؇8pqi((H4BS$ ws4Myi!ҷ~ \+ Y> ڋy@a|;7%?e yy5( !“F=6xkWbl_|÷@y'7@Jf>V* "12hyQ{@o Z6F6Ыg h/ħmXrۃ?/D^ڣKgӑW.uR 5&!p>otF*]hߪ1ڷi][M&*Qv+/ցVJXY }zs<~K|d? T{>PYWFN-Бm&Bsھe)<wW ٹO%B^a@B!Ϸ'x#pDнK{tn MkW~@Co*4CH;/\Nd06F&lG=\=vҟcyXp fh)fz@S8z{PBfcߢudeD~ >HozP%a (T߭&*Вg_ e .[m#tv݋UԆx˝k1XBn%aBPB2R>\f.>T|uO?cďШd r{berG,!\! |{@uzyug#†0|ʏS[=𪏔 1ϛ^`߹ؽjϘeoNA [=g֛ñdaP$"+P 2%.mڅc<%F1Y[X*2C\ſYcPX9:NGNX+G#.GĮ3873jNŗ{vv4,[QP;!4FX<.]9b³]]ƨ_o*K{zeh h`+d8bɅosB)O +B!B![O芐yaA<-2xܱ ~9}<{O|wt.Ĵ?^I|i3 wFmB+NZ73!ú+hgU|9>Лw[ˬpA^̍Scбl$>psE92Rs TJ@jr&d qBH> ~#R7-{ 4W,a򄟱]d[{S񆿰幈J>}:- ۯpӔBh^?g|r=ފ6Br^>1c~ Rcʏp.N]ZR POyU⼄BoOrpr ֎X~E/+Pq;Tw>(#n8GE|*T[L/ =mFeLu#ޮ)} uD&Bo0BnmߪQwp 1p-:żkTwIäARu܈ˇ}IPlpE /2BPBhRF5߾*Re[vÆp+WHT*/,*LqmV<-?:7*-Cw3ѩk0*PVm47ǥT"vMxH+7p!;  jbT.5t\+V*pRU Ș8DFujՀ3z.].~޸~=( ddfVըPի !ؓ0q\Lt,ܸ]?+ˇ\.j D#5=Fhi!sEoDqCX75Ǎ[{BC/´ӮNS{/"<{ui R-maR *IȐt7FR=oŃ/#By>=w)9}O(*RCo01)oc}߻!<B!KOB!Q@!! !3D!b( BBB1CB!f_t]@VsBx 66EEEJ?L!cyyyHJJ?JWhBoz &&ez!Ǿ PӉSO!=S°?!BE'BBB1CB!fB! Q@!! !3D!b ?,Z[y)" GO#]+MfH,I kH\GgC+a\iø濥ۓ+ǝ.LPɹTg3 ݶ+3z#o#&='# < bkSzvf> ?'/YH)_ˋ_|fN^xq<|\: l9k)eܿq{4pA1I?'VJ\8T΃_d<\9}o=\KB^zn\ǡHARLnA5+³i]oiUuF@?|:}^Vɫ86Rp^,H=8}{RbQ"^7sp3vG]Xu< 2"dvx iyIIV$l[{U{&x?q0ϙa6z ԘEkñY#qfA6>ױغ0 W/ u%lc6:kZNV%s1Dt#Au\Jmm;T@p]"MUA~v.,QZOԯ4kt90{HmO͝Xv8UA:TuaعOׁp|:8bpo8U&Mj@Kr_CVQn-Z7{a~| x"Νo-i?J īHyj$:~ a8VUʆ a'`M{4mQieio a!ʵk98(]۠_c/)sZCɎh[^իV#ͱ%rDX=E3'/Mp9NjpwPN܁'/>Xqy)Jh관GU2lwHK]<jWu( ?~ÞwCjni!OpF" ?;["q8|.q^QK8u2fc׺֡(-!y*4>ߎs#X6SZ%WӂYۡj/AòS0]I`¥)w!'n&t[gli Q#g:Jsyv #Bu6?7C4&]j8N¥QoagG(t> v^ Há/Ц^QfXVniܽߐұɘd?r<}d)FQx"j||/r[8ؿ`(F *{ثr`?%68q)'S/`eM>evFhࢺ fbކkx; 4=ņ`DZH/s>?EXF[c/<  s/nLMEZp\8u '/I ӧq9\pwb+(0!\pg`70s(\&N bc^4qAօ8T|I^olAðT6}\#:gdJU0g}:u 45i8g.|غa|>pyiVG1n1p(֭&.@˘Dss'}cixƊoC%_N!˾qb8>7X`)<{i"oxi6N7Zֱ+f#RKg4-1vaCfmdYRPdHG6nI E#YYVL6;7&/,rOb g;eH)E`wfy]J)/nD֣Xv4qIL hY'pS  {u!x_`O\ȢLwUٰUW)mOǐؕtqR>&я-#ds>a]͎fJI\WǰKERl#7nI %n_ISmM؜-RY.,gw:б¬,VpL; v5KP6þϤxϟ&{{?b{ⴈ>xNJ`,X JwF֡;S2a˻~`;;v[#%]֬;%Z7?f-͚ z5l5g9]B?3{ z4Y=r P`@A yiˠաVn&*'NL8]%qWͻez%`>(3X{nR.h\V Ks^7Ԥ5nELo\,,·VgASvf14&0[Eť˹Z)LmnlEGE2"g2NoAt.xcK%FVȯc8眔‹Ѡ꫃PSwFL4F, ~j]a,; ')vNt)E` @Yr om|vxnvjlz_qU\ \ A@v~&`=8s-9F ~H\ub[M(83?rc׊d ,~8è[dE-?/'ŘO`Y8̜!@67o%=GoG^ KTsM=y>,~5c0k%}}(κ[n&{a[oɗˑf.;pb}2:gU< Լ(/8_jfoȻMQ~¶ mtn݄S 8>ܷ=Rݔ% c5<8)u$k0jL> _͜/g_cɦ]Ʉ ?SY ~nB&<#/%%(3v] FK%TRV]]S"}/BAMN4Rr [3: rbA|5}$j`-c. BybK`(Rȡ{)(`2\Q> )J:~Q^-;miQM)?b]C*&X.nDf!6u%O mn4ziNAV R^-_B#L1b8j^yE./G|̷se&e:f砰l!Of/ή@`h&`O0w{<:Kt鐓a<~Fc䈑W&~ Rqm0E3;6/-vtƍu7}>c bP'Gø8t4Ԗjԣ0?V09 7BfFDJ̋ Mmph>8tBf Z0# ԹGUc<!­}z5spmx0uP qػOD'a 6j >S6+  <15]!t/xXc1fpLn#mGbKҥK囄_~Ĵ%'P<(*)2^98b̈q#$tyJ6o`H,̝1j͏QA^n4z)!G[_[d#{:}?T[cؤ`qFqa'`U)"d£oooe@OuP꠲LUWO*[ZI} *8 3 .v>LCĽ[?i[:qLtC~ .ΰ*-LE. Txid*t({6s؁Cݿo( { qXߘ{.A՛8S?~.,FN;2 9:¶AJy&H?! 1yӺ6WǶŽ˽u-ʟ`& rݺ$L2 vcH])Id&áJ)ea{CP[  *4/4ByM-翀t: a>;Wbj@ \#%U1]7Ԫ.ùq3!Cځ-/YGK5Dh/ 6ͨ'<#70{ ެw1%'^N6 ;Z {6v'^՛q9.Za NnѲSolSoS5u&P㙟%NHwBs s),]W05#dr XZ]Byw/@9h2 X9 ByB!<- !3D!b( BBz@.{bB!Ϲ-MB!"==F!~@x^dd$Tn!' F⡑B!B!cBB1CB!fB! Q@!! !3D!b( BB/\܎cP,Mg莍Xr+JQJ~DG'M ,>kIb!DNB b$'&!+#]6"@'%<]/fC|,YO"_5^3h6 4É'p#eS~ęi_/Ɗ~,~6tDJ!g52Lk9RR>A1`d Y1a8s.YR0g(Ar'Lұ~GxkEi+y+MKaX'"LC t:(yCƼ(Ro;6e2-"/]EHbH:-^ڌ\ib^f`h'C2h'tƠyGa|L9??=ǯE9hl!)-CO@oPC@n|J-SzY YO2r@qA&|Sg;_&H צyy$WJ{u7-DQKĞhܪ"[݅uz$@hl$ 8} ŵL8{ɪ䔪G­8`8i2"q2, n(>N\x"dUm+ŊpYA)U) ěa]?Bx ܪVSFq+=0/nۃW\$e4"m*.=MCp+!vp`@FEʾ`y =mG ZgLDg K1xtyDzSאiakG3mFN>Cg`eke.sL8{NTU[/8:wSB5pcع.I>+8p35+!;?bW .Z|\=yٛ T똽:깘WqH0vH݅_@%l' ސ^l 뼳5b6gf۵SQyx8([9h٬xov Y<:a%QG`lsp%(ve^N*CpL~n"߮Z{FbXt6 Ei8w06n:y~{_FP|9wbðw*D.&iGf6] ڤc}I8솚CXE`˒ Цy-X >`c RQ^o~Z d{j"|u7^D]urnm/¥PlAB-޿ة`&f0=~>Bw@Ëa½MOESؼr|UUi}8 l*hZҐgbĢȗɐru'>J=^BM[)KN0F9V Dl¦S90qrkv. B{IOe/]ecͦO3焰?dk3·۲׆ҹ3XщũgG۳6`F1mk8f6ea؋XaTͬazl)B`o4XΙgC֯YR" a};9Tܦp8)8:n ˔wN| cfPb-gk؞ͻ#u1ەX$N3]!Ke0e!3~`,^/`^VPT,&o?:j.9k$#b/{U6jUX8um[^RIØϋsyn#/,b:1}!:+陰I(v.oGVt|'Q4<5mЀMLZjtdyk؉}Imf %e{uwi.=t;˴zr3X5Xpt,P Mu5ކ'E[>d~]f3/Ce %6c?Now)Da-ڱwJ7=ܞ8-a* jݚ% ʟSV wMc;4ְ;lKta׃u4EnƪUȂMة_`ul0EߎE,Bc8ޕ,އu_ɒ^-(P}= uQs?lpLL>sQuJ8xBvS)aX爚5e-ݤe;1%oo {x gbt7 ZXΗ"Déq#{`Ei+pƶ 6vv̹gfzloµTq:v+-B% ]zsS#b,HJ֣]p[ G,ʆ%׋.OYg#̭%ikĵ tPkuq=cpq]}PA8XaY2XC)fbkhQ'>kB@&eQFA>H9uUDfDaQ%̟*+LZd@a [9NI^LP脺5nTB\u)$~n +r k8(ac# '`o'o2ޭ,2խ_h|x _Ϲ~ƈ&v|Z(UEzA[/2RK,XH1S0\m%/\X<{?v{o622[xc 4 ׄL8/ Q)7F[aGOVkD\{ &N1M'f_#X|u:MX:/M[=O|  EE{0$ b<&\ :P]J13W^[ e|}Fn4< e7 ˍ=-)Ӿa0F</eYjʗiE !?ĀQ_aH}XC{O.؈/Ƭ˲UIŷQL4.b?&kD^cŒUh$\4ѺI5i&Vê^MF0{H7/cd$ 7=2޽4" guGؕRBy<ʂ~L7mg30]ۓ+=V6zcIWD1+OMD,gPVmo „OG55X-4Fڝ0e_g eC w]ky,}Ä Bd b Oj!nCG݆Ѫ=a)hCvV\죎G|!fә ÊOF:æ ',s?#W_)xV黃q6<n5o8o $^-6x)w%xF#F[{/|%/^6N\Qa(čxVCJXR`|T_yGx`Ыi<~yLPrï QJ)ھjx-<#to5=kdOOM®;]rX%#،6'⥩?b=оA TGAOo47R\PѶ57b@ώyܼ`maI ⁏M#A,{0D#ˢVљ*Wi!72XSJ(v2+n/maJa}WyD2^K$ ?tĽxtVXvQ]VWld(aazMr'gZyxqXԫ->-rmUӰfZVkޱñ;tx8?Ys\ΐ)58U #^R#q;&9g ;KXX(LDݻ]  ۜ]+(CfGMGЧ auqW2^$KqEK !ӧ\ř2 plM=Ws^(Qy"e9鏖ykdȼr ~; 10-B}iu4Q t+qt- 3y:dp |\WBfzYwԡ]#neY%ti᭍ǎeqn*.߉[@})Ǫz$a}9)bμE(l1 \e^g1kBpZ?UQXxGU(\[}֭܂s1|ۂ@B?LyĢCim$~A?>Ÿ[Wqllh R^lr{Q:}pt:<ޏs 9ב 7BrT6,}СeK\<t4JGQU,s?ރ"n_ZG}Jt9! mL;S#p+3O?hʼ*?N]5SwRܣ•T=|}}NH: W}ãˎ d+ȅN Ziʎ9Q!8y#Z(lѰSWq)-W#ͅV挶=;u:bFhC BHYByBZ CH8Is)B͓]%#3d ? /EImj9u +~_;R0}= .d酪qF>B_Dq IH{ϬŘ10`Hi\}0SOgJi&w##XwbZM!,"n!5=8<Bdt,\i.By=@ֱh7r#ּAFMAMހ47c/0}gk}}^_gƇmFcKA{lÂW0K鼱MDhUz8ObFo=nZQ$$&B׉ߋ:k#^yuF|EqQɈȇ rXl-`im %op~ *^.9spvìs ZuA^N֐mLD Kakt-,ʌYےlBJENDCN <~)pm)Q[ ֖|XA^&(LBTd,f#{|(amAܝHDƧ!G Vs~_VD_V:2:m`-D܉ARr ,Y5_[aለ぀|}큜<~U*KBy#Ow}ܯ9T-]J5Swl]l kefe.ܥsŬ}2JA[3ް\inƶbpʆ^s͜g)\>Ȧՙ{fՙҳ 3].>Mc>j6f_LKW+tf/akϥ9~5f՚weuZg^uz{6H6ض-7cM<̗fu5ըUY{2MX?g[oJ9YL<֡ Wm|K9+J^Ӈ8vZZܳO06{V-ŷFٽ*C,N ςXqYj ^c,e+'<Х:St`ߞ0%2KxM2s|yTgUmfyY*XY`zFLǺճ_`r'׿g1F,dR^̊ױ-_si+]S.-Љf;l񤷘Ti>-=YR7Fv2Z6٧SWgVYI|`0GN'N)Bv\۵0KLNRh4{X0OR !t1 P:|pk |2(|[ '$7>dL&Ⱦ mFt*1oX{ 0p@'4w3Rmf mBj ~q`c=eFׁD;oG_;|{ХCCmKmM!P pzivt,y60 BԹ2G > %>no]J+=.pq=]Lcgg{iT,-տ@!aɅ!27ڈ7PYPԪGkA* >`F#22ś !yT g)h3m)ί[٣ބ\.ABN,bF~-Lb)If瘾qpJ2&޲!7Qi pn/ L__jHykOB5">-ע QA}(*gĻg?N(`1·F> }W|ȉ<2 x[VƧ^VA-=&>9:q}1X7e8A#;Bm\Z;#֓UTʄI{bWB1>Xc=3s>Y3E}75 *yKŃ$3db?p;P]c´0sVe/y4ZTjn˷-QlTnx y`P' 6\ A][ϞX5u}Odt-wwp_TB 9? 1w>J!goaۍZ BPem;VFZڔ3xP[x<N'Sx@ժ *۪pu֛h7߬;8*>_|v̓`_5dj皞@#oU)ƞ7&<^NVA ?[hJވ cZ]U4ozŃ VxekO]?RXYC{׌ JKQƬ7` 1`;@hE&4<:/!lLaYڀZ[xLW kWח\- IɈkO/ XeppT1{^a_7!ul0pk,zj>@8-4Zcŧs?BDQa!/ B_ߍ77nڲ钰hXЦPLWE!/y^"4r:i65 7!h=lfMnMa|~%GVGyfjb~I/55g䍨8Njh·]޶IAMp1Z=uoɆ`G\hj {B_|araA>-!X &3ٹrKnd^z n0A[bְнy?Ck\Q,xxV(Y ~| ])x4S<~,{\݊wG¿VMLq O)Ãz{뛣|\=yAy&2{bPUf?ŧz [RePRӶVRBۗBdI=C.3Pnu1鯚"6dJSܧo 4J* -Ξ1df- :休}(!NBdS: M*k9}yaƈiX',mv3"tvȕrƜAHxBA(*\ ?v**ŞkNAώV22q7)nв]mx3tae!4F, D i(RDoDF^4YM%ljna}J #\7't}_{ PG^ܥ+q3>?”(U GԏBԷqYy|2LzT˷:4|gKX>.6o[>݅ K8 _x&>$^+.EEo>ӽns9Ld 0VЦ\ ;7'sa#8<=ڥ %?z8>VBV{7ElsWPf_8``G 6>Q lz(VB/ҕytjtah]=pnz 75߼>fESCO+,W#/&ejQ$,fKGN8=XzԄuGXZ皽uWAwAƕba@|qL =V 2~;pj16;"@^"~iةF7+?kW E!kSuê{n!ZzQ'v:F6rr˯P%bSkP2#7CmޓN }{Ҡ[ֽmEGY8~5[/EMݐuD&MУ{X^ѹQm`(/·OcSfjpAPع1е}]xzxvFhӬ!ZI_~@k+[R)͚n >oes-ظڽs|VLXlN-ꢒ%o$QqTm D4 ѦEԫ"FOBЇ76Pa4k q=yl\PQ#t3P֭-d:lV7E5yx`] ZYBc뇖͚E`7 j^@:u 2/i}btla  G}롨nkС : C6Ӧ V[Z6mď>i0xyٴ.m-`SgS^F TtFNPΕkUhڶ>f92P 5эpt<V_.hذ1/[}tÃA/W|'22y VBg/> 1 BTbr DEC3Jo,VH1Q >A!1 y yyi'{cU**! yYG:='BgB!fnS&BBB1CB!fB! Q@!! !3D!b( B P(((DBoz ==U&BcBΝ;R\.B!?BcB R !B)!N !GB!fB! Q@!! !3D!b( BBB1CB!fB! Q@!! !3D!b( BBB1CB!fB! Q@!! !3D!b( BBB1CB!fB! Q@!! !3D!b( BBB1CB!fB! Q@!! !3D!b( BBB1CB!fB! Q@!! !3D!b( BBB1CB!fB! Q@!! !3D!b( BBB1CB!fB! Q@!! !3D!b( BBB1CB!fB! Q@!! !3D!b( BB۰Ao,IENDB`backintime-1.5.2/common/doc-dev/2_weblate_setup_03.png000066400000000000000000001072121465446530500225510ustar00rootroot00000000000000PNG  IHDR8`sRGBgAMA a pHYsMMgIDATx^`Gr I$ŽݥhŊCⴸ[p %ofoC }r72ܜL`@!B(VҿB!@B!tM!BH:P&B!$(@B! !BI ЄB!' & /#a4B!B= ry*RB!ocX`~V0NȖ53g"M!B_50}n޾'h]?,:&>w,<;-s٨B!/)]w"3(rXYYL&dG,񆙍wcDDFA׳@-C,6jC8v݌[jlWA㿟8?7J*@fEDIPPg3{V7͓[|%&&qP0bgAZ.W+ әL3b]C͈&MJK6sD>11ihUUwopǹY* )ٍ=t>f j8 A17 $gÑO [ހaBҋ !BȗQ0ܹ ȥoz[N4yO&r#wЏbSST\R@v ֧6vpK#K6=#}~7\WK?0`St\TF!B} 4r`3KK`+LxFdxĤ$i1 Hl|Qra *4=%?>Ƀ:DP4|1h֨6ꌡ.Jo97N¥h؅4knƃg! 2a&[ڏkCn(˧mz|l 8xj 5-GʫuNY)a9ۣX1{"LB!O(_B;;1@(Hܥ+{!5prt@ aogd!io," ) +Yf[+YaHϺȯ;-sQ-Z,D&M0y>k(&Hߐ|y!uۈ]FTdaWf"t?̌]jBE@4ҿgqaj }CWaTAfk_8eZƢ}1tk nkѲ\x͸u;ǒasu.J >B!߃^*ʔ(\*hK!))7k7.E B7@f% aa k?JX0yMgLDf[w8 *v`ѐ^hղ;f,X=*=Kk0K;l?{H;[jGЩQvXQHpyX?u0ڶ싍z@ CVJ*vAŠmK^5:@-nMA;c_94a /`£=aVlecehKX|*!BHB-$1" C м31vB_Bn%/Ƃ#=|kJ *e:hE0CpF֜3gNdqFFMPLq7GJ# =[Ȅp`A܏SeD81q-:<+4VNXb1SgR J9K%1SɨNH5GעNΙCR F_T!:9 #K5L6~XfZiNZG4h4ƴ.k )p`PT(Z_lC= Gl xeS`n B!afuLn 쁐!ʧbFzdaŮ902_IcBwl5|(^FvC!B^ ͝y梅 =kVMBwR]ē`]_'`~3$ʐRmVM,"+/0w o3]7Z*X)$}?Z6 k ToTO32[D-:vw.ǽ p1?DXB!$En k2L&#nƵwh%ؖ"?z }'O(/3gOhѹkWt 0>e( JƄ,bJ(!b ) Vxe69!B7TfI.WK[iOK)m-91:$ 1 c<cbcq} zf(4ރG/::أLbRVE`S<`ҌKz#dp &w k& rpB!GEF[eJH\z!1_K #C՛yBCM'k7i3#g 3!B|1qq z(~yٲ!; ԁba .Ec)11bsvrBv6nVGB!|9фH )9rĻR{(X A,]RS!BIwN'b2++ >9ۆ̮P 46!B!_)@s< Exd4t: 2è`/E!Bȷ)xh \XvﳙB!o' r!!B!ߪOygB!ޏ !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B! !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B!2dG}2=B.J !BAaSp:b#gm򭐱9)UFF@!BNCU2i]d6C+aiB!6Ыv±a(~O5|} B!]ZXQpnm/B!whA=}B!yO p/B!~ !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B! !BI ЄB!hB!BhAlNh$ED!)2I ZX9L>IERZ Tmc~kH0j_8z#zTH^s";g3wK%Sx^HϘ'8s N c<^=}#R*cNZ-pNsf`9~k  { upr,:'An &Džj}f_&`6p(`Qލ?;# ڷC'nc58_E||h~g٬F0B&Vb \koEуxlR@.„X$9HAR4)hLBRtulX"`g mTiE~I9Ʉwܦ5fl£T~FC2f숓' 7{q :̆Jr*H~@еTfda̞M8BP#{ydRlNeAFby:킭"&%QkXpE ]bfլXwqJsU_dSke̘_`>)0_K 6? M1e_8l3ȑ C~nO1d+ڕ7s$\:QN2#jo ~*0?vsYµOa-xE aVhW\?½h؋vc"w͟0g ΒvB"^<% vKb(=[5I%ǣck4nXM~7ĪJfoEXݻ4:$(X/(ezf &.? @Fgk4VQ1ZΊk9j6n܅Y;T["ǫ 'cKLf4өp~AreD.rEFuIwb"aRAih\zNysqV8~gj\_I-zsEQvQ./]SBecq\X1Ͻ;GRU rX-x+bʣAQIņpyb=y5n g|z*IŜ6zk'|C/+*QaOt$ڰ ;.B5|ڹg*/Ǵh2f Lw/:pZ|ˠCǎ~P'zŮ0{TKAY~m)?h30sVLFaǘ%Vg kS_;)p=JXJv~ EG5@㣑x6 h E:3VXcq2Һm>ZO܆{Z=r{-}=;{2K3AC#J@?V3$JUv87,'YNMG\VcgCGF#(XmC͚|iƅ1O]8?ڝ2Fk{ ALA"uF Y`gy/wt6v {uZ(3ƀ9:wsq 4)Ï>ָs."yS[vI΅Wka^Iz]:*_TϓӇ!&jv@'bo%ICUb}hʯQit2cBaHw&4Ҙcl ~"[A*xp040#W1uo܃FF"]`*Ǹf=v5u W2T̲p\ B͞31G1vmņm0&lbH(تغ'%ƺQ6jf VQ1|b((U V٘΅b(zfO{m4zN xc!^79-]!&h.h2|ƶ( sm"av!D7P{ bUrS(cWBfk,V yG^@RfFǴ* RY< ADLދpl2'~gx[Px\& }[C!ͶB?l lXQ#a8d_ Ax x{*l~ۏ ~x;;TD̟mg BcOc8[n >Gg.YBu`qgO%n<ǽGA\m.ͬgoKJè}9[!`vl}x[zTKfU^b? M?C1 uf'[/3FocЃ+‘ZUu 3.kk5;3E+ЦeDի;6߷^,? S:ÍO}1B^>PĆIP$zm>>=]ܺKzr]J uc܌υ.ŋX4.dW8!68'I>vVn;cNc'l,=dlT(yXWP+\Pocfrc!`Xww{bف3|<֮vs l2eZN&N ըb*܌;NUo%8e/uxC=N`qD)3N BD/­{KP+ w ܽ1(a"Ή3䋋y|r7UK#XMHٱ%'<}}(r\1A8ZOKE[oE[&"ֹ#.MŜbXq]~ml5{a+{_waDbe9?í(m8[&5BCq_++&< ŋ6]Y;w[K?x7?FQ{p`jls g.íqC!8>|('СI J勩{." @x {56 uxjL8Wa ώq0D(<;j~;epy\v˗/Ν;p2vgpΝ>3OrԸ`d06!,hey˗nΓH0C?U90q GVd-ӦPt. mC\bD *=!{̮<Ѭ]wTs{E;FYxy3UgS<%pbK,qUZ`y'wxvd8HS bx+vvoxd,{w%*e³p zL>3o|Jkȑ_q^1p#d/잹``dSչ室gшKd`O/+OIi #GJGofl3|(.5+\#{عw k2! JdBDyy O:{oONGf͚C_6mеkW9sFk ]$N&-ZUVhִFn+ Om7c[ y? GF $t=666م+oRu6y뗈sǮAr;%~}\ؽ1KƏuJZaǫZMZ\û:q͞]$E8sUYO{R S0ݵ6}aMy9z0ةY`R@'fXT*ŏyk XabZe 򉟺?;q+Ap<3g֛M.Q;k(ٴ/Y zղs~aP[j3ef4l:F{0h/=V&򉽘G5rέ2$l"_+з#w{Zƶ}46T,dV,OG !g=F3'T({q 끶:1oeDUPIw@Q#;斥( D= ["bӞ' 3*vBϟ{[{8קv$36|E:RHZ-)+xB #,0L-VEq^|QO._k^ֵ7&:Pc_^܄zC1`x O11;~s`v驈5y!gw};6"V}LΛ:u=B^,%`Я-9a[oӜ[L챚RPz}U<Ŭa׳#N(wQxqvjQ0j(4MtCIppnܹ{avt|3 $ER6Nzg0~=e>:&b̑׻;~'R>y^.yu9{ z{#*1l-rZZDhLw *xdG4ٗ(cP= {K.<okXtx/~1?P"]~x6c^:Vz̤93iP}NaSزv8,,Yf~x{.2JΖYC:B` m5 k;[Dp<*Ξyb؅7^Ʊ0F {JT<Ö  eli,''>¼](u*ٵP# zs hhuʕ*Ո/=:XiR ̙铦bIc:y*f$ Î7mZKȞêCKi?jɤ/YӲ$](V&o^$ |;f>0`\̜2S'NfX G[k<OقkbHҲ7$vR YY}&k`{5V{]Z{ױ[)$:EĄ/ǛWި_* !{S&нx=Nooo̘1sFTTF-ZHC{*d/X SeQ }GEv-cI?11i ND:>xle1r. s'׈9JnzNže%w Ϲ8nrfI [#Q5}z~1uoʳإ#1gw^=p7z҉|qWr-NeKa%X| N`33A.EX{({3]=rGt8`<]G|K\=7 -9[Ysg b@:*WwNǚ=qplZd-"a$p*b\yϢ)eu !v0wa{p8J9?t:6:w]Q)cdV+N?t"s~Gv9p.:/ yp1;{aXj ֮[ţ`M=J+b|D[ü |9ƒbsuC&kL[O,t}Xv}_S];Kw?*ĕ7?1j4{>oaSxo )uX d̚. qϏM3waXoŵp?2rai^NzvOljŖgԨs?nsX9{áMKk_cTLL*wgV¨b߉ܽ {n"2ր<Z^mޡ|vEO6kSv 8dj#9  QR+W_(XA/'6|$ #X@B~0tVRUu*msbBɉ⿯^wo.(ٲ k&^s]*&x% p&fgl^Ue2Bɖo*lYj -i=)عqd)5?PV+#=sVtśۅy~% ~s x 9|•[ Wz4+%N7-Bʅ/B!6EJT+v[ЉcE ur#ϟ[ȑ0eCq!ObB.B 儂9܄r C߲oGo+l&i)E +~ 7/PNVy%mQA+ZSh]WIe| :!9a΂.s~I|gp~$~i_=?y ysW|'w.b򕳄J9u\ h #V g-%i{ mjsyV-H #4*BalbOWԗB_[;`_ ӛ 9cĒ'g ]k+,TXZ/4Px$6oCh'ԝ/7M8ٱ.T8WgV r/)TP\]Xh4xYg:dMPeeUc65Γ7L BVg @>= gcY7Ê]1~2o[.O* yAɊ.(Z)@M$DǠ͜ X$BAYQ0U5B\od4$qÏK!)gvuOR+!gzS,=_ вYR g,%G,^`XPdl-f rW3{-R`\e4Z?\c)2RbFrT0\HJ5ї$ &V_o,+=㣐gu`[5+4gDſɱz%6eR◂ lP,o"!V!r<xM?!6 5,w:=.uYuHhΘ |$Q&@4NWM}$K+ĬQLa$#1I Y Lvl{lDrb[I2}k!b7R*i`uRf}=`)cۖ弋Ժ2:9,eTxvvyG]Q8xR9:w&HKdG+8:wyev\V>X}ӷw=q{R֔7Pru:dǓWksz?sP 3;!hNi)a}#7㍳+޼SdJkNd\*{G8X[6>)z6 Az} aC/!퐘d/p~|N` vB˘6AMrR켗p}&[g'[2C Ob3c-ۿm}_$@s<͖vJ;y0~R*E}ىcɚwDʱOG쩂fr.?(W(iw\YR|펣48<\VΤLRnZf eAAmǁt29/kᙳ<X*7I~r-i=S+Qؿ_!z526.o̗+M㥔uuw JEj夒ц܅ 6OX0OAOj+-?GnBP[ؿp"6E~ӫmLV| !bw5(ܤ=~!/`嶳Q5_݁tx|8ߋݼQhX6|\|=#c]7eKvX"$!+7 ЖfI&rWI}:o[vi.oRB_xC|SR τ+Ɍg3!ߟ/}!|>i<5DSx&B!#B!$(@B! !BI ЄB!hB!Bҁ4!B!@B!tM!BH:3@ ;D!BHwhkt O-q۳}@!BarlJo{}B!X3@׬Pց6"DGۜo{}B!XXXzg2~_7@Bi2@ 1wT& B! )4Z FU;BkTB!BR|T&B!XP?ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B! !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B!_4@ &3f3A*xfIIqM}MǢU!B2VXZ}rp)]wacdpGXb/B)M@! "-Q`#81bl%7:5J#5!2#ʃq3l:2Y^F!Mw#ŠdɄnrZ'/Y*'-'1_~fGdh=%g0$Jm %dB!o VJ}f IwIUCRPw.b岅9g1vݏNBRȠB!|+lhP|~~Bod˞ ^*czS$oa<OŸ=+s HmbBRUEs#;Ul i _15Ap)kT~(K#irY#ow1&i7L ][k 2EÆ1M\dP8QQٲ#OyhF]QjqdNr#ˠN.׿>xwpTϗM¡Ȕ Mf፰-ɘD̆dӋ85sgC>d.FQjwB_@u ܂ðuPl -UˊwI>m-boߋbɢsJw~i[n8q"J%[,Ż 4ZXɫ4~N:K%T)mۭ^mR>E`ܪ%s8h0_4E /nhCwKhllGf*_ߏ{|~ m,X A!n_>@Tk.ժ#V5\ Cb(߸xl\,rwl;"ob,5ȿaUlއ=;bڤRʴi*Egu/ahS-8\lލm[bMذv#nX־}{tnnnϋ-* ZQ>x*͍Kg/Yd>r"Y?Zxcq䐭kaصm lقM7b1Y<<<w6lAIC!B!@,uY,Dpū/ k,ӃWfD|UP6=kȩCTA.>x !7$zC7iYLZMXh z5* )g-"ETxp(N>.\g׌ŵvaܹ]T82/=4oBS4B+3 OjV:(w](Uu= ^Dű Ѱuvlg8;#o߾B!hd`PMjJwa& fر5uѶp  kAUq>DBk;`hӠ>5jȅޣK lM1lbe(v(TYXK;TI+Wےm۶#Vډ'2 oCѬN-a|;89I@$B_4@[gxwL1=ۻLY\`(BDºs1,p+1P`!;|}s:uud6{cϟ}Bb}ٗłS1\]]dtqᘀ'pW/]ƖгnqK@rDLH {Ca%ÿ].x" /'5 %r[e鋐Vjeo^)ק6D>wtd*futGM+sN_f^+ c Ѕ[w`EbB!|._XLV).z4wl'MB!|_@g%]Z8T7mhQqR3!Bw@a}buF\^9Q0!qK`b֔ !B[9-{Ax T+I(m/H!B7 !BIjM!BH:P&B!$(@B! !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B! !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B! !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B! !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B! !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$CZ.9[a#)Y =$%}yB!Gh>:Yz`@&Ʒ JtlTJv,n'V>.:aIO"41㿵B!h}v-N!|Y p6?t:3\`7*$M)4oOB&}6a1aN$HOW.ll.I.>FckɃ{йz^!B!ڐK~~\}=)ފGMP[K%t*{9Kҍc}Qagp[#{pg:7έ@2٥{qv6B.ɬAph8rx!B!t\%$9UFHTjq/ WŒ8DE!^cRH F@T$rgV#wO=L91&'#&.7 xy ;$dHFL ;nuAtܨ a8y  .葬5i2{C\8逬Žy٪ dς}`t#)s$i5JpZLfnG_AlB⅒eMHEkEyu7޾`hVB\(%=r O=Q9{q 4 iy12 ?yb/`'z<>pypmdA&TV8n[HIh슳 j;Tنv?!]U[ȕK"[gO+;2]al9c%<9xA=XgˁLiKfTTtJ ŷ#<\B!;EL0KFn{r5fhecw9WBr}/}p_xBWpaDr%vwOq % J!e n+PPJ[8s7`?l!Ժedyq  о^ClRGmWΚ]G*ud+7*Jg|U&vB_L+ˆr)ĸ0_4't5L%pL=M8v0Iz,yڠA τB!v@B.l_j{!װnCԚs!Bx o oC::BVL!B 4!B!o 4!B!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B! !BI ЄB!hB!Bҁ4!B!@B!tM!BH:P&B!$(@B! !BIH?а0\|OB",qq ɤ!s`G5+ ȗF BOc]8pbb.ftvN2vmQz%J-(E!' Zg/\ƚ;}A8;BRC.'iBfy(l6%$$P_sWT\xB rV:wvbB^tѶE4SCB!mى"&&V VVDB׃4-lѨ:@ݴBei/2O9?FR,;>'14K^?˸o֏ɤcm  FiOI!| CXx$ (_3-tM1{X t(ډ5 бh0a`z#;DN&6^反0^mi z6iy[:=Sʃ%ZK`0?>.&/,7 Y,V|ΗU] ![uI]:SE86EZ4]LbH4>,vL[\F鵖m _l:,$'jb 2ϯc|_~Y6JiǶLWJ=)eudo[i)ۍ!O|9؟X'/(7?R$MC)a2Kzyٿ|zһ?0:LU߹)>._I}Mh>3$}OyϤǿH%Bg)og1xDvЉw>7SR4 hh%8rT\ڛQ4D8;BɿN: Ι|QyYg86rwm:yo W g'e].Р>Z5,B"^ѻ6% 29~Ftĕk9|"{1m#pj/xrV :hU(]X"n<=)S*Ncύ@$ ȝ7-Xcvܱܜ|e0ۊK%bH{Cugܠ_^q- ͪ \sSۉw#\YxԦ(_Sס TEF`Ɇ]-0F6᧶мbV(Lrh#.y=J@ u y "K|l4B,,ظ>7jתu~^Gh^ Ӱ9Z4AWvCVeJ*sTT-B.'+D? 70$LuF6q&Fǖ P;_5K= dPg1܋E?"Vpۻ DpdhQ%6 3!E,] _ I&мs7c,Xu AigF}b2j&ܤ q,,LJx& YKďm۠uI:YKn"2/T|gb]e+Zmm1{X=pf$aeЦcd#{RV Z+eF9뷜xI WD<Ѻ3{8 ?ҋ̎lVzvt/i$kqv~f\|&64J!|WۊisGÄH5zFVɆ]s03& #L:Af䬃)szOI:vj٫}^ Uq _0K:KcYf`|wNE萿۩0Lp7F##p#Fìť.l~HA^wVppۍ ;neY;{ow`gpĨMѫ= ׶ۀ85< |mZZ|kL1֣fgc[Tbo>t@_GS#TL6W^P"4|m0c4}C Oꏱ]BeK7 /F-^~Ϣv~3TQh[E] $plH\A\m48n>9h^0w4<͐S,I-7 d uk>b% k4DŽ Q-C(6"ƠkvOl޳Hı/P<:l{YJ,nqBfll1-훏mƳdV]Q95AWb;/?r@&[\56Z_<Ğ'p͙+>e?Jezz =;ŶG=p/}ˡYh=v;@|>Ihݴ&B^3GDE_z$zWBTk­p.k TVZ=*<]Ga.)c.l@΃1fbzz<; N8wtb-P-kDz]0N Z^Bq?uD"y`c>E'E Bfk ʧl^Q\uYè3W?L!} m\]A/xa fᄣTNx3oef99Ivሣ ,<܇Q~AA6oCTG+`Y8xaR8|F8UAjtOKGS7ךsŚcкy+J ce8jy̞8= K!Y=s<.zƬQ@LhjlǂNcŽd3Ic[bl |̶kfh(mЫyy#v>sv!UZN `ΖF8U2;Z f<يfH&$qf c!^- 9p{t/,MQqMg_o4je `93=['4,3mc0)oǹ~>/z(Ik,3s g`ٜX'H,K6]۰tM)z^xBhSE𥊺},X= >c쨛92U8} (<6pgCu($p <5H/WyU$R\T;ػۄ woc8|^qy\Mzq׷wAcvs I(q V:+1aDƦ-_]X"[A+0,9")gBu̼ da_Ҙuu#yٸLnYM Q +Uh< 5%=sǰq܎ƛ\Ը:Uz~܀c/cΈTN?$MkQq7vaX :l`mKՌлI~KA~i; ;.^sѨPvFB tgܞ$.Yfײel|Сi>d3 m#Qlضob,CІy%j_@Iߵ~dC=b?6 ΦD8 "h8fػbƮ -[yԬWb)$޷?DzcX4f;`vyxUbёg-Oۉk P  Ax'DtLp9;rD@ruɬd$B!߻whe*޺߁Vb<(_B=KGb`jZMH؈ӏ~`o{]X0d n>:;@JIΐXhְA(>]&\P46UBkXs7rg3kdWAa/ĵyNbOi<9'*8g`@|=VZ~+maVhhatZ;VS:r2 gXi>qm)eoW1.$& ʬz)̱T 9_|l9\`}\Y3 +=.g]Y YG!¦-ښ]ɬPհgxtF3f!G1{;ą`!.S 6 -3Q|b`-#ʲ XJ-ݑOj9pc +:` na80ۄgm.X^xݥw>=2z΅X~ͫ\3,[ǴCQOSzHl6o>N6ECaV24jB|ƽXVO+ -P`>*-^(cKuRJ!gkx?ɂ B|^fOf3fXϱv#2`< p6@Ȼb6++i8餯Ff'vRY㾵˻z¥SoDNcd=@H`Z K{c7j!GH8ȑ/_qT̑Q=xh~uWu,Ix[qkx7d&{''N?Uhᤥ ǿC2Gf,lB\uz`Dp&|I K7~-V"Zhu|=B +))YDylF5Rl :>{eyt'Նtc?ׯQbtfK̖`BrQ[8:X&juHAY|!|1gZBW>}m8]v*,;~֪~9 Ev1BNς;.xpsUj>Kj^ \0svD{GD={סo~}x#-WᇽƤCY,*CIkwŐ9,ҟ^XKSVw? M]uF&0*MEbAjA3e'.D hyd:dd%_^Ft@,A"ê 㺜1?@B-<+n+,MƑ??9q9EG`#^֍Tfbn,G*B!߷w=gJwwQ [?|u t&5ZWY%-+bo;{OCgiOA\i5Q Xq:ʻ/vZ-s1e)mkwWVgvbF*(6m؁ƠR& f$#>.pEag .)dK_ʒ n<cvm)CłJ7jswkʡq˺($,3-ϭ MN|BܞYtֈ`DK~WM-X5۔F!w#)ki+Qli:DD!c!P ‰Us1%Z^J~ǂ#g޵sVܪ&@jog+*>/Ц.2ύx=SBag7Qc;;]UDƵX.¶1p jMvPsA7GDl0(=G-URCi̹qqNΈ2t M*ѐ1oM[Gm!|J1lc8;,Cf- f= 5C.|GZ>Yʉy#tWޗ7|(Oo!;CΜV};;&v@K/ކCTB)U|rU$>Yp!RAWL pV.6O-!Z6J\}ft%>؅tR kKrڀ_?VVdʇng` h_,;\s)g&MfOip/(|O@{A[#Nb(`k\<͏!b]:ֵ- 6.;Ug+0S%$\y01ھ{P2F.?(hgгio"oYۃI wClv@"jv9 tJ?dp@n#sBG#%p:[=qQ0l7~nd RS[IL+|;[!_67T7طlHX[RˎӒf'[ ̖ǯ1#`8xGU1v,\[lm_"nbɴ9{-~yL肼zMl VG]&ecT؞Fj AXb3#;sg,;Y ._KL;il 'Ef ͤkm nk(ٸ6SVgFOTΖ ñ:|*԰t,E?vOZX9TB֋ObygBg!ɕ6CDd?zTF==ˠb^[X4x|: 8`S V Jzg` a37"M"^4w&uyGkX)1Kv"Ҁ{N㑍Jq'\ D*dJD=:q/t } Z{f'b/w5 H0kQR][Vu~Wa&0g@ZQ[#vQ eF?!qbofV2=򔯍zerWp#䄧`ʚf0cy r '׬bS YqȌr#W2(]xɍ|~.x(m'Pf=ԫT4o&"o4Z9BCG>btTK,)vz*V6JĄۑ,u%䃲 t~R?y AcV@ɶpT# ?!OV?ԁ+Κ{9m[l N K6̓UjT6WnxdAggT)V K ӛ*>|–EE3jk+]J3Cq2Yrj^׶B0ڷ,*{t.Wc,PEx,?Ylan $+؅*_i d ePLdܿw7/Dسkdul_tq\F|ް}?> W]cH]s}Y> .iCx!⧁_NR$ 7!7};{ kB! >mʝOh1j𜂇hޕw acN^RX +F ;AؿkK_ȼ(Xm h`Si 2 )xf3(J O|zeVl :͕Bɂ;y줯n؈l҆WwlE 6*"+s |Y4x&oT`K슐M-;)&6o+cokWdА/LB"{PZ&B%Zi>^+%v%0lZ ⲱe`ےì^qu?n&g&KSgf:q]-/ 6;q}^mlvϏ!\SW bm*gGuJm߰yM.-lL7^- :T`9o۞fߏEp *7-h-fg)YәxwpoMFllyo-``E.ұ?0ulX?ٴMl\"߆)?֗MSǖ+'@lٌ{>6ޗNqB:MjnN{҈{N{>K>ۼRH&XSHOW&ײ ,kaڻqSHKOkJ\'_vєl*`,@0ϳ fd/6ۗzs_K&6q~k;BLvŋB̮0ů1_Ol+.Kʼ@x<;^utyezJS_׼cM_#-ӧ j>_WٖESx&4פ~-ԯ]߰&o%~U*!B,>؄#EP43.![?m]P`>B;\X2k"`47{ >b gB!omX>W6JЂsB!KGe J !}tN1Q11Vgm*87-;TTDlSСPĂ88d]tpDEP J_J}|C!){mS_vssi鞱C~dbL<k ɸpZMnf(NBPl(rSB!H:}M e}!Bvӕ07C9{;sDt,#OwW>iY073M:9' {cz0 IwWPsVe/Ne^i+VZh Sp+f<=-;hz7vC <H/W48uA8P'"V{ȓSH+E.+ƅƢ{ pkF܌ /yj?xQϰm舚|_']Ľ%cȯL &|6<]aL(dJ>$7{X0˺-5.}_#G V?ݞ*vBd `=p yͷXRΡYKYX0A2Wcf_y1zhgW>4q2ܩ\cw-`aQb:RhJ/%YoرCAbwU/bf*],mYtfbg\aiBVB!/|U*B/_ӆPz =U@BJ*x BիyHK[~G9{[vEA[/Qi$]F 3c@~/]T π9H65P ģW0Rg#8BX%Lz7Ƕ/б}+%mtԕ}׬rp/CrBO!`d\ޓgvR1~"=%\cJNThEUZ0)5r*4\@qC `n/S8FC;p;*ۋ;cn?yULk*4hMlŴl S-tԨ憬8D'a5vDu;a` V&aB!ow=Chog =xK2 :~s?CxT M7F(鿰4BN\2 LJ='&''aኍX|vHU:jfXu:B2r!?a>kXJ0`>;so²k1cC j⁧n  V01 (&GDksmU[IOU?(DnKJ{G#SB UE0Anla]Z*Uj5߇BLh&,ZpVO _8~,5B5FgB!L+ >!x)}YUF̓pDiKd\]jDJU<<݀_T*/KX8٢9ظz ]aX0:O]ƈQG<+ 8 Q᳹ؤ[ΎbN?80)I,jmWi eR<̃*mm/(%k5_3.|T!?+_[K ,`T ÿ[MkmMVSF a .M![]u4 I⯕4mX_|DIua|DLҵr^ŐY` c8fUpn<< Sd"MY"~8%lɓ@ksq`Zܔ\!$Y:vh[6ߌI q{ 'Y02J5uhiPcj(4ZB0C.X1\}oQ[(`_8VfV^4%ZBOK(.ަU4FzY~:ru`| V[<B!wb_b Eú5abR(bQVgwaILGa_ σNzƀ.Vٽ-*mǍh=%/[WÞ#}S*`hrkƬC@m 煉q!$V}b{>|}=@9p:wÊ[§Nkt:SN Wؙ mcֳ#|OƠ'Ͽw nOA޺O֨=1&ٍ^YLM-2S9Jm7_²Yܬ /h3b9IMd-꽾@ӨS %B?麂B En^>\]t_'c&S(v Ԅc9qL&G|b]mիVVX9\^AB!ChfjjU* 1q x2o"qfTl<"J1jVLJ\V˭ !Bw B1*x!( 8b:n<| c# 8(ԨP %B_^*IHMTG uJ%cBk}P@(J)$BCcB+B!qB!qB!qB!qB!qB!qB!q 4j5PP?\fUvȔPB! J 3wrv|LFGXO@SּwEV)QX$L*Q*0~Zu P9_W0pۢ8BLFBmyWAbHn[7|5Na >M*JŻr)([tyy(R\x^WFh0f4Ə=PBYk_ȗR-p!Uz=rO =֖<}p< ||r5ҞI$g!OmHMOG/[10*e4Jdd _[~\m\㔱ܨ*u\ ܹSq),n5)^˖@V䜂G]4H{l l7a lܰ6ؿL*@rF~!ym?ouML.fp_=B.ͱ5D( O$(JAvw5y@P{< SBиx4=[VF.PGFP F};w]>i&vhܶ ׀9@ixzd&ģ.(2 1K;Ϗ jn,7ܻ+~q=QăU7o 5Ю{|ѫ,Y:ʰRKlˡ *Ы[gǗ۹!|,v[_xxOO~G=GJB0(/ȇC&һ ^ jbpׄ*( MPm:pT1}:ce3(r4juM:k u[N6՚cW,n|B iWVߧ(҃>?9.9rbc*Njr#D*D~6-HEdʹ*Q1| 4!x$,U;NGo^sJ ]':? !1ځ"$'Pe=u*Su#:QA<ͯ|nU:ç|'O|NxoTI,aQoϏ]em{ɓGÐI!?n߹G!-czg\vcZ8\=,Am:>@X|8g^A@{=4DkEG{S!9 ޽Q(C?o~VBd'E)1HJ֡0 Awq$"'#?Lbc~"O+?> @/t* >K6:9œk\푬ȈA\0mmz m)"=z4z$lGc^~uJ(ړZ!CNx̭A~7l*}%=w7/)Q+ cQdg H&_;ϯ{DĊmXFT |E '5IP E< #%GE+^n!Il^3";e!?Oztm, ]w!RuyV 2<[H xbYQ!|{|VWO%oe&wÔû%NZa0C^Mprq+ j\ji`8'חvǙ&ճ`Dڡ31mxK;cyX8!;16%n,hYF. }lJzz,5ē_‘Oxm')??{v•{ ? rKEiG%?\_7 F9aEW?܏N!u֢:o.9ehQ+.!>6?Cyq s.ãd LMMlgN>FR_]^3BaRump$fP;ƚsa?6#BBuJA_Eqc\ܫ5;TOpv?\1@O_eE, wbka+=*v2kquds41kផj5N<T)>bZg^ÚHSLUdwv<`S~ MPU#]1xKN$wA-=V;,p σB #y-'5o M͠^.ǶcӮU@m߮Wz?ΨZA/.#Q] CV㗋! jv}t1tVɭs-؝Y4;RĥٳSkGIV0,M؟՟ (߾)<'$Y![;~04*Rb'La&b B1ӫڞj9 H`(̘un՟M:,/PiS1z5G 1!%lþSmv+yZVMn$IKڲsqf0-ٸܽڹUh7*ϙ^MJ_@65H)fZ=3ʱF[sٵ\a\ŷ|/ج5C!lU`0]~4s3Rl爏`4c ז3_-bel].dwlIagZugd&#ϫoϞ̰4V+Il>ozlТG_fu5oWZ,{Ƅ=\Mlӂ?.N|gɹEL)KeK{{s,>.#[)4Y'ccXnHVîl9] Ugvg5ێ`nb&ٓTalR.r$T6;*Edif1UKs 3t){̈́O>u۳A:dªm:*DiF.R T,#.GlD>2ͨPJ,Sc؀GYIQ6xǞD6o /Ʈ>jZuBk~!c\1^myF S3'T 7 <кrv°Wyo ܃XgFd"oC 7L450_I8~o{J}imAÄHB*(&у+&lF7C~t-X ,-ѿ>.gÑ6@>L0|Bu~+<BQae>|*J(ś~.KR1l~e E:(AËX%F぀35l(v>?(2f6ߤj{ o$^EUS?BUm [#x~4 vx۾,}NZ#AP>gΞ 4| ffpmR[|Rw$R 8[AOrn/WN ȄaqK!IU)qN 47$H g|;t04<*MZ`/ညnSNТUu(bp%ijrHMM@j.`[ & L\h/ GT/ ȗ^Eֽл7L,~8tPM7nP]TZnѺ5R-j*W ;jj(oRp2 <5mM})*Ӹ 5!7! &ptV# ga}uF}T$U"e| l`$1D;KW~q/8-hoqI4P)jcԸp0Eƭ]Jz>ps#3Y9]/vif:f]{"r.v|j`G;>e̅cj0MDD)/oK${A3~,ZxU{B_[ 6=lxAq\ B.ҋژ0z|m`wG~3}}}sRKh'xZZ2@.޷9ʙmǎ02'?\)G w~{ԃub$?/| c]Ə) u|\aCcuk\,5n^aHTn|M^<ٸwV5 <<닰l Bƞ01F-W?}wg?pgzѨ3s4? bR`l!r F|~2 `x$単mHjȣ#Cybe[&g`Sqp+ ӣF\C9y ]бO' 0Y fV8z'y/d)bZ>x ho#B)odϜzkE\ ksړ5-~EG_| a`b Cz5o`&#$랏΂>?p>kB}];(4ba 2yZ'lYL`lldݢYPՌ7^:o^nzG&#Ŝ?iKm*-Yb!B`؀X<6: Sj/yPd"t.0weǯN7^XF^b!VOD*c7]}u =7E|9b(,݉ϓ`lmƧGkk*'o0A&Ң`bdYoR-_/Ri# ŨGMiBQ 8psr644F!Vt /.PUbR\^D2,Oqظ0:1g<V|g(yCtP>GX/;<o}(m+äYh"ԉwht`?~ZwVC(B;ZNUvZZSƵ;水LBplnFx*,522:I;KNFJFmT̥ $#4B`<k!q2F|Ó_%Yr)CĶTMD@I`ؤ^U0_yƣxMKà|۽Y˷g{i v|ȶE,n|.9(LW<E~3-8rn=>;'"`K[+<aך;gcgaSlڽWM'|:a ? σV|kB~rz@$)B o AT OpVxymx+cJ`eG*g ./CTD7HxBK!m=|Gra{,b)6)._ B*'W{Hy K{G^[e,%*gJS|c búa"߉r/FU mn%bo*EHUU\my8 ϯiy;-qy4+ +6L8tV5y*V6^+wp#7>o:l:;<ogYsYL*@[6&^ok}a8]TmZ6_X:44۰s<8 W?BMQՑok~V%jGeU )P!5 CS>0Cq ߭<1}r)2>SSwp =`v^K icw-3|nWifIMSgbNFV&(7Wl*HX:Vh L'[c;6eZ-^ߣR*~_e;_D|?f}=UuÊx5/cGMaJ~S~/P8%6p+!4·jI",y ģHL+'msnlp~<b#L1b84v%  |oNʷ#O VCv[t&T!\ 'E#C4@qD FU_[\8 CtbnlS,D:l8`ڃPl+Exf^|{0n[4㑥EhRm}" luBRΑ8a5ΝقC!QJt*CI}do(]NBZ"ʺ2<옂Yo"2!ɈIy2. ͅT{8KAWSƔCпX\8G/EV%wԮ' g!a.o50vo_ &/c y݈|䏫~O]O|hu>j1 UzB}L}W.e6tZ{ 9(wbuʀ25 jNc3tS![᛽w:-6D|=sc?߁YvOo Xܺq A+7GQ>E֮u8p5_T@S>]%T/oҀ$e|{d$ gR2n/ލIq7 8 !dV8-(.s>,)ņ7 _Ϸ=U'^W'Y,6Aژv>m}[OQmP [|s@|]?7p'XN8xu/e\Ɣcq0vzBxMSY|3q9`cc,D'Cu:j|3+J® faw_WY8S4ČSw O\ȆӱzL5>-=Sqzǒ` WGoѼ7oǺS,;*I S?u y$iV sĖcml#l]= NVG 0y@lx=;9YFBr`Sèo n0Mq]4_ Ag8]I`n %.^j@ȁ1 a8`.,FOه#)uТY6LnfCک\,J59նӫ) 1a 8 :4oj wowݽP΄&R7=X \Dx fuPwCƍiabc.ؕT(g ER%̅[lssxAji&~ŌBe?4,abdPUB)|6a%T3fYeb3c~(̐JcO'#9WL`N^K7B);w], o TB߶Tw+[7g􊿨U7hoϼ~LxI_>R*Å7>0v5n^emo_igB.l%# wG^X=B!eRz<0&hP5_+<利? ^> ć0 #oout0( ~O0Eb舾ӗ`GKnYB!e?MȟDoFzyg  !IB!qB!qB!qB!qB!qB!qB!qB!q~P#BrA4!,FЄBk}֍QVa$EriJ!?(ks Aa~![7Ɔp43'B_B ?K(*$#n(!B*+ $B!?!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG!!BHG^=.IaQXsn!4Q"+ x㏭&n8Ӎä9dR|q !y]w $\L;%&P]ƆoQQE1n# e P`{;Ejy؈cE\> C6Bλ0i'Cc:qչup*NM[3`hiN4Ҥ.p|W!bH5rE' P%_AM9'?Ď'R`^v?W+e9a-NѓQj|xq0 8;q.( W=]ۍ÷#aW +RB*NƱ8w?fJX;EG GxXzUeUtlyg/v\|8ú#犪nڀAL.{#\܅x*VG9f፻-reps/WR?i!8sFv`E <sëAeX2)QƨPgIΊ2\ rJ>'^fZ >Ϯ~pZ?;({B{G_pk# #`xjuu2a/ad̶| ͚&?r줈|W B%8̹]}97P^wnp]ti * ߎ +;EC>SÇK8ru"ݻǡ__iw{2qpjo|v$.\gq 8 [`)ꜰ ǮE7.`]u;`bL`[kq.!܀Ect}mk5S fV(xmȷB'*KU SŽ|88 ƌJe<ZBq^./m}}g33<oeoqxT|6g/ H xo䇛,9Q!W[?bCXbTg[+wĚxA?t=>;hfbE(4@̱e6,߼('5 hܽ~8t ƖzȾ5mʍw쫺3qcq,&M4 ^3aHdX8v$*tӧ`w1vk \qy(^+g|#F̗Qxjz[X|>\OwHCbPUDspm}t=R .. –4/׎Dx^ 9⁠CS:xfxy?*UcἛ(qpΩGJȸ{;G#=1~̔׸wsx;IicN&*yW׾ĆgE0W gVbmXuNG+z vg7d~Yg&SJb~<;>bq%v|q)^7^_qۣ<G,9 7~tÄ a~V ?n?G|0EfE E:|*nkq{_<,]d87KT8W{;r%ɹo`Yty׈-Q>cs~gҥ\|:YH:˚7hSOf4wNlc]פaڏc~9tKgw#|bw??5(0k2sVyy/ OF6F\޲YRTl@p.-*ld?U3KWòGbJt|svL\tk6mWlzF.+R#[oj{XZX7;{-e&2"uT nkpruq ·q.]Ꝃ _ǁR;SPޒХ=ěVAd_d7`tiunЕԕg݅Uڄ(mܔ*loT'?7o]YAhIt;_Mn3c@v vU]K7۷gldnlKZ7kKw Xz%>֙mx𖯿'^e1R,*Tg/wlEAp^l7ui:{!~965)֦;񙶌-e}woU𱅬CrE伟X_5gCoUOӍK>$W6Mj>Y*ON_d!T"Mّ5j/|j|PgͫubgdڲGQ=X%twbngm{Mg?]_Rdڻ.Ӻe>^ļ~J-ֶVovGW+µ QJco[.XNr֦L 2EuZ ~~G.MXKo&k6tG8؂CRu%#^R|}F:Ks}R[t8O*w`,쫖]ؖ'])sY nε[whps)m6py5ƄC;vֶ Jȓ(-ԁ9?eΖӝJQߞƪ|{[*b[a߿¶R9?˚VT6ա ;8ؽ~R%['m*!z J]h,mQggXA/!A_ӧ`Sس/o%yHJ>P9'qA) j3g ?q eyxzxx<>ϯl hx}wL +1ޤ(-#gpz8ҟAp\8בYGT0@dZ!S\;rTjdJO%~Q/X:CGqЭ'@ҠMbJ`lalWjAF:sx_{ DcB:|@HjvLFՒH$qf!kbOWK;345 i"ǽ9ZW|Na]ywA_[p{b?$SA]=c8#KHLj9@46GaxͶBxoN >iX)OpQ_ wM4t/f4f/ӄFJj&;}GN Gl:9nfj >W*4*{[PZV MӿtiEHCwoN]` bǚD6U]\ mQ-XL|T**&\ƨ@+tvs|;%U;^'hW8gf ]`9Be"4lRrk0.{#&mQu//¢HMcg1xG?CBPr,c4*\< Gv#/8 YEx4_M uЩX`8|5M1Ǐs4Ο)M1", HqЦV饏*_KV[tK zచ; V¸A[41 x"SWah+_Fwf0lui[@bw PG'l7z ?1f>CG\e9N }?;Xuƿ{1HՊAT'Oc v{PE&2Y$'PlP NŷaVQ̟7r~<]~ti;_R_KNk#ĝUhB\ ES AjUD|)%i+WFyQ}H;]Q#ҜYw k_rcǷӎөh?uZ옽3OO3'7EV)R #SC󓳆񓩑 M=מAS 5^R =`A)OĥטԸt4ũbg\} }KWTKB{BH4(`PjТ TxpRshgwܲ#9 a[ KeL'<{@O,aަ=k^g˜&mtű~7gnɗ_P'J2%̌߿ahO>7e+PXzHK^TIg8#K5X-֙K=HKo]4; f}=W[hBJ ?3<6P<['F|3 S;TFݝ:g'A7q24@R_W2eq]7}jϞoZ# ^ եYI%rOKBIUE0zB)q_Yu.X5g*Zg9&/&M!^⻃S]ѳ0>jec?A]8{l>fdd}^45~=л ?񨝔/_8u0,>]~ZBJJX;6s/7U?iM!umafN;3îdu'uʈrJxvJ@3&䙃|;F'n!Ot2u~W9U* OapFp1HñyP}ď?+>MHWnqo+baeg{C /Qҥ57kXuB6+Խ)𓼘A}0W# 8>r_݅hTcia(:1)9sʧ)*NA[ry7 ¥F7Fpb.GBI 0Y(nsHc@MG+/g ߰Oe 4U~70 AOz1m\C$}ۈ5BZ~I7PFޚZ,9!CzŒ^U+;Ͽ.r7!>?mFO~EExx;)1:r^ OR$ ҹwA~{vn'?~?Mxy@,33x[!5COH8T./ċ>=C]k::]p n 'ß_5io/\ rCڌAѶ_ͷP!S|@=[`\+f6d;pE &2n>DV!J~A̓k8p,^E& :4oRTpN*~Lo`ݷHN /qYv:ȧ-Y(ǹ { A£b\mӀ 7_CDb*NaW Mzoy|xSjɷ95C[<;NCg2` cNݴ7!(C D:dV{ۥ^;3ˬ0=Ðh~ APX&x (_NfP4m:FWa>_DœxN+iu.?';D5aGhty;І<4M51/bS4+cOU;ܷX8[n$NX/"W0/!8ⓐ>ꈃw"8.2[9 zcoCQx{.&JJmM?'0= 9Dk0V3m{%ofd0jz:h۾ .{ JY*匪.0w̫:: ߨxt{_Ǎ'}ƶa : aRԭW ZS8uc@[AKw nhg8zdC#ƛxc`׊o_{ظl, ]Yn߷QY{uy4@zCk7~8xΆsæn6=P!vB@U4(-EV 2x6iƍ0F/?P*X+Tww옄OJ@|uWI%rRTTj|-r=ڶE JhP<_yyԸ6l|1±b#Tvegxz&NeiԲWAzb=Rd@]Ti1lS`ѫ\3bX]W0 9Eդ"u%r3yԼ\])5x~lJXkUl_{[8t6?^" -T1_NA.ukBۍ#ɃqڨgCe~ZE[Ǔ˧C4YcGgqJA6Mѣu%D^þw:UPώ/% Y(W zI*>7G-kl}@:lP^5X2U >^ezWC LQMx*iEiNBqYFR\:|ǯ=EZ8j4FUP'ͫCUG ӱ+nG[вa _ZxuŢ|XKw3m;ԇ"` -6p;|Au3L|muQE=Fժ<ߏ_ gTgp3W#3;u*#M[B~.?]?+\i1/2:"䷑o>g!ޫsBR7]oc0v)FWmEM߉_| n#K ؃Lǹ,tkO ( οgb m)$!$Z8g-Bx8y/*Gז}rN j@[K)8P z6J=;A!!BHGB!eB!eB!eB!eB!eB!eB!eB!eB!eB!eB!eB!eB!eB!eB!eB!eB!eq$9.\,ؽ-u#Rc!72tCI \H$X[Z&)!o*_-h,jWٲ 3z*+pD7'M7@3rjxy8B!nҹoeLW,{傏 ļiCЯMMTX>n;]" 8YLBn"VmG"<tvA!pe,V.ydx}qJCXgc` = x#2na%3p/( +Ez'a1zT{'='9_Gq7 rCx;Y{+N036Dӗ(D9[ܺ폗 prAqgx,!ٰrsX\*T8{8!||c7Z##<7!Ie /3qQZyx.|#Eao Dl|"١<\QJBRj4;[BW}To wæb1Nj{x cǍ^|l"fT큇 g! ]BD{ofhgVfɘU.yVEE|Af厮)%DU7;q1dKPq|:l&#$c~8U %?<5V̟<|}f½<3A=1s|tqq/:|TӸ^rUbƶ=8_FP5*M;[DAQs!84숪8˃Qw KGՁN_#~NA KӦbT!8=T1&pɭp?bקy%&jXzNZj󣓑'σ`*C_o'j% z,4mT!A/d<$#KTlq7[ bNAXi;RBF1Ndr=ygm vP*Oo(dX?CdOh>^3cL1C,߼'$ U59ݤx[A||VOZlG,: a:6 5+HP+3o3}^ʄ0q*CrZw3\+vG0\'`i:lܻ|!|`捪_f~l `*))z7 S6GfM=Xey~c`qubۨ6i?o`Ym/ű'&==`PE,\0{ usB!BZgЃj܋CX EVmepi4k>b_DX>R 0j(ԭ7"˧9 =" *9rƣ2 0P#%dڏ*Ƈ Wa#W8q4v*оauO au*+V]xgM~Up/s`jXayP=q[A]/~*&WBirz|es$:1hɃ%JA/7,<o"VǗ%21M! Gy|r{.!ng$f!W~|>WSXZ60HlFb;(( `w͠xseAhyev0;0t= {B%Q:7tJR FպU|d<0I ŲQ}Ѿg̸ nhTxy :j 9^"+V*%׈#X>ݿџŸ_WaXM,8/5_O r ޼aq<.An?`l ǣݷ7b|bϊ_*-e8`a )#1p4L-}++Kgﴘ0>B !o!Pi6DZ %2,T?x#TF9֠̈́8}2ůRу*:B,^KCmߔO{:DE<8pCF>0+a[)6قWgtEGx+ $GCqr4oaU|OTr6eR G|L7se>pu}͋i{ ,̐~!W;[ .Зam:0@^598<Ͽk{ -˽3'\mӛ4ߠEbBH-_rr'[RPQ؄DͷB!nJ)U vh][Wc.0j CrZ:< Z " /'$!>)wDjx-[؄BG¿ YYHJIEtE Z%$"&6/^ωpqr`B2$!nnɷ|1X[Y N< |MoB!kBB!g2&B!6  !B8  !B8  !B8  !B8  !B8  !Bʸ= Ba CB!Ґ RB!B>( EBB\\\`llL?mF!/AaHH ZYYQ0H!/A/ !BP,#B!3B!eB!eB!eB!eB!e?0 dHGAPB!ٳgW._#47Ze{o4;&=4<kbď`KάcR7S?–-7P`jZ ZFFR(j3eaFib9WR|B!Tj*YIh޼)ñrT=$Pr*YK6z.״)6opGHѨU 4m UMU1uJ "BWoGMѢYt>jΝ0XA&_:z`n;O.bm8y7*(J P$Ժ:zЗ61?OfJ^ڋ!,灩M۷C ж je&h#~AV@_O_^ ƣ0۷EQ[Gvŧ(y''lW+D!WB*xRC,x|>,3pxԎo}aXq>]C'"&.5Oƚgf>rߜnj)KJ FCR6VD :Kaź3ȓ94ۍ5߾X3%gu|5a>^\,ŔK!l!B?5 (ڎKyL Y#l9w &E_a8**,}`M{^0ebih3/\ ol/U5wSD]<ĝ@C5|_W=~6Lh:iF|:AEDav~w?AыsFB!SBQХ1C P5a#xq }h{N!0LB!}8OHa<\\?ԽUW݈@ڽꖼbrr܇YϋO؉)5 :y}'e ۸|<.\&@q(!B_Ͻe@nֶ(Z:Iݐ{,ZOհqtGyW;0ϔ j, }C/mi3><BЭ1vB!ޟ2 S 8*o^;GGcfËLDź;PTY~:OW|PXkׇ)`p2z{bh (U: "(r(2Z/0| ȋ r1^8 $&&¬/0!Bg]!!B{B!eB!eB!eB!eB!eB!eB!e3 e!U0ҊTB!KNl۳g }'w wK2ݐ!? ۏAP\7B!Y>0 T޵kss^ ڭ1Bn<`Uo0VOƺ!\~CB!O=ƖYxy&أZZ(g*&">7UrniPOp ~ W__0rZ9B)NdeE_0gWr:+B4vԨGQ}kEmk||F u͉ϰm78g 6zdfDU,UfFhŗRc4atOmK0kbn?ҫvf/Ft OrT.2>_r,~arZ|x@*I,rR!T((^ˡQĒȴ8w^5ƘQ\Ћ@gY]6T*^WOtP;84>_83G~> +[*9esR!aX \8 }Q:ѿMI2+qto![B+.(w:B!fktpMbiLņYkanf2Ð %JHO2%bӀjTYG1B!j}$*Jf b=2.FG,'bKqIe;erɘi^,F(/(} }X+wuo&"""7b n25ڨMh2gN&+r9?MPbCMND^(?Pg,2GޟU\sP@Ჵre FL.=xN:"J\廀4n>(ny] q> |(?x.7p<'/;uC./+YGU~''tD QZglkH%"˧5a?~.:}:""""35=r^g|V]Keи2 QV?B]rzt_O!ܥ9d8mCU,eqy+YnEl^*emW &ܺmjVG%H+\ԇ3?G\u]DDDDoLzeSG$CxE ? 7QǍE^|/Hԫ_ r@Dp=%qĚD̸v&w# gZ]ʢq"3 <^`0e+SȐ0BƔ@ ̯<{gWں?!ǑK/P4sb?%9O:=?V`E԰R*eB#4j9F DiÏ?D*Ė}*XgPDEEޞJ 9|C-'OlB%"""wb 4( E Hd))р4WH󿄟O_#o8ܗC{,$?<]ۡjjNכȌakRlt\(?ǟ٩qZ7jubECVAdNZf"""Yanw0"0*J"LBDKA>WOD$]J:}smŐ&pO㵖Y2'4 {]BA׺'beFTY\Uu:5-APB1[5zN&Spi3srY>0 d6A{z&V/[cYzCAOyqzşv(3&=bmP2 ;w-;ׇcc8v= CsN/g(pp(;#./x:M8v'xSd[pW/+bbkk3AJJ-CjMC\\<|eg$""^!r,i {hdk S4%KMBBLUQ;7WOÌ;FΌ$~H>3>DC~:t} 0G 4ES:%ϷŜ&kU%8!p\t e5bj;Z >'=]Ahg8}8_*C*!!.Zw0hP0H GDL y{`B8|EN' LO.ب(h]] ?||r$F 8#L5|oe2z'*@^d #kZ >>yD%^^HDDDKxSF wNxv0#J |z/ǹ5 _ij`K47888@<(T mgɸ{3n;Ol(JU!u$Wzcc-X >ټjy"ѻF61/BF>??. DDDDkY˘- DDDD618B""""@HDDDdcgÑ>@8;;*_"""^a`` j5rF,K(Q{!Yd2ɉa»l!c $"""q DDDD618B""""@HDDDdl?SdYJOExD4RJ>])T0!:"I:R!ӧ,DDB,iL)qBR뿹O2hat@luDDDD,.7nrJܶ8z#{<*cҸp78v \< i^܄`S[0hZC+8**bGbn; ֣."[p !Y\v-0dXWOoS-zDbޖ-<ʢu"]h]NDDD[{#wj觘ptQ|a+ @䑨+6V4ک2\*1a(nҟN\Nʸݿ܂OeFؽ x/Ɣqpm)Vʝ1~<,t äƣkY1f<瑘!}2jevDX9_ cͶh6fjgnTW d\=} ytE{FG8HNe*HذPTa+-{QԮHF7?Tk dGB5y[ \eQ""!X_=vu[ޏ6 ]qeV|- F1#!/J9CB^ E9PB@Wy;>F!`z0E = s=4v> 4R2yC+>l_W܌@eRVC->3>cESЩg[t= _S˱c<>lS W`܀h\\(aIѳ=:w < J/S l\ ',E)9v-bKq%k]7'{f5#z ='ǁg)09W̸o^I|1Ɲb`QlOZn[ûe2Erc8] >%gB.}0}$=_فOG@˞O^k;y vmGDDDo,B i"TWibE+qAv=GO'2;Ri`.)XؕZ3ԋG K+`mD\:yOxgэ /_ ;G]kZ}i[fحS0|;8}?k7"0 ϮruV˰  v/&D/PIy-#PJoJ1_nGS3 o(j/+ Ƃ 08 ]i3 q,9`c2L8;lDL%˾C}`$Z7N)UNT\>.(_!@[' 7/||CêEx89 [rmXkO r6]ע׸EX3o5"-m.7E06[r f X{z|" r<,/>*Sq< *aMDDb "uy~5qtj)!I^՛@)72^Pj{ U}N};x:܃o"D"Mh[O.j>- zIUXS _JЦ ]lWl(Z-z(-`it*Oژ99"BmC4h:,B50O#LǯoUAAp] }CPPA*?G?q1l7,:Pt${w߂h޲-ͮ4fC㖕7?Zuei{|M(fbHҸ虥1IJiQ0/NCc|Z@o4n\9]N(MDMIm^~}ZiUs{ֆ_v?4Y9siė>}4*[UD[\,o^7#)嫣e!{\imוPAаS7љtP[wB\ aç[|)Xև1I7^~}t(mE2CdUǔ`tT>ֶ#>pO XIsv ]aL#C.NV:3'n4^ptd"޼ HjoF'Y#3z!CSכ8ZwC!k^?k_t@.#uWt1ipsTo7JoNUnP`2!o!ӺA/W |u0ΣczXIT&낞=ODPvs{^V,A||!eJ j=2-y^:~2"Y ٽqqU; f$ b yp FtFy˹8ě1jt.b! }\>3cy .j¤k{4;}`􁨒M'<$8+$|e*6t2n;#wz[3XQYc@zNr%;Z2uGMlkv9uw/}7o!0xǥA2i^}dCVn֢Y)Ӑ* >|Z/+&_[cƅL99)Wwx<Rqvֻ4UF#I"@j n ƫ.HOmPy$1f}Rׯ[v 2~MDD⧷i j Ǡ!E-b.9Pgw [Mtkp+E驈OҾlЊb^hS*a;3 cZqoޟVL7a/z<\ݼIJ奚p2lh }F?|^)_^  ~L'"gH'8X.'⣉ N0f bVfҒdLG0#*TÛ։ [mf7abx~R l G-L)4&]SӖ;ɀ{7ף 8t,< cٍpY%!#FcOnq˶D1NM( DOVjĦZoJ*&|IN$垿d2D!0t☈c,GlCecvT:? }1ԨE|6S) HN9Ѽi[4Cű8<k6%15r8mf/bSQ^D=8h,~<řg?|M°1ceSDҩha∈&*Io͂ĨP^RÐ/v.'?:.VA}r$1OˬGXrxXz%C2bΙ]YŌDo;W㋼>/9`Jaj/EG (4:/B>dO'Pyp0G&>&^CMo֟ 5`6FoU Mr,?tr~ITD.S'((Ǧr6KϦ))Q:,P@9tFdl.b?CO'prP\2"64y)pb"C>"l^ް9QɖY #=)oƉ?:3\}|/<D%xʃyKypzY'""z[d9ѻ%CDDDDa $"""q DDDD618B""""@HDDDdl!c $"""q DDDD618B""""@HDDDdl!c $"""q DDDD618$(cǎ!00jdBѢEQ~})DDDDo@xIxuօlVj_h4pѶm[ƥEiD:rORIDDD@8a̞=[)6V5k߇Rj8r)8d%Vg[]}]Use3&UR*rohX" o/OdF^??(S*KFQ)6^I!8g\ JUjd&m{aPj .x{BOMMwpS@ؐ;/ G{ҵ W&""vSJR^Z5W#4/7J,Xoō4*aZKB&D\ٿ-_""]qzZ|XR~31,ݴ ~=LXd~e."""zm!%\Yރ`奧{x%7c)ihK,ڴg~,Ju/0h4e-=Cߩ;<L϶^xeGƦ?ah|*_7&op\Y;+~(ju3_A0|Jl\C'{4ގOn`ŔOxk'k.@>O7C1g6,Y n.V/Λiu ۾ Gl$!&.A->4<"/[>Dzo5kI&IA)iٲeVUj~-.cbJ,AңT*)MM!dm|I~@jPp4NSiJ9C329K;E!4Iei`kz뫥:e IS/+R| [}I-խWڝy7K=RE UY*i*o-yU,z`R252f2GߑF*/> )RQy-]:W%՝~RJYpd6x(iĤuGϞKGOB”"""zv{^l<Zq-1B3M&\=^{km+O".Cbm;k|s6^|~"1} &.ێs[58p'ϝ "^(5} Y Hjy|ZPw_ƶ{(U XkUOJD+'Ӈb8|CeqdD|<N# לH?,Y_8 bM֞I?啕:*/8`$ZW͡Ff-u\AxD$>xu.__EY)5DDDR 5n=#_?[ TQ#t&H. vw,X} ca>qE01``'&RIfUh1`()+RL5иZax9F<7zL 1p<q3,:*gE4YqT0db}<[9}>݇o\B +I"y*Z (\X8 76a0b>6b.~֣O0om>\_6&-/6i20i^ S1xd(Cpx=5j,,[VeZd82*ZdL f'""EaΜ9C&?r®!>FC]n 򖮎Ů%r0.Ho(Gm0``|8>sql?bW+f`HfѨ$^>#Ym{;qʢa/ Ai6?S,5|ڲ[?,ذ3ict*7?j3Vu"X;? Zo>I荢SފhUvlRѣg#J#B bb-=hy y"Ym"""z+MQʕzj>}gϞř3g~u}|K\&;0y:O}#'"\L8o'JbPF( R`-xkFʋX~˷@L/tH sxŽvZ@11Oq.}v[AЮZA+ۛcq5l>n`qA<%3?%1 G\C,/bpq󀧋XZ],_~z/?]M+aҭv${Q P;Q8}0?[],sϲ)J,k"~v9:tĖ ?SmggI$,2NPGi쐒O4N(V"""zd)򯥫X"j֬5jUZ5là»D5)CF.SΝ:^+rɆ\T 4+)(]9y,ςYW(lN:<rCIoGDP.GB_7% ?ƣT hl{aTs6&A\  .3?p[B;GbT8_嘳94ixj"sbVWrBۣb^1+ɋrχ2)rDQnw0 ]= xt1>I@6}-qD:h#*qua^JxvFgΜ> CY#Rk%?g"8,{հw;]GO%>>뗋@}轋a1hR_z>JP<ODDD4@HDDDdl!c $"""q DDDD618B""""@HDDDdl!c $"""q DDDD618B""""@HDDDdlܿ#e<)DDDDOQIOųwar . XpwP"1r:X_C\dTNtQ~[br2;\\Wge """"ې@x=ʍ]G:pD0cT{x+SeU-x,$w)AqTjU~^~Ӎ J;;53J+LIDDD␱]b 0`X,bӷ 1} h"f:%6a0[J)-aaIQV腟oI#2>EDpG!Ig]Z틈EtLӐnX\xdB ePfԪ^eJq#d)'5|Uhh4O]6oz\`ʰ>(U.ʷۍgɖ&^*"ߔ= p: FC1=ָG0k0ԩ[*7 99Uv06=Q搷1:/R˖nܹ/+'Wܹr "* /B3M%j߸DrѬƋҟݬfLj U/6,qp7xIBDLK0!Ѡcn×UNسk-V7c%8bcΘ/1P4 CŐdCtkABm1e04aY,>>psCy-u!O6HfkobŠ#2&tKѻ,ˁI/bp^hҡz̺%ѬnuKJ/4+f=p84 ƈNmJk @㠶^'ɮ9sЫk#ԫV UA 8 I`]ǽ>Wș:^P8Z;WNFV\\\dI0h,1))zDDDD︿C(Vq)j$H&(Z7#XI%XjT9 /DYr+PfBjR<42c"j4f>pa-9&: /ok{`2[f r Vzw?S<<<,7d˲M=.^=;;Kf89:)5DDDD﮿C(iPFC.sau8uSZ;qCdMB`-@D& =sc^2!96 IQqHֽΥX+v H70}wX& cYUje`*f/~1j2l9ZQ_~% Ŷ5շaR? qQL0 ?^,OټY WWWkþq$;g88ޓ Y|0!ƨ+Q=Dxd%˗>0͈Fhx4Yw$"""zcӻa`rÿ?=@l\tJg''XC'w.eJ"""ws y8|kgju:h:PTpppB!R&"""soܸʕ++,JGϑj$3G.J_ 87$=OB"J͟ H vvhT00pY UJԂx=RlDDDDo0:~X_kt>J_eHC\tJ8N>PJ׮XmXGzW\g! 9apt2 !|~~_hGᓰ88R'!!7oxfJCLt"$Gwdr >݉c؂kȈd]`)웃%U1whcLpr)5)<%c~p3)fGڛ ;|\,Mi H1:W)<%. f#$""??T*k=yvvfxfs &,Y-i3akrw`¥{9L_ro||&L߮Θ89{mcǰ} |gjNH4f7mtfl9!c ֭n6l\=>8[J+`r,Zӕ&"""?U8hqYr6^ƤA=:$Q}KæMǭ2)-Z:5e F|*,%[9 hަY'qaʴ_|n/N`e?/5!#ġ:M-!b|47S¡U:5,stS|>a%¼ϕV"""?F IBzb,:vu.k5JysBuBESSxd(#3r)rXo;g1u}>5Y09DP@6xy*; ֞GI,>gmtl\yQqm8%Qm_F(mw,ϱ~c41sm5-""""slDK 4_[p^8f$> ; &D#_>(FdXNڌ+ev\'ꊤ߸.PV)ˍW$"\W t4't[m6kOX7S#@Igǵ{u,$DG=ͷCT\%B,U1әk9!~mh:⽁BUqt]V (^C"tGJ%"""}ޥ+v.=?²1}x^Uڠa/hө+zvyjN7om ]uCðlЬBUAcAcS˜/)HLULz$' ݘLХ$"9߉I2fQS㑨v]Jb$%YuKDzFY8h{P aoгG7t6 [qYs_韽ul!c $"""q DDDD618B""""@RX[ KJY FHH""""zewgx`6-DDDDK˘18B""""@HDDDdl!c $"""q DDDD618B""""@HDDDdl!c $"""q6T$'Ck0+5DDDD/BSs8~>_ˌ{'VlRg;Z-Bs<~ꏐV""""@% ?%dt:⡕ z !LbƩ#r1Y{Q}axh FRhE 'l[zSeq˃ƍʯ:"#Z4!<,!Qq+.QшON%4C2"c* BAnLi@phRu-t GpX$⵿5$=`jw%CŦ+2#ЀHqH7Y[3' $$ a11ATti.1Q,Ӳ1bS_mCBlBCTb˖Brbe"9=HXfZ`{p|U2jVysC7 mZ 3gg(X]zvG.U[xqf Ux0^(?C0v9E~QrrÆ/Gb-p0N䧈XX4k5DWVLo o'yz(P Z'3oQYw7@J{K G߰=M[eMBݺQY_L|͂UoWTkuAQG qBF]1q)Xv;o"RI0hh E+GðCnPASTn M;uA1DhĎ_U#/iuLy <<\QJ)T"'D¨Z<\siu\DDDd 6F)u K6yS` Ɂ3â6ilKrQIHJתר AiOѕXƗߢyp,4_b?%__*(l…`k2r bĽ[co_ZsL_uvjSq~ ʍ)SGu)[vH0g<\sk7ayOB-[udBb:KL} mLFpFĞP; [<_}:G5j@yh(ၛ+bC(6xm c[ gB"agggvrthDjZҴ0%`<0bZٺBƒ/K5pϏavlW>n>gpp*6v0"tc|5i ӑ"YA'=@йNet^:{TlX!XF주/ g;_ |}QQ̟>ǡP +]b>3+6A1`fd0]exnS~Ĺ(3&'ߵʅūŤ]HаXv|tjc(0̸]-Y6 NT&Pr#&}=S2FI_|:FMM$.Z e*7De/Pz< cDy znw&|:_-.؄ ׺8/bbộKlv,Y0[+egK#XBt8i "nŢo&aG#đP.'i!*Za@(8%.BFݍOCyNy7tr Nay&b*`,v\~h٥ m1/ @x Yb}'(xCh|LҴZh:xzNDDDM~៖$-A*WP)*=KE{ME(B R%{ߢ< KJ(YIB" 6>}HT)Hn$TRez %w’:{!HϥrKnb;շ䜻Y,ns4ŤĊBO͕*"\,R:I2]:Kh5QisE$$ydYޥK Vr.+SC*ZTDsy߯!g[MyӺ>Ra`8:,$TiAKv?,;|Gg_eXy˲|Wr-(9U*'^t2_fAҡ'pK`4Hݐ߼c)G~B2yDDDd`j{͚@/a|0'bҡTqkV_v]Ly?7vZI€OƢ5,=~? eJ_HHLJCYeB\HDDD6*KCFT߾̅4>I!|qW5O[&kC4˵ȕy䆓W-b d$jMprՠKBJ=Do^l 3yg1e@HDDDdx !c $"""q DDDD618B"""";s$%%YqGDDDDo,›7o"W\pssJDDDDVR {]]];"5""""7R uJ. {{{#tSl ;w8B""""@HDDDdl!c $"""qo 4:MJşOӇjP*l?شe36$mºuW*cN^!bS}7m)PwtJaMr&{ /lF=L[vhB٬&`0,DDDD\ KS =vGлg D1  ]sWFסEs8y'`{̌i GƯZN'qMD*QLViA7qe0ٚB$bXlG6bƌ5\㧏 #Ąņųr\y.BB`Ͳf<Ooƪ/W`ocp|3fX DDDd{@hc7q"Oc>ÕFo]E`Z?d&}:%Mp^5a F>}ѮZI䬁U| JdLC\t썫O?Aǰ.݉1aHS  OGc"ٔ.JfrZQ:jԪ6>Ѣ^]yPόpMFs#Ovdy`J1FN٭Ր7D#xA#כ0d+&uCv˅*ٔ. йCХK7 {)izQ)`2 S:ii\xO#W͚ȃ,bܡP]߆_M͗gZȆsP&׮ҳC^pi&k]u厒sjZ "/@Qa#"""K0 B &YL'5˽vr\ScpDDDDE%&nܸʕ++ )]%|ܣR'’JkQꈈz2 FR۸\*DDDDW="&ͫqcĦ#W*hֹ#W/ e""""kފ@HDDDD`ȘN DDDD61ٸ,BJ{ѻ KJY FHH""""ze9={$f02R $"""w2&"""q DDDD618B""""@HDDDdl῀6.Q0)7>##<Ũл+O+c <(=8 J'36_BR,l|4w;.> BbAEclз6y7DŽ1l:)5c, Ob…yRf$CǧIY _5'B"Oh*"ϛpx!7S?脒J-Ůc?=9|aR9\O~ R4FY^)Q]5,~S",u0/& ,41)>FhӭÝ(FD#W?KH GpX4RTXGVi8 h72o 1#&"aqHIKARrtF^uQ @|ғi&؞tĉuekX!ᡡH*SDl'8@S5 0gL0D%e+ɐe-qr0!1&rR@0$BaNMiЦ-ҔuFED!I~,LHIPXIb=?D. I ,O@LLaHOeP1!:TEZ#քtXqE"WfG!$"ɩK;I{q ͞(ߪ]np$uyaLBxoo{|$BB#|.`{RA.caKH}휄1ac(_I\GDT=}ɼJl>Gxoc%; xm9K@8eΐ@oF՚%1^9cokKB\bb ?8y#ι?:DD+Y],1`X*;v@$E#F.Ơk@, eώgq~:1PE|Iwvz.^ Yjub3i4J:fm ~OFb.0q| B'A!Gys88oF9Pd(۰=ċSUr9Xv'Ýbf4f,i)LJW'ʁ'WbGЫ5^ vH|v[vm [Cza/х\Up-ƌe# ^67_ve-{L;]Ըzth 0CH0D(B&1uYga9lÂK&x :;7cHIwCrЫ]+!&6ߍQɐ$|>Sf ޅ^`!oYP ^_|6B{ u@Ud[nC/nm1jX hmV\x FUoBlknuÐn>0rDw\c R4wC&y\W;Q8 y CNĒ(;?9!Χ ,+ 7G1p*w%8{#ZW/G. T܈fê!E)GV@R_#̵ۈaĸhR:CmW"25cTe \]}}W̚YSXe^$rE6ѹV~C.aTҩvYNL0G8_ˏh6~"Z$pNtӺZQ+k;S:)U?RXjvA/\/ǞaN'ܺ~WFµRo>㟎'7wrEBi}4.O@.#1{5qpA,l~ 1ú8w @#=ڠ-we>pV餐K[A~%jSIORa.SJef0HIIW J#&ߒ>hB$*uAZd#Xf{.J_HFKxIҥdI)ݺsK (I;=IץOz>\}SOIkM˯-HK4>L'K>Jn> ̢gJ'b R.=R8yR/K_@Zx1ں>V!ʘ+;Vz[ԿkiOg%gRO6I:v[wP8PJM ٲ$Iz(Mn^p)>4a-iqB 羗*w"= i'?ĎZ! htS<>ܾqC Zf{),gRuGR3Ҫ=O+SJ6H:]nRiKҠ#ğVQK%\QJH+\uLHI]=-̮HIIRgnΗ 5/u)(.V)OHO"㳤|UFKOSK1GH"=u[ O7H*tȭҾ/K&l㓥[ҷ﷗Z-U>Թgwi}x).}ҏR-wK}?-t9Pҋ3yg#V4u)Et藥R>getwRV#HOĠ1FH?IJI"OeΨ_27=hnOGQ٣8UQ#0:_jhTB|Aêj=R娙ZS!G zJ􄳽; Ά,mo>Fn= fsU"=\;.|hV>h>3R?CknbayuTj;8890pQ N(Q&W!0"lS98 8ǣ+VFn0)̠{QQy 8'fu(l];f `̟-|5Q@̠w"Mptρ * H8m\0btg㞫2ƏjCqO+u1G$˞:=W9- D1а`v8{CUzh0\1cv\z`]KNHKysΆ2ZzFvW7,BrX8:ojSfzfO WX?;N>AϞ!()wj eM?ę\+6q!8+UB.K+sA fUvCYMӈ`;עhק GG'*7Ksۏ7)r"_ʨRk>!yrûhYI23 6D;ʷkH;rz!wh12G.O^i >UKX}OB^npU&wua F%ˢ[v( F c2ȡeޕD-[3CrfKb#2߄ߧF_^'w3lYrcl$|Q,x hd~rTyEX|gQo,{LOpfLbuU8g%XGĿ.FfQV%o3X1nͺ⇓Y:DD?7ҋr4{S6*AKzrŌ8.ھlڅ[6"ȯF(oiч=b r(V\]3s[$ COٗkfOn,  ;D9}=\8ޅ#sh2͙EPgp-TEŦSxm|-ҹ-˾(]\f\I@\LӯY[6'HAWᘧrfy2W~ϸ nKNF9s"OjpVҢAEA|^Yk >EtD(47\:E+euٟqO>&şqjd]eLjd0Ĭ#.Wp]FpǂKA$U΅ ؼ|5?m82T=O>gKיU0hNH߅Lƥ`ssᛳJ 'Ojh2]X!>.EPrWBxO %ycia+밝` SR4KܴRQ>BÐlyX?Öppޥo!.YaiE T"]8l}t$Jd(ck)s**?P>t|Geo&|=x;Z]jNbQ<6[7i -ۢ XXNeLZ 6F"ǣDKɽt4(m95,M[x/5ꡤ]ʀ@ BdǧY+K,kLO?1,V%+32)U>`Ee# SݞhO!eA6wmgAd|CP&ݍUXF4TA[P|괵Q;;g9wff hTZ2n%7!9zo M4•SC.sk}fhXBdK,Ba\&(] JViu:a$6~I{rT0\ J%b`LGBbO#R⑐f )WGQrS\,ZX"Q85E Yh=кt :A?3TiF\PG%Yy(GZ-,-Y9/.UqXM!-Lͦ(Q <Ȳ̊uצXF(u5JulOtƵQJ3V_/=KUS'B̆bMQFSt'6BY?ܙ+7Fw(+‘tekVvƾ!QFs5n׵cvFm"}sEO#j4 '|,x⸗g ·7%ۻÈQ}mоX$Ӭ¤|7ݾVkQM_*q D  ]6@f([]o<&e|t)+3h~U~Z=z$&Ӏ$c<ɣn{: E7e:k)}CLcR#)wl+GE'e^#9Fzm2ݫXyb Viҽ±A]S(?"G\E b'yVJƴuw|Wϱ`@lF{-Pb_5))b2[Qн zċsc[$8`G-Qj#]z`⩨%4eVyqпs||?7fk5mOܴ| iHOF?ɀĄ8(=+q8jRB?NhR.]֪N≈?cgf޷ #K+u)i,r_4TJßc|9 ֦n~YƎfpmd9:W;%Ɩ?zyشQ+ѻ(Csz!/^&9vH.*A>]iDu- WmcQpT] G{T?GǯCTSK`PT qzDRr2g/)蟐Q }x ]g{ϔٖB F֪57/Ydggs@! ӎ11h޺ :SzwXl Bֽ1d*GaگkQŚqz[n`aӢ+}w>=9~ ;dpiLи}OUpsf탓bV8L+hp,9ȳۧ+l~hŨa0qZg]̱QmO z%(܍ ͷdq^[1r@>o?cj,Gy)#!iƩhӺ:~2jkZ?|"a~hx| da׬!~֜}n O#2:yDU|gCjj%U! . ""ze|6pK@0]Qp9T#ʏ@XuGG3~1'#q[ؼst:??/ eӱcc [:{^ħӶ)WCq{ɡJ~{=V鈩`Vp~4.gD=đ3QJ;|&]^X#΁gA>|oT Oo]DnP@':^_VLCp&Yˌ{ԩw>EBx86x/"=#6%7#r_ 칈]KF8CRrh6*4kA #oy/VEDhIDATt.^C[0n.>n[&LuT_Q'cp4Φl^OŮ3!Hu@w_ (Y;S0k63#YKѲ0C O]yP(7`ҐƨVfBZe2L I @Nl(S84buz`4իT8ak |' Oc#D/"ųBZ X8m?beŊ8GXkp\T8;| N-F<8y_Z#Μ:S+ރ\;9ALĖ#?]X2)`ҊקְO޾ݍ{D1g 9+W1,{)qРDq^XbOo=W~qb- :4 7/,@b> .Q8`<@cQLXNYϺtG9'ⰃeSS]DjF\pW4 _K.wjl D{Wg.q*T8 GKѫm1 õ|cl9~+ċ_a 5S6Ȓ(_y,'G{opv *C|'*kԖ.P 2@(Sjl^(W\]F* c5LK]JDDD/Avn\9/OwԫdC v=ۡHyUlkr^U0|xyD^< KTjy<6 F?tki ժE\=@4ߤ+'\t% Wg܌߬ӈP" <.j2',֭Z6=PFq\8t %.JI)x,@? bcRCDDD?* y8.ƾdx4D_Ѡ7 U hֹ#zuh#&ch""DX{$%ckxZ\5:xGuh|̣r1DvqrJ,GRk1C_rb;ZT?;B4B$%Zd2XdGPlONLi4j U1̐\_N Z,U~u.L\Gظ; ,yb`IlǁS *Mڵd[޿?kc;1uL,<"gbT"/eF V[w>h>>mSAnW%A leqֲzϠRJ%~6xF$Žz.O ""Oo9V܌#Qdt_<*`tlL@9?Zo*R(,_~ދ0oԸp 슶gaX OEAݦ*%#SSlr K(^ҫdrem|DEyS;R9%[.B\ΨFF DCܛ':K(}k^8e~\EPza_ލ"rOm7$.1ڈׇM)qL$ 5u 7&,`JD߽+BL>gu8#zU7nu8IcFbR"^YS^TTrj}Sw&&;k&wė\\,8DDDwr s,ҵ-=RmPBMh:z(X郾^5J%ޑEhݢ JTCDbfRh] n[U\W4.?UDtSץ%9 JO`zZR3ϩнqgf y!aH[f'|s=8xzzuDDDQIY&> `2DnU-7dHWY|\ y.%GG ZVQ&!\zAD57O49U@r]|5оej$>lC6_hP̩wn?APSB۸p9Eܿ~^R- n9rJ*!h}>i*8{N#_.\ƥdUkzyľ^-U Kd,79!#Jkj͸FjUOSGh\<_e{|u|2ZmDAC* """;13"1 Cq|7vhh"cbO7|}rE*F$&C9=:"""e.c""""z08B""""@HDDDdl!KIHH^oDDDDvr `JRjm@xΝ DDDD,۷oXbpqqQjmJL&Qf""""]01| c $"""q DDDDyVQm9齇TRދ4AD Ri" 7{/!􄐄4{}:MCW~*{_2?3잼0A`0 !`0 kA(,EQA!+$fU#!Dh8f0 `9SG? ́J@q&;{L$%ކ>Ju1l&OE;}z`0 _BX;l!FZ/׬Y,ȆO6|=}3Aj>D:o0 `0$2AX'YЩ Ѻ`̚sj2+tC~.odV '1 `0P"AXgtBi'a'p80Ѐr9 e`0 /2T+d0ЫCZ?޼ 7O?UB5u0i7fc?`0 ms)! WV6xeJ"3 `0p"AkD%fDj/"c`0  Bm <-vdX㐠V*RBu5h)$2f0 `)e[ư ƳMWq]\<ajJ ֢VacSp DZ)S `0SSʓZ:hm8~yP.DQ(״Elɇ3 `08rA`0 {m3 `00A`0 !`0 k `09L2 `0A`0 !`0 k+ WB`0 ?W,`0 ƿWʂT*B `0t^Oq\ `0t^Yrp+ B `0d'A`0 ~v`0 5 B`0& `0^s d0 xa`0 5 B`0&#0T  5@V9Mܿ (-N ϳdH8~֟e&< $~:7EY7!9aEg AR3Dg $!n>;%j WW wk-I;O遷P0#ո8wBo#CoBEN- _wB;(L@wP!5!n|HE/"ߠmB_"spr$mljca' w㒐a$$T6($"Dg ӷ6 r!@EexhYl4O}1κBQ]ASK:g T 'R_ E)‰7_z/Fma/1S?<<|~cHm4eRܺ텐? ?=_0"Djց}k"p`4hmQn$:m"kpè8v0W ]dQ]K ::t5pP[?66GV}MattM{qBG}=E8/Ik=MdL+! xp|3J.{Mx[Aw9dSCHK8v`"OŅSGY.tmac FQA[XBޣ$9x|<¥ ,b ⟦YOU;XUp/#F-&%7+,LH͊@Vh @v;*jaJjlMO/ߍcxPj:x4.7!ҍ`L6P'O3Nm`HoDZ@[8E&Ctlͬ.Ó[Gq=Ś6hocb*oģ<-\`^W~yT9kS^s\?}^ږ'e|j2&!Vz괍=TL W#In|X3U{e:0JW¤0Dꢥ%1z,eX5)Ap24PP*•A dFTCcRpq<}[( {PYzJߣs"mײn$A8q"r$icm!eqx,]WR~.zE;BO5٠uw z5mO(KoH:]3L)9M8`k T)óOi$Y Q'!%E ;D>=trlrlvhiT'ڥ3Iu|-CvL$mǻ{z>uRdp&㽬0N[b),[\B^ -l!*IB'nVAU. xZn{SAjl[[R^xmb][D> `M1q<ǹش*u c; ^+cW8rBrL0yDJXZA_Cpm }@e2e7@#SQ"-A^:i$@lz>LpGnSm G&@Ѓ8}?4mI(RUN Cy:60+ 4#8rڝf8uKՖG9ʒ0={U&Fi*.<еiC zeMBu ژ$4/^\2+,;JK#KPiFM :٫4?k:¢2 ϞGH.zb)$WNŝIjv5".pމU0#mg1ySo(v'"f ^r1J&z\ɑjMXTF+@鸥j:¡Q$Dk{S"2d%>w;sڶhcc %}W܂e6BKؓj|/T^R(ϫt}ru[8ۙ4 8in~7&N]a';8v>kѪ tM"i҉c%h:M C '|RehQHS=r$\k(еG{cGi>Hʡ&ϝRXiߨx8wqպʪyU+^*D݅Hg8r"/:#pi (( B}bXjPY ;Z5EyB_.A{;NpQ253U,|o!A#xKD#`stX稀ix_X}PPQy=:EvgSFGܙ#~ghbޙo To|˷CƵO1otgiG#aK GqĻ>{C@x뫨,8y}.AW!|,9FCBQy ,_Р|r6'-\Z> ŏ(5`z\Ml[ço =B\wa."?.YN rEP,zs\E[tz%n$+Ož%K1{H[~?VQ ]$ZO7]n2搣()U)dY!x"զ5'0v0BO1z_p| L ugg,\=v©Ǣ`abUD= #Q`r!#Zy6 <(x<\ҡc ~;7)~+Aja n;1|V:C=>^rSme^ƤkE$6""~{bOI1}GDhGQ5]ݍTeQgoXIi$Mb 婐OQ&N܄}3% ?-@s_+Xԛ q"v~Фx`eEIo}dl)NSXL:rTD)kk0o.uM}_9%oNG,b9pE,N*eYBxF1/:~>(J.kVM'[zܶ 'W"n`'yqOb[q h 0w֧H}^,损#Q-7ҟ`@ Lk0"OD&*cQK}ކ1/:V(cߠ^(|M~&"/a⇛8]V9| ̟OkW u91^&gFb~iRhD+ǥ7 %R*/3q`vHaUga Ϗ;l-|f-hXt'[ƪwV#Z M˾H>! <؈oF=,j=ܸ&OOψ@O_./P=B:Kp9)e;C帻G{g{$ƹGIXORG[$,m@w-ȵgg'q[#u|`5}DLwkO-̊T$l pL>YOĩ9z:ڡm76na\ꏲy_oAxoeb礷G!Z"`*8.. ƌr;_(z#q6b\Z)>6-x'xg#gHrþ`{ȑ-!ᵜ8Ͼ: $SzC{`5".|HV֪{$ ,d3>v1 \6㸹j :Qꑓ#H:!d$^ )'}0|ySu%eEI9BFZNbd*2C7 !9 'yd <=8lR,r:7o|q1_}{WyB&O"9dʃWi:y$W7RBv}9U(ewN|-"y>Ʉ?x*&g2t^yndq.o6h2r`QB#փ֑<Ոd%E3?&?>)}dҎ'Fsli!H$vK_,XdJPqSɖ' Bb-%ߦ"2pUZb`>;dZ )"?5liRI'qi (jȒA_ {H}@??+?I P`r؉\Y KwMqd,({F>Y9:'u*Mh%yVYdy!䇧\kSq RJ:_@wz(4~z 20xkG*#[nAUm'3Uӈ ,YMG\Mhvq?0?r6j0rir5I>2nOl$v#˽mU?4j͟YY52lg'6lIYt"Wo5%}>~vFbS.Eå;H*/S#9r2{Ʊ?8ͽ2.^L) ' .'4.K w6iQɰ H sL[N[S#q77IoWq52k Hq@\<:_qRkݖwɓDE6q (!%?gbI%' >Yw<Q6 GiҦjUdqdՄd'}&m>>2ԱȮɑqr)qZ@ed?&SD<8rF~t{,ꖪ&LNGi,͉d? wh߾^iv] عc?$hqUvj's5"I-jP\.1.6 (k̏&OYL1mBk>C,ˆo,7KRzulAT묤Q5QXoBL;4LB[1vX'{" -X ,iL0 _+⌱Gx8erFXjV|ۈ1)"8; 0Mx"(F!,crnxc[^o!DHj先OGAz0^ӗnzH*"KD.va*|IV͢r:Uz\'S@!i\={ gqs1m7+ "!ՠa0?ʍGp'$BNmG]PH-!,.4PW \q*mIȴjmUaptx>? vSq2^ѕ)K O[uZq0h96GTR)!n8{ N_4CT`|R_ny'ȪVauBq:8v~ܛáp#$""!#{hcy<.30M>si;5]l`T?CODy fC?!tu!yO# NZ6"6IyK8ś S߀;5o n-|8?KlX!he JE'~y?K`1c h8tV>B@ge౛&IQEYz"2-aNBH=5fD"\AOO=o49CF6]u :N; wBK'm$ e 9]oC [1m}RҶ=6a#XZ3Rd2Ֆ6k#4J. 5M8 NM_Y`ϻ8y >Y|"PVJw#4Y$B"J _n{Ozl0e#{E7nFm6gp7 j9%hƆ?CM[R1$/.qm"n]q>:EvKL]01l?_sG|Q}DMj#ؔ311>P &IЀB-e"-TH!6=1]co}xG#P_8ME#(+DyP7#psr\g!9at g^xdJGepJ2nǁN2N#\ JBRU%IsIM^cWcRy}'KIPӤCT :DHJQ45譚2Z ]_cY _؊O{!Dڽ=wI _ k&Cmb grh^tB@mӠ0/Z̆: 7pV""K:TCYMC*z[ܝK|*k զvrui@J՚{7/MiCo4PWXdnXg*aآ5;X2>@$Tv=uԯ @Ȕ(0*JY;LƆmiycֆu?}5;ǵr',y J.`g®80B ϩG!9 o| ̈́hrެZi; 4k!5|yocGܻ1ӣU;Do)$r8P͏{0+hGcj gբ+9#<ȺwB5+ꤐBŏK- oѦD^Mٴ[᪢@ n^!mZQO[MfϤ{Kl&HFA=Oʲ5M]8|G^S5ڿ4g!P3EN0}Jci4+.jܱV=uEԝX8ҹttCz=4`[hCIQQQ3h#4MƟi`1Ţ5]d tp;8 %J)Nq>CSM&H*hl]X#lݺ +`hV.0 S8A.O;Ubї{T,::R) V!C1o9p8 ذfGr{RUi:$+5O}9}`\8VqmKJ qи":m\ Ti@]:染Am_h17UWZ4 }{5﷔):\ 9&|sQb1oo4]im_i| DPjk5CVZ𤱞2[ob0.q&F)&-Q*){ઋ>i91~غ bH1Յ?%kZ,Ypt}S~٦5rCV8iȋ#='rډ4]Oej3EZ|g+j^ Kjmu9{'ɜjÈFHOG={\Aa7[M",ݜ[8iO,`c$ޟ6TZg1_ki[%d2D:K-P\g0fRhi֗$ub"ºэ-a 0++b:8C^wcά)1 87.DB ::fkla 4dSIYC#y"siОRZܕPyWY 9# E/.Z;b`mZY3b4mal:w&㛍N.?EԴ@QSz!|>6wBG+!lta  SVjhљ5R'y!mқI鬣Nn/InT͘}lim2'A% ̍& CsW:Vδس~%43s:)(ld ̋Yc@g'T=`R0a|$W0`Uuȋ1?R&Tf-|i]ƜoI#ѻ PC^q ;|T>oۄ6У.tʯ|8~=$W`a߭'w01ذ}5z&_'aq":fn_eXFǝ1 i zLRЪJ|ߎ爋I;g6w-m  nMoNM 7UUt5*,Tp^S]^U&GFM7,#L>otw 7y{E_Jngaü<ztoIHxQS>`0@#-X<{ZtlWD!-TM}LyF&VqHo,\ C+%J3 'Ձ>C N/7b5l>rv0VGב0=n:gdlLL/c_ ~\:3 FA0ׇ-t3sd]_G7U%YC?-M*OO_W3\]:/:7_p2DEZ+J-``+xwH!}zoN_dMO~:tڰ2 G8sYyHJEm])μ Q|i$Z+!i({5eȯ+ q (duesL1m>ZUÑ W(N[;sJ ?_)"E~D)GM_}gl׵٧qirK9Y@EDҬ39F2pob: pFV Aggҳ<-{Ru&CqN8= eź#('1 ep{pQIjEOME(hH;k. gBb(H;s3J~9W*8g#>?<=1&M8t0<,x\: TEšmPb?>yFtv"'m\E|. xqtLN!>=qq0[~Kq5$Yu8:D$=HM|ߝ/LVG.*iVI8l; 9^_}A(ȍOVhjrbT7luIp;ʨ"lpy%m)Jf3~AIFރD-#ήD%ŧmG|~1|NaڶhڰzV5ƉaH8-RIO#u4ʎ&r|P!yxQӒ>?wwƒvQX1G@f!}q'X\_92Š9۠1s1Ƶ䮔eڴߒKi}Rqږ3iLa G)Hu8-a'V:,miV4NJvLh:$SKRSRq}ZP֖"Wn1p@[ږD"sH㟎x۲my5¥)nV;D :9rr^_F.9 Ցqň?dB[[ p>*gTp}̶ cXXviG|?÷s;rZG)n\ӗ=:nm">ss[/wøaYG-8@'091Tiܫ-ZFLѽ W{mgcb~)$zʳ$<ػsA脾Iz(|qc ^xQGCڌ)&4ZJg8{8bӑZ8:Bl~ n_Å`ؾŵ3W`ZD|AgAoddg"\ U-p?)mSR\ǍM\{U49͋X+BWX}V^!zNJщ?6ld&/-:Rѡ tp!ꖴ$x V 0MݤR'O'PWTj;j~ZBrG Ro펣S>]qyy}r ?]}) ZF$FNh]HE x̙0O<%'h9ξ8)h65)-Cd~vujCML kيC&C?N_{HcHN0)Q[)Yvhf,DcW=CfnVP]6}:ÄNuam^"`1+UEp2 r6<ؚ [:c - [`بs<%iDOrbZx@Q 3ueGEmO$jPU shL\0)B¥tVxJ1֬CPL ;tm:[>ׯQ:Lr8 6/*\taߩp Սp5ʁ_\.owN<</cp1׃BR5:vnɯ(w$eFػy=hD:vA}מP{Y;3ƾIiChU[>5{Ʈvy:f`C-o"- ߘJLĭbԔn~HgxzEOp Ap7&]cݿ܆OlY/*fݠK7=XlU>CZl}:6;|o$0h0Gݡ菣WIJ)C4}A<.p7@$Уww87~Oa ,~-4Qy '/{Gt/wx5pqÖ=wClc|JܺpCsB !^ yGAkg0RF0k2T~hWd@A[tm|Ń_D:z ]9bn߆Q:1:{Vn vաy.x{,mAh;EЃpO%x{ J KuuH{C37hVc`N0?EZё{NEx,րC~D>q^2uZ%oG~:[CUY\VCF$%вN|Ц5R=q3vƔ^ \?w$XUuhm,R\JӮxh[-``Wa KR1z +&jibX;:PXY'B6mac(QWU }Nh-E%b ޾O+Wn#׬346tpm1z-z7hBMsp>0ùN`G5 ~u]:I.Rm`ecػˍ/Dy 1 }6tWDhת 2=5:\pca\tЯU't1n|ĸ۾všWUhDYŐu>= ]uWx:⪜C~d*}j%phT-@϶ F]^t7-8أ*yC~&}3lq۝}W~)IacS0Iĵ+nPĹѷ.:X@\S RF&Wq];D/fqCT/X0`gh ;*[bBVI ًq;uż0mOa08;q˰n_ ƍn4GD1еO_B^GvhΆwBj>v ]5&,Jq`7QO[c(߿M_hþ݋%@f8{#j~Y:_9lѩz[2$nޡȨDn`Eӎe'o1APMC"M8 0a%f00:>8r1䭱x`X8ִ]=KWhI[q.z r亢 W=;qow5юNIV m+'s6/ 0yb~fc<=\GwB "| {oGki?Cpe'`ӇLE\wozf0hXxV[0A Ú>aQ r(!Qq&{/.&=nM rUWDPԿ|]E3=8!lG1O]Dq6^tg(3:aۃ혿'2$H rlGOBL?b֟!([GNگ) ƿ& `0^sT`0 5 B`0& `0^s d0 xa`0 5 B`0& `0^suHkP+yg0 ` vT`0 Yx^/`0 a..~ٱع]˓̈?~qC#y9;{668ZǕxo!1Op iɑ _,Ǯq;{'8:Ea?a$ց/1u$b  `0(xv2^q:s(qfdS'ưXT/ K?ogUI?IN.E<*8ER06c _W`m8t$۶Fqǹr&'HO.]ΰQӀk_NHc~[$>O`0 xEAV-ySF+uهNVrQԠjHyU1Ta#~&&1җA\T1IsjFF!h`0 ""wŎ30,,ͅ R[;Zh\6ҩZ5ǒ^-Zw@Ey&_ϣ `g< G^k< q 7#L&Z.;vaqyZPJqmx5&c>#ǩh9ԴuP qp^gĞ\A_~ykj`0 PB~kQR1f@ #=@V\H5BRVLZ%*sʡggm>u!RQ%Vv0TY%*alkcaSLV)a׮5EH(BEE-`dZW#9 د2 xyEAEݳ 8`0  -t2 `0~x(L̤l4`0 OrA`0 {n `0^s d0 xa`0 5 B`0& `0^s d0 xa`0 5 B`0& `0^s d0 xa`0 5 B`0& `0^s d0 xa`0 5 B`0& `0^s d0 xa`0 5 W@YT嗃a\9vNaWM[58> F:+ %Xhp?anf _| صn{?C'隡)2C=]89@)|3"f!I}4ȑx ?"pn K]X\;|֮pD`ˁ+*@ӻ_d.yթ8s¤Na߸ʭ F󫂭 ЧiVWPZX[YBMz.`0? nw}h[w#ZHDU ͹ͻm>ڕ8"=\q>$ɌULLz'l3>L~3+䱜>{73prlGDƭHߒ:z~2lĺ+Ѵ@; %K ;5CQN?F͝{4#''?BtI Z}~|uDDEۮuĹP2q}Rß &gL'.]K܇dնȈ}vdg䝷.-CȇkOl$[#=G'Nm^nedw1O5.KWbڏGgx%S$m&MBDצ=)/z%݇v++ҫI}aDeZ*g7۶߯!F%:Fu箐3kɓ+ 4Gxx V*Hs8\`0.el7>$= [J%kO05y_ˆ޴ 4ZGSj" O :D~f3 )ިė3qKl ey Bکqi$W[DH󽰺>n,ޅD^ Hqc7z?N368 X; En0> E غJ!ǝ/vnF5X39 \FE4`{*(;=yg}z<1_Gd^] IakMlb EۑDQ夬C% |./ G_<×|Pe)ą-fcҲ 4V //܋ÏҴU=ڊimuQ>=jZ ;CbK hc@Tt$䜝7,q Jwu jh\X?s s8OLÄ8Og]݄lΦ]"-[016f6`0?W~kpxL|4q:fчx(qQOceb};{ğtGPAKA1hB U1AAbJ+]]XRns{c,\- bG^TyUaeˠgVoOwgMWJOae64Р*Գ ZcӖr'b9E=Ȃ25ږBӿrj@[kcT?_Ѧw/8ZkLd0uT0d:u1F*u1got*ķsfa{x#mlhT2^rR)TN%+2jf0\MiR!ROfTԙ@NP+-'c*|f`sQȋm1懠+N.hԅԢe~*lXV-(%C_ܹze6b1|cUA1R3*uP3./" 5^ w6j5߬~oZ ND 1T%:cevhC-JBSb QOtŬ.F)L.Ec6 f<ّA̠1T8W#ɢMLƵ;yΧ=_MS7[vEۯě5큌?qq_cldRJMrq\~2)j=`0?WY7`fj-l60xÎo |yc'0%ʼnN/c%5 :\eStlaՆ0`6#! n YݺՀawĈcGw4WGNK[AR\;!0ovTصR|04izSD G`aGK6GbTOn@Vz] Ƣe9 =H0B0jfo@x%o_a3Q#vEYpž_CKˆAˡ'  H+IYZiZ|!ŸϬwp~ssBұU0 G~Z2*V P4b!ޗ#y;v—MqGye%r yA6ss _`0 ~vƸ[Եzw댱39áZDᤩ]t§lXh~`5U{oy $&ӧzS{Uùцu+tcǍNo;#U~ѱp-ag6.1h0L 6̩.r4?ڀٝUѰ='{c,; ."}8w뉾]c }',5`a {n7sqA.4E!Cm[]Z6p5^RhmO{(NvF( mp8/:ڥtּᨺX Çt6ڡ{lז]\[Pfpj>SXR5Wj= }:MS0ie[ڴX*l{@G ѭoY:RUCu:خNn60FM*s݋?0_eс& L3s 3Scn~v`0?WK%%"OooOzD.-HBt=ZҾspz"h /P][$zz|8zYV^ KskJE*`0 Ɵ+m3*Tmտ?%c .؃p ~67 'zt.BP +;wiߖcb`0B0?՚hZxE`0 ?& `0^sؖ1`0 k `09L2 `0A`0 !`0 k `09HR(J_a0 ʂdB`0 + J:::DB `0 + X^ 31`0 />*żd0 `{x%Aȭ c0 `;`?;`0 !`0 k `09L2 `0A`0  ªQ*X""@0xRQ)#xj`RGYj`)LDXx9-92=$Zjoi=̀W@(*?XHL`0"ȑg=i<ӛ3?BJ09Uw eFBː#&y'GgM/cyJq:MB_:{{y(\< Pi@Fs C҃nRf͐ 5${(¹MϠf( p1d kH fݱdD'gʳcNT/| Bz aAikf@wtB`PMSzZkJ@p`q$(~0 ƣ`r*]6la/Kі/1 'Q׀ACK ZF%1'=$Q[Zuu5\z;NЛ*DUom]h䖆bmR{o`}hvLb#9kl`KU-v-(e0׎?r|)Z@]:ng K e!8 FpP: <}1&'IeTFC(aEE%g8?o&=G7: Xow sVC8wO| PJbC!y @ ʍjS's?n!~-MHd5 Sy:-zԠc`g8s0$4{ tO^E\a3,!iQ@E:Tfcp{^(Ex𥦤T׃:-sk'p}d4Nʌl`Q lz@Y8-#柢@a|0j*퇠!ȩU4% ~^|8KsCܳ̇jLOogo!Zfmn8!Wo(xG{!Y 1}]LEDMEUMGv1q*< E A)y(-tBWWʼtdqXkU8ϋ~gsE !+e/ֺ+Om9 zt"E H"Tf౏“^z0 O aYQl_eRڀUgS?Gpʼ$DG@Q&@FSXl!􀶙9|oAJ\ ybܸI!n^ bTd==Ful=1`HO<I( Wb:~KS=/芓< F:c/ KUf^ PGpïq,o@ǫAKWa`ZɱXv:?E$9Ux8x퓈ݟ-0,q9Q/]ٲ?)){kXq86g/Ÿ/z^[U:wpʌLs M'Ws>mP/-ԵP w@!l^\E!8Ix,re#S:wOz<J2GV  ܱ胯"3N/.߈'R F,\s^#aW{?Ɔq0ki+[p.BPi6؋u`gA ܸ| E}V!B z!utRT!޻6 BdJ._aٶV: n:"ZQ~T(_eo2w|z?R_Z h}%uCD=r!&1ő8zw$4(Bhpӛ~3 y={&>r}L&}y 6I;Bƽ f 9E)G&Ld&GdV?(Ysɥh)5BȒqKHR@u9ӧd !w,>-_|UReʧz/1oqV;**(,V97x2E.$d>ZhrdOj2SDTLJ֒ "8Gd/_)w}SjɁƛi]nLD)97s\rz\ŕҭ!7!ulBBw,! ,rí$YH2dqb}6?N< yG7'f"&M$-lcQorQks[NOvhкQmJxiݯ?* D\JVjt`f K0n%VC(Hl@*)ǭCG 8 'M*-' z]''-uhjUsTn Hq>Bc'qbwRS`0O|APpDKG(vԠQ)lR1Vl-vP#4PTD~{}o} ˰며|Y Q!5eXPn?Di44Yh3WQW ,U蔦Iv :9qb^j^n K*Դl$!$=H圠iUR9KQC/v*ݤHu JS`ڸbȣIqC #+1Aa4SL{ ZPj˾ErżԠC2lz&\Kq7W{Pŗ>ɖv0iyx1~HtmݢYpS!̟8ޞ뗢snGE42 ǟ*r $r!Hmk4%retWHU8cq35yDq5-~H1=uݻTiwW8¼]tx>B{w;肑5~&!o6,2hv:4Uv7Em%fj`ѵ]㚟 1T37PeYB7#-A^ Sߟ mC§AR] Ƞeй-[-d0ep_ C=!WE n#ʺT\zWF^(bʻk%6(2>jk.B뀼/߁VwfWS!}{0e1P+vCwF~P`0ז?u@!X^qvr u s nk&oiA48^x } EZVjY9nbRp@ <y6gM17d0 F#²8/D:`XOBS[6#<R-kt#W/3vp.ǡBiVCBot  B`0 Ɵ~˘`0 !`0 k `09L2 `漒 |O `0^Ijhh@. `0 + B;;;dddB`0; T*ՅH/}0 `0 (//D"W (d0 g? B`0 ƿ3 `09L2 `0A`0 !`0 k `09L2 `<Nm(a%<ĢF*@eA'f7pEHNKDL" &M)kkB`X?g^RWH*?iN$ _xEAXQ,%O:R +!N;cg3xQ!Cz/ <[ ' jǮ܀;O .e>w]r}z.w8RVSEaBEwÒ$B>/!V_!.7:w R^W ^,QNJ=b&mlEȭ b_!%*,KQuQ y~kn& CQBHd_^QVʗ_9>Ƶ+P5} I,@G DVKFӇ;P}IH3ðصp :X a1pÿx#" r8$ / n;"Al~=`*A~RmJHw6zB@ýT|<|  kb`<_.؁% L_Uc-'vo^9 yD.ƺBWpl z2("D\J5X6;DR&ZpdTH:-S zVoZZn9; zc&Mf/Ɖۗ1CsOitt"Jӟ'BKB MP7 zx<5pC`|~VIu*h;#ewO\m1 $zHUI* $x: }R`s4 deqy#5OzI0/|&YW Q"<ƣx|8$<CFf*J ?$5d(LmpD<V$"xW@juZuum^e{_ _lb=}@7FJKPTN~x^(4*R7 U[ҥ39rJQ cTg"%-tbU'Yy uMJPfP eѥ#i#~I"F$-|55]ʓds~ȧp{B&FIZ@v\Rhڸ(54QQ| WVey(~i _-+c77htDfV%=P рΣm ߌ}>' o02=L,5n^>+u&"s e]KTcJFWHs!ʐ)EBf[Q_Op?x }B8>4d @+Fuΐ"7] WC!K=FwW /Gs۰F$%{ t<}QEStFƵ;غb%(YD8[FCᅽ8PνNVEX%Au;NܫQGMq)(#Pgo ݦ/:N7#' 1hDWp,_|,rPl1; F@'_TJRPf> 6ߋ\Q+WLኡ-gV14~qfw[hO;M<+^2 T:6С'9p/V 8w/P[&j5==k v郎uQ&v.UEG.=eN#ߎ{V.SְO͏2? oұ':4)>Bܞ#KZ8鑍zZ[Au02e|/]@@-uG]%䉔jiMmƞ;N7 Eo.}ϰ+-x>v0DQo0Rr:1oԬz:BxG%V`bD:NzRI}E<6nЦlYF׾ {!WbP**+z*}>-%֛(!`Kdw,L>w7#@dpZp5!Q|!_={]d} ԧrCء{xCD\=qyr Ą{":= NF#n't\9sZmѷm ~bYx}ϫ(K Бu n'@ /!D 86 13 C.?(5_zb-q V^ϑ)t ׊Zbjo{dEřS/,5ڭAhD*؅tR[)/Aq~OXUa(EmS 8 gvmG~`wLp<M@ucZ} wFrpA<BK= dQx= i׷^96ك TI,tK ?.b9k贠yoV-<_,Zm +zgh^v\ǖ|wtTéG%~3Vՠ jFN<#i:3N[J6,?hkpL< PGPCkn\hdD )#WOGz( Tuذl d6Ǯfc؞osP>s kйSφUN't%TEc5zjq"C`,QFQ ~ S,UA s; D;(2*IH5\N{p=sjQW#dp/N/ !xP7;>_'7'd(eRbY6nӇ/@-`.0"I&m"7IXʇZ&|L\MG=%O$"!%#iܩ([ɷYE}x2b2o>ٽ,Y )-+'28:p$ $gפ$S$!}BqO$*yg{\*롳bR]+at2!w"R^BIYh:%X(%G|F>9[?\ޒ҂B=6p+2g;)i{o%F~և,x|Lz|1Jұw$ʂwm&%\)5~;I/NDzdd@ pDc2*|P)#y $T-!*%{f1$N9$^&& >sd' bvM0Us亯&\Y ir?_.0.ɬBd$&2̈́`^G$ZS T\|Lᣮl#+ޢL@sqHm-_pQȖPLJӪdꋤǓqqY:9 X^O*4LJ|4k69GqFY0m:񑪝g"_HNDdI[MnBWG>!m8[M)8FO߮+hdwRU^r7Kr$HrF.#cK?"+/'Vdhיp`p\Kn}#\q1z}ֈAÓDudSĴ?U CK6;:Y4@ɨg0y9doRB;/ #K?K\#{0|V _\mM}LMBz&VhRxufō5LK>u4S!+#i9p^!lnOWgj#?h|3ʞ7˵C$E\t@J J@q:LM;[Fj7գŒøNhZVUD|6F[g|-};C_W9a|B:ݷq/۶RZy4MV1vrǯ -,i`7$Ρ-n !oD{sh >jJ@Pp$?δ/B^C(Pݯ־ o{ ICԩwm-.YZ5F(" X;A E9R#Y * t&i0Wv#H8򐚞2601ODt2_@Z $xKgEK;HFػZs!:t Ւʿ@ b1ga^< {pwp|1rm[n\z]ڂNZ3iY(mu2ʞʖ[ڐ4=PԅydܩNX0;.݁lrKɴ[X݃r pq\~;}t4ڨVdl;tA6ٱvBdQR.jhor.'ƊM(BJ)"07Ck:ZFTܔ)";gv dוfN0RA*ߘ `um}Zt:CKMz& •,|pU#͆oGs@[GTXCP {r):b '|dcn;C%%"<2ebĥdq }|9!VZ +_r;!,/h>4]F4-چOҡi/D\ LZUdDH9B|1mw?USnnCc0V)CwWTXr &xb r8ФKNM3ǂ:-Mm>-8 Y\=T{"?i]@#`ƒ/@:ZM)B{@t|ҷ%klsCq>_}CQ]|>GjJʪ/б[/p"ꉎ}94o=^{`9R#/T`Ǻ1^6~{ٗpZlV>ty'CFO~.A8Wk]p9X5:I:;C3lZQP54add =,_z/ZA/+AL(!i9k~F1:٢~=HQRgl8mDP}B/eq^n Z,Y?^)Ӏ nlٵݎ0lĪ[Oȟ#,wc|gg $ 3GpbA zw&tM(}:K:+U2yk};'LGC2<i`}ؼk? 7l7fdY161Zۺ/QQW֨C\'zW6^1m7@W('K.^)W4;~}wG;hc&C_V4ϴI _ dz"dhpjb ڡ]CSO33EWrYû z,W*A5h;Q4kK5IthAj*k}IhCTjpeG iйdž>o?-;wa:{K%wJ4Tp *9KiD9dt4҄a/t@B| \Xj1QMPK(l|PNUU=Ǻ:wMaJ&( @['qAlںǃA%o_?^F1)J+aDxQ1"$Pù՟WS?ˊ43 NAQAN';ͨD-ﺛȖ{Yf~LQ ~ 7.UNؕ#7oݎEK'\5Kmo04z!orG-i*|^5y znRZtW/)ڃ)jdjh6 Q EUpND]Oh)QpѾWśmiruA S%%m|Yey%7ԩ¡DfjX绌YneTn!]0GR^%*EmH6bYR:&M]ژcٌ_n]wH6X$>R 2 x nߝw{`{(2HԴ$4YvjIII#_|U#C#jk{䎎s&AvoA;XgO D^z^\$^75wn/\yބC;isZtsC莰4-s3-Jua؅Oޤ}@ZtZ W"^Ρr&ÿڃVbt*xdPĕ&3s'87_wɿ֜ P aEHEUBǠ]DEVdӎܐZ/M6@ꐜ'sGVu-Ѣ߾Q}[l2ԄdO"U'7v4T !gDg2.bS# #7CX9+]ԃT#&[[ vTKyr|Q3q RE/ݴlhۻ 떌fZ/zoL^CSSUZ&-Fzo,H*Gt&h_{EplO{UzGJQQD)JSAA@Tt"* 4;Hegv=||<);ew;ӱz`$Od qVz)iw8_O/B0=YZ+}>R}ܹ:_P 8&nvCi,S4kpK9UJP~"$CM[4.湎c8bn!^y߃IyIr /N=xE8kJ \-gۅG(\GZVJȯѿV(ӧnptg׍NEjGȟ֔u/ēU̽*"P_LKv//[^ndMl2 [oJIa 4xћߖ\⛟C2xR\P>M4P=5oחâ(wMs1pyF+^vq`\D9-+oy;;Ǖ;~B⭧c+ !7+b)mIJE_3[Ӑѡ/Zcvx*W>ot|yLï;ㄚW+-Nz8GH0HU9*kн/KV q`w{} Xn)m4y0- ZxKW̿.@{qw !@b/Ŗ+m,VE3ŷG>z}$lͯA5=V|kTF(Wa#!m9t|NܼX"p%kpeŸzJ)>$rlB+ؒ2 7?{֭=i&NPklH^E/uwl2=ZނOܾZ4:3JeQ9)!>C[z60[_~f-$%Eo7MC^2c/đSfW&Zh+.(؟AL8(Wtg neo㑖-ub)F_T.>:[#Fu/M'(._\ H-vm0D-Q>;UŘ>2vGpxVa"_W&Sez;F>3bB(_yݦ/@ZHӽe!70g„ВZl^8Eb>5c=yZ/ޯjS0o$7u}>s@zTo61pi}L`/ڋvu[|fx/pAy3h`3x?k=>׉rӗx$eF|meJPvsT| 4VB8~=6UOy[zev3>KvEk"B|K2-35_oD,~Z(_ o!F3<& FAT$kC A+0PNqUO/Mk%6| vNW4qд25!U>UʊqnCK_qeKnZ聺'Rh5n*^_`=%툃سhR> (8R-Hʉ\o~ 9ݪa@*[`ȘKYru;a̎3ߦ gn7ZkҍF}/yV u9@Gd͖83 A2~@j,,S6ǯ#l~GF3k7`k7okR4M@34ڠrx]=Z >byĶ} 4'r)X印"bEPGq4|g;4؇cT}&{iyJwIb~xxد[-XRg8|a'BQ5V-}uqoʝGa5z߃% x~/5*^}>/dK/@/+}D ^'wAPy\*V럿vb{~go\ysÈ"ᷝ)ꆮĦmPN9Œ-YN!!0Lz!DIe`=ێ5Y8/;3'VcjQ0 Q[K˦1gso Dו'aǓx~rxӓw# kS@M*\ۏ?"VGD+#2hS?XѽSDDDDOr DDDD!Q!@HDDDT1r DDDD!Q!@HDDDT1r DDDD!Q!@HDDDT1r DDDD!Q!@HDDDT1r DDDD!Q!B dZj$?$V<`j3fst".]q= +.bW<:.nAvflj^K: | ˏ5=Vƻcbo]\N222%bCa@x>z} +0q$Q9)|.@dʅx=S粢cW0krdЎdLuYٌSg/38r?<>v ձa@hCkJqdjhP;~^>yNl߂=Aܮ7WĹX4j= ߐPha4"j_l8v bnĊi5V"Ԭ?r!=5"/H/S?@}2DB@S _cYdTho-^o>h3OW ָS+^^ucsƕsvЕ@; ;7D3N)|D&̠y!OnF?b"v{dr;]و6"i}ۖx|$RϜ}Gk5sGw48KT@Wd׶ MPo>욎G"Kߐqi8JF(^Mۉcͱh,CFly EJ#Ss80 ZC1h-*ëD=tnocAUдa]tԮQmZ6VXn/#uRV(4h[60O!]ХU8|qen%O/7 [o|9D$$cWpj@aQbGx` lt}o|2q{@`(#`4/ 8()ipJDDD|BV {Fل%~ik :CcqQ^R5-FVJ*Sܭ&x(sk'HLFbr,^IUk!P4dgU".I8Co 0*Ss-N;$|\bǍaD ځAH 8+7ȉ&Iжe)aX3:UƎ(Y j.PX- . gDY_,4 xu|/NP 60l.KbՕ,鼂D ׊} 4˰*6-/NoT =}#GiYY"L}aRk$D:|} $M"""z;:m6hUą#'6]Og(Z#Þbז e67{ԈLbT\P_ l!B՜-"t4A =:҆={)ԻG%S?pA.Z}ijE)V+$> $hJk(f%,F<٭^z$ѥؗ"JJ;_оlԔ38eWLrz7OOst3"*%&Uy{S6HbY&""A N I"uRc2,-A Sv<6lǣ38n5yBx0~"V.^5%;EW Y/ĚٳK A7-ŧ˷!J_5^XjrrS"_< .R_d, vǪyKeVy-in?;i^"\EZٲ;hXOķ;pUxW|R*!1&#z3 .؎ :rm"4Cr`qXm,YmW-"E*ϭ]N'23-^2DDD/OwҒ r* !Jd/NbWHGHp\q}e0F6AQV^]p‹݈7{cB;B@Jz/'^ 5`FFzJ2ӲݹXOk~67;QEbʨPs^}'^ YԏCdQj )E\ûT4hVYq(TGwpXVMynJ\ j^ Z*S,;=_FbuN*e !=[>H9ʕiv3E9U 7bWc\[y~s"~<&ٙiHJCݟЖ?K1ֽ HTffe!9%AE&DDDpш|"j"^ Iqщ2zd :Gbn'a~NFȳkbҘ\ɂopW$$a1zxbTsˌ$e/0_=$I It{\Ɋd7r` FD`Ƚ2p#>ɄPݗN3Sau&ueh8/y8-KDp\4|P2[RBR*v#Kܚ9 aGAp%!C"~pe%#6()왈ˮ7mZ݊ƍkqH"QlÔ$aы5La4a%~EtJʕƑJ(vWAHp:=,Cq9?3g?U:cҸU DGS<<S:riFdD ܈K@\BrIeޖ.жxcӧ17Ӏ%{w\JV[ӢT4#e""""*0.JOGѡ<߀phVvgax:_D jY>8v1QUR)dKFLDDDD8.UԲABOnz?Y}W_Gs[X`[l8p)62.cҗ~;XpP/ .N/]DNp.?V~c1LYvW2}h9VHD٪O~G0;MB Tl(;'Q'<kL[ç\T 5p6[SK1X~4 kAQo5;k,5v!"f,.>#1iqilo̾nq#Q/lч0u(|<;qrJ{•͓\E] XkTgQ+x/ѣEqfW7y"f/Y<".ۄ]R>oZ&ʅx*ϑuL#)FX &Qt)uXvۇs>TTUE=ع=,s WbOa&.^QcaOG` *A!""[݇P WĊ[|)T.J٘6dzc|ڧ" ʬ8;LM^܏#txtuJxwp е*w ]Ĥɋ\7V|> *JL}ΦGhSWGX ;&'PWj$z~zׁK`ƒ V ]·\y$Z34{6mi?֒[`#x@2u6cP,48yֽܙQN/ `(Owae"PB,jxQT* $CJ7}'#M*WHDu wd 4kCD5 SfEDڵV㛣z; F<?Gd_=sfal hzXBclOSqkX <_1/޾ g]M^njySv^QD0xBhRJgsOᅱ_!qpd|}(Y$CңRߕy_0=g>ҴIr#=js;ͻJ?*n>"ng_W$I󺶒,&p}Y:얳KIfRKI:I3o&}di,o^CZqU.Icj5FTyҠ$\>)AtKu RZE-%U8qlGKO%-uBnpiмÄHM-O?;@*o;n$=2xTRc?~^ZΕ%-`4@vK^);mV+tCR҉>zDJWJb_-ux}t(U4~Sܿ1_j,ȻoI7Ex욌#RgKˣI;( _1]%#jZ"""\l! 76hnM1 K|N/E&mQ~TkC1#*![ Ո5FqF J*dxC(l*!Զ=NN68]܋JuԭIH .yhQja: •+9Ǚةo\ <F֭ sHuN*P=:ꂚ_Y됐RK@x୑]Z %w+ejEhCʡ\Тyz ?ttrȽ &/NjYN}/r.;N֎+Q ꆻO:cᓰ=&6._B|2b`]zõt"t пck(Vzv"[jrڡZ6q4 O]W-ZekTAdXl1NCuN;B;=q-8oۏIMmہ+ k#-^sl˔TUjnݰ{|pRzDЊ0妃V5X$$x#'M:yB,J=Z37lBW;VC.LtE$+\(2gx*mN+c9{k-3$MrPUjsX,Av֊ԒJA6ĥЄoR@p&*Fe@j^8u&V^}yhu'_Ƒhu]fuL)9g).WCNj)e#Dz+5!VsOKNyÍxu;" OM˿VZ4k`/6+V4:x]Jϗtxs'8FnOGxwE@#LiA)FMqW.Ҹ0 WDj.Fp3YY 4)t[p锈g t!'3 f -&vHZ)9vĬwbWo YE zWv!+ʘ32hWa 5 CѷXz8 6fyǔ[XYMh߳_C1]~r,ȶܺÖ#N" a+px-L^{b<ɖf6 >wgl%ֽlvK67|,]"m!+O PWDY\X2[D^ Û'c[&S +Mv`~I,フRG>< Ë>} FH>[CX~7:oyɜH,/^ł9l9 W|ډQ^4JfA̅(Hs;(G@cgVJ"c-N@ SҬ=+INQl@J4iʊDd8L(Z"sexq!ar0M_n'g#֪E# âCd2RCfM%3;jEDb0efC ^BNj*klNÍ-Jmn,.c21yXDqrp\9D@ns1ѸkFX2(%EƕsW!wۼB?-d^T^6'c}RbҁЈH#PlYȴ"_\0i]"' &Zc·diT(^og.U&גt '.#H~}##bp|=*~([kԙKc2+ %ǐADDD|»wm[spult]1ODҡOnIto;ݻ0 C>ZpJ<|JE~I>|:%ˡdiX"""} DDDDoDDDD!Q!@HDDDT1r DDDD!Q!@HDDDT1r DDDD!Q!@HDDDT1r DDDD!Q!@HDDDTi$A}|O:{~?g/ z,22poFEJxvu4k\_HDDDtx t\82YKBVv:&zhuZh5Zt rp:`9`ѦeS= ɨLDDDTp 4F]a֝0tw]cZp8PZxо]+u(Q(@Xݏo/o$"/OOl?_u(?S pϰdjtFsTaU1gx/!"""{8v7[~ٍ%2 [1#h(O5UVݝt^WnܶWB2 6B|B17b!DDDDwWKW ߏAĥ+,B!?qWjt G1òש5DDDD@$|6k!.\`` |ma2Y p(/ JԽC)vSOఛLxzcƭj Q;~|5r̖ϠnEzr"Zr(CNf:, hάt$&!b%9 J/MŢw:N(C$soWk|oZ*"|ٳE@ /Ջ9XG<kB9 jKУmu%:Z]h(`0؉ӈ} OCNYLaT]<ZVkqH<l2[۔Qg99aEȳl؝b陙Z "CFފcGbk1}rA/iXbȑ#rr8瓗[w4m%9ʼeV;\갂j@oDDDD_Zi;X SN'3 * ѣw"|!dd^+aoJhӢ!լjʠ6 '3a26kQ4l ";ހG4EUQt$\B.ó۠/QH2ySa7=!jP) gb!gѺE ,nđ 7YVCk(zXqz*tN 2=mh G)l\xP=0%hm .Q_*v#<=< $:fx[Ǣ,R0OL-^FH.;,V ⹬(S 1edC4vvXL]i$a%KW@;{}F^ `"2~A"Fc|x *h{\Ocoo!/[=xmx%<ΘҺ: BQ0e{4D- C' a2DDDD]߈+_$`Q"Balo^T`:reˁJxzP% :.C6Ogrx"f:1w'DYzO<tk$OE'Y.+{J.Vr r-s?ɛ :v}iyiĊ(2M,:죄Amch:}#ZLc%!1 NgA&""]azFD蕅r…t%l}3+DFEE)# '0k|c-FMwbi/bd2h . AJꑑ} 6E=O@j.\|1)o~e1\> V%h%_QC;7+e4Bsl&$!Ŝ#|geТhk-ТnN~UC_6 Sfnj[p&C%O Q~+VXNu*:[fA2XOxyp^d O#Wb a̫7[= DҥDDDDZvz= Y)q"~ݼ Fe_\;rǮgNkϜ`MpyA@pQ=_4oW0!a>p &wQoh&\þ%OAyyMQu\YN/LZ=OŮAfuݵÿ;Ok>a9GS譃fEZV".vkwK 0`YP8-V7[7DDD>Q$$a.Ϡ $>ꥅ-3q)=`Tp’g.b%!I<ʲؑeͥVdfXŔzxz98Z!'R 4`[4Fx(."":QRN:_!X+I՜ RY(BXn: >{-b%q uL4ewaZz<=^%"""{;g]g7?v9pɋ$F:@%_M,_dÔ{"!_,ԛщ0+ԉ?&K NWi~rdTTA%"""{}rhްX֏NZ z,7JXTK#M9YàLnMUOWaP6C Z"""/߁qúW /E(!( ^{ȭuQ=4DDDDw㮚:m lg鿓yŌ#RkVŔz 03>v6K@PZKDDDw,&Ghh0ZKP.y7вICJGĜ)QDqde簥>o1#CE/DDDDw/!3.Ecp@)Z!"+>_ Om%"""g $UtJ (@BIǢ jPIHP8AAJxMtHꤣ[}=]:8O 1i6D!j mI\HN@Wu ׸2Hd E-.sgoJdf}uT}B) GB>nh7-~.5)7= Oh͌ñGz#*ˌt,Z; wJ;#4żaI>^=3X(͌(c 5^?8ܷ/4zUSN󦺼c7S>пXo3"78Xgh\D] 8ǽ'i(_47'']&ER]]S\"Y}Pl۪x\yyQɦL#g-4c֨vF/E)3q1|!L]fسVx z. ᒏRvͱݟ|o|cKs%FFɛ2Lݶ'Mo?imBE^ExG{/1-]{%1kg2<|,r|Yf~z2ai 9͞~w-tTƤ x'e^{-՝LmvӅʇs%tf7*sSyKcwX=TG/^z{Nmm?6͡^bq+g޹[BL4Mm7W~@ڸK飿PYGϨ9zGt\#>H~0(K%nO=*iMtN>}pƣm? Jmeoϵ]Q)YtnnLFGw8L>r:{o sI@Fy>a-vg/OmJ5|aNCGxG)<&n"xPjQVV62;zto5emcGkijrQ_4A.umuN~:"]K9s$m d?ai7AH 4KƜ̝ixHsIK5;8kB$I'$6 m [hLDWt ,'ѹU1hU2IDJaz71ʼvs/Aq\;*3mRSdAKف<՗BX}pFGS/kaq) (td1"z˓ƌ2_.|A_$̋9iYkvE#U_$mDi܂}8x(C1@ON6(K5E봇y(|/<hDMjHE(V}$W4Q ]~bځ?xG8BN_`zzh1S^irzvrEf3@4}߰qt_VP8s*7ƑdR=Aa2sn9ؚt:m# uM`$BFPҟ|o{܌TPNʡϭQ53A g'<ƏIЎ0,29K%xf|H6herL2 kQ'9(",߷+ OQDʅb:>-7{ms\pkef2nH H( cvBq$E1v unˋ:KƄ99[>7bId'!@wHab.Ol1G+Mcꎃ+FmOtQ;t ~gi绀ܾΤCE^tdi9](0:-Ǖ/S?դQEznEzb#uDb-Pb95:ez/ @7ƺ^o^rSj,c/*fI9 ZEbd0z Z7;-Hz^LuFW+>+%0Z."tE;ޫ ڔ\ȱnf3TEQ aC ~@iZoj>5H-דE @3z[=rLqwn 5U/$kBߣ]ee%(Xl1w馀UT;:]y+/ʦ^~%\zR?CS=#z ׂ֭[IӧOo߹;@?ڛn΂֜)3%zQ~JDȞC',Q{ۨw~\֎c坯Ф$_#  Sl|µ!ԑG' <}6ʭl6~zT2uۚt*z$"5:EW0dPD9QRs,wx1 ` Uv.tV( "Â$\ZԤ9gxwwpO fS go;y;' WӴ1ӉM5W<៽QH71xg_;UxVX{Gh6,'hqͷgDZad gSn+ Iw~Dk7mM9_:*40jYdޡO}ʭ! 0TC7lnӥX5ܓS-K&+ x[j]S.KYM]} xr3j!򕆍4z7^z?-"&wɓzA7[?J-Znz,acMwޭzAߜ3cB[իgtix#Ϟ [dK*ab6T~ mΙ1~/&{4ˏGJ'WCʸQ#d7sdt9MHTNO5y<1bucͬe,|ݑ"͉͙&񯦧Q'V\%LƎUgQdv^:ݻKd>Utf<=>.forǓ'mBpEEEnqYv0i_wңNEu_݁cǚٳMُFlY@m? W\sm m?Cdp[% DA'{˼D}'䶵RSO]t%qKw,q# g}`M}M^Y1e y8c}g3~ jg}hd2h:eL^-|Z$sή抬{G|bgMXdf/ܺeɍ8*,h g:&j,tƼMwYjZ\q<.|8zIWڛψ{wocċcob0&0g].K?zWǰm(KADĽz0iu1nݻ{ԇo/l.*IX#^tnљ;16EdyT[[+*+ʊ\65Ȉ=HZipTJ氽OykA\&UD%3{^ry=gr*kl,UW>ѱD+^Yt/bohkd6d5Q1[c' ejnT]7xEzOm\w{<}~p\1jv߬ﶄeBݿng,1~k*O ڴZ. olҖu`:u ?nچ_95?5סޣGd݄O]=~bę;V%.%[ HD.8b=͝c YwPh믝wֻ~ḁ#uBw)- >W~ާ~(6ݴKYޮвXQͥ<ÏfrޖoD*?1wn#kL^d [y[r5_<-fon߳jݰWq.=iMZcmh 9q?͢7m6yb-rQ}9.{ڃt)!GSOF3WB=%ZSsԐ5j;cɫw9$]nF㎞lxESzАFݤ?'5D/&N} T+46%d<vAj>=1k+E5E"e]dmG{ :)L9U8h}j3WY#×Zܳ-{WLr{y@rno.O@{k  @@\ r@@.H @  $\ r@@.NH&[@k \ r@@.H @  $\ r@@.H @  $\ r@@.HEi֭$ӧӥӊRGݻw%}S$ŷ }4@L:䶵!lsc %zogŝ<+&KhyV1/j= # ޹oAaQqR(jxӇ^ 9IRCCCJZdtqo޺whm-`SX)4k)rK$D#sCMz Ywx=ZvԗU!.Sn&,琳CxiպT!9\xr8&cN%_9ɗ&^k,73` NOkuuu+701VV~q)k(fLnpw{{;2zˈ% Et%05#(xSI4-EkjR\ LQ`q+WDGyfϰ‹_ F׏{h/xQ{E/(ȏmZ9&_rj:qa$'x{G:#  -%(fPm&Ú EL_kjHWb_Η9LrTou˜dQ)-"߃DZ{;(Iʲ3=\¸dB7p0./-g o>->]'8zihTm}T('XE9/W@Hiie>AAP7OÙd}yꓑ<.E+ lc}HS̗%mN ( %Um㷹, H>s2s>zBY.S\,hGq,ysؘa`'}Y*^>T96oo0'~;R]N[Ey[2NGmD-BucaE);8`TvG/F qt :~6d-=|q =Ax5hCү&i BNH8~!5=}S.Ryl;6?iBsh:72 K6}I.-ϸrw~^24m\?O>Qs2ነผ^M m -v>XivA~ bpo6-4Qܑz^դ^U&1<ΎZf7n@DpG:8uⶠfڊC;@{$ړԌG(lReEy~1&}alN& T5<$t0/zRE%x)p`tpe+uYMs+ʸAkf'e~^ʯdg\Pc^;C6~nWw35!!Um*k*,rbhVDP'N7.=yB%x)*T'R]I'zLIMZIRIu;hkR{ 4H`Xx5A¹kOw G1Ց \Y{[&Ӓ%/n͇>QEhтJCc/s3fbA~3l#/dJ?$VOn ɿ2[dOb 7V*aLr>O]= ˀz_պB7=%:&:0HQl\vn 0T_KHȡ%oTCZ,b*qVD;rlLKm\sESKr pP+pa-^38K]L-"UGs+,Эm=#E,20":>>'ԖEJTR$w D\E<'/u+ QJ1U}lRT,A>APήBF-eQֵe`l?𢈵>Qr|/.JvlQ{ '_D#=5z!0gN}p )*pf/$#FN$;x:ff{$GMDG>e O37G3]UQqigl !{QR T9q9oIF_Dƃ;y)w6*eZRMgx\\t=l3n Gݺ QqbnL4MQE>"/c#:APuNtD_q4ōhL|~VS\ܣxkfڧ;s( tdk/=t ! E丫8 -w8q%+|?soc\oj1U3ȑ1QII>T${GòoDPD;溉f6ʞg FZIs rC_}6zsl4 A9vģV+}cl猟’*{B[$-̰pZF/8Cl9ɁvZZo`IhSyQOX$-fVUp+9#_>I9zlx }c(TKvݙI?X8Sn63T9^n2/34 ې##7K.UeY.t3g:S!-!4_=G3=+[K~.gNiv0W3Z=K3vfJLQc]j4q*GCR--R K{gt\P}*Kuܾ[AU]ۍFxXͼY6,=tl9u"Tpd 5R0-5/z#::2|AA \n(yYbS 6!Iqtl>&l;'} (i駶꫈MRomab˵z 7~R#I z%B!ثky[[d2oӎ 9tJQZ,C} G|i2Jk|xAxסL`e<)<^-kdr*$w- {6ӋPFȮ-@[(N6w SV4@[7@N~N:-:!\ r@@.H @  $\ r@@.H @  $\ r@@.H @  $ӧOIRCCCJZdt󳲲+++UUU8Θ1csH^jiiibbү_'O 0`***z/̌B}=,;6.v%A prcܭ"35yO5Ѧ⢌!Ϗnch@H^^ޢEPؠg?(<ׄB€3q'3,j Lq!3.j~Ur ^p ^qGۢx80Q?|/Xq[[-rP_οWܐ.8hvC;7}/׫Gs ̨n;xIDڣM"/G$f 1⵼"q}݃ӐqHٴkQ??PSE+ˌjB?t:Ѻq71ض2eGo߅E^*7T?*4fܡ>qBU{&⋸DĠx])&p8VOKKMM}D̜Ƨ_a@G]ejO.Y=II3'_'PW^e"Cnw;N]4 m3ነ~% l&Q k+Z AR .59-3r^R|}:S݂mGGzSfS&GFUgOIi7.p=?$2oQd:kbͯ.T$0^EGga~a{lf,ܲiZ޶. Jer9%FETSIzˤ_*pɛGyq5)3nv5j:N_ H TVV@'R\\?戃YҽГy¦^$Bڹ 辘9k4ԘhV*dk4c90C>vDD] O !N4o~@`qq2BYszrN6K=<3= ז(")S=-TśON^G]qߗFZ2nXz= 8J..=CѺ[XoV$7v;'h9cb>|@/tx&0i> IaDEE7n ܾ}{hh(驯O.AРIs\v LV͟<>Aq 1a^@%lq26cd hY; %jwZ;&59&a3~xd*y4!#1seh i{Jt7(\ryl4J UCPcc;&^2?^@?o ֵ{+Iz{Ҏ?J UO񛞉sp wU*:R.>vK# >YE \UѴ!gų"H('O^nsqqٱc'|y[ӱaW(>*6pB-3wE駆tG|[a]15q9tl9[Nff%'j\rQڬVM^otQ.@ +08yX$ \1 uiۆw/yTg}{gJ鎶b^te^;k(?J/ypni#Qw]eӝq8/tOMDkS9s[l?g~;vHw 7cqq86N9nRa1>Zn`, Q| 2Jb?^כe֍a*T__t~$|${+=?}ZviQ F-S]Uxl^u9Y,Ge>F9D:^PEZ)N>|EJ Q]Wvw9qNRpD! R%FHkԢ[8/&#;:*;KLr%ÍZ {STc%tT;9u+|vPbhjJkCRE0̐w WH^]VS7$Cƣ\‚88Weo~M~eQp~r~^w8Vs!DIw0/ hZ]z\k23IYft$fY#tzB?U $^Rе@.H @  $\ r/$%OO i*xQVVVhWI*H^FO {SN:}tWKK*khN:t5WOQ*Ӻ:z $"P&$/@!r<؞_YYpƌC/] >އz]ZUںu+=IBqz=gO>=lذiӦY[[JJJ)))Õz@̋ڹyOfq)5Ew> ?Q/*vt|b >1m]_S@%eYV x,lPΐEy zv?Ԝ*Qz=ԃgÈ' Tfk(yݽ{WgH?zU'Ε\/UH-}ȏzV~C=V)EH^^ޢE ?~5P\]]US[=tT8I

:fYL =F^L#&|(B5ٕ%s7l,KKK|>= ߿ɓ's;Ùi붤e zm@l|A7N7nrۨxHz*UC*+ɣG '2RG{s=ro;5~, ii ~yp1/ ˩&yh;X(~(354Zzɧ16Qx5~]RDCty+l?u!Q*V gS쿹]y)QB,4>]TN'^ kܽ{W:PB! 3hH$zMk^'+<\^Sq­p;gGEF}9xIn1MK;ϒEAk43t)V3='\CQA +Sƙ"Qh&zQ^ hS8$ӛ *xa>KQغ "G/C@rv`8LK\n]zJ֕qs77.]s)4]~= %?dсB(\E-Z?,ޢK:PRxx!;cש Ժ!poTyJDL(.Y5{W RTYyM_.{*8^My7L^$uۑ775hw٥]Fo8mr2~x{~mzn͇\due편MD^<[.Kj/EWqyNxGkx"sOs9"?J K]K7ŢcyI|tcچn:g|B_|G[sMS.4&7W+gs$[yyRo/-; d=][ZC̷ܼFbIzC~rND BdO-Cʥ)bIeee~ڋ7|jNՋ|z=Y RTIʲ7\%be&oo ~)mtIPjɊGwG7rxy>v Bx=3TwZn.Exnއ33֔Ey-s|URޠg҂ [+j;w,.mxư2K} P^^ƗSf Z]m|e oᤲ6ʆ2Z4J-.X ϳD[ۜ /@%\WBBTP%[ՉGitnx>N4L"sii V=Nܦ1pAY*j)SMkQ%_ 1uF:Q+u ӅK-s-!\dEC( \Ljn.rgzQ"B ZP? bR¦0$28/(?kQZ"iC_%bc/skUw$e[  zۆTf-:; "[ ǎ e;J3K.NʟpU紡2Nyc{݌/5^wᗽ @m>q?W籤d.ao-u` JK\ޝ镗V鴿/[]v^Vh(U.ZmdU1Tj~~B*GO$_y'r:G}*ї+nj-Ry@@! BA^^M^&UU'O-b&K5 0u`T өўz6"TKr*۩Z-} z^4cS,UQ`n I wyŦMCYInf.G_8i\|`;[x!-pMˍL: $!xJT-J[kslte ;g#ATzܸѝl z~^I)䅫nj?xp4_6YҚѤ"SeDaɎASyѳ`8"xBBCJA;K^rp]𤡢Jt8%I8xL@ ]H!u1:·I |66ՠ3ߔT \SrFuXTp)RG5>=}]ԛL[{h\oq5lG$dqj#6Nz(]ԧ(DrDb1:)'=)=F[]P`A-u't_!1A7wDmɸ^UK ^{NЗA| O XM/n}V{X/# VQ^U`l؇>UV5=msnNXk6Oi"`)zceJ)[}Ih_~UO 75?U7Jc,hjC*kYbC'qߓE5h8tUEIޤUd۩=H $Zb^AAYRFY\\\[[;dzV #73'ʸ?H!JVI٢MNU fWN]6,uC;'BMbM{iYg`P[SqqWI[wUweq%oҝ J*Y|̖TIF=I_<YH8NZZӧOΝ۪]'褦UvNz}t-1Ղ鎟Yޮ%Tkg} Gwt55hPG |JUǝGmLĆۼxwE/kV] w(j5E=^tZ-A1mm` C\t>E_YM 2llWl_O/uw3cG(K-xFqWTtD$.]#B^|*RE$ GM*(괌dUXՠP^mQ}-Qto Giê΃X5I UR%쭘 lv̘1 8~5kf͚enn>}UV/_GIzHO~ɋB7B?&oڧDXc7d`ٽUGI%~.&]3]ךkv_ҝ3 Skȗqp>LL\%'E}DZ5hY}}j1zGZwZϖk9(%:cG"j*%*UơvdGsׅ22?KCŧ}ǠnT+;RzS3ֵ2`T %O[ժ8eO.DTQUM}_*2UI' bs*Ji%Rw:d]*KQaiEaNQV LR/(-MY翾4o[:A}-;c%NRo-  )F:Mù~NaZ.PMrcNr;^)ϾN>2mS"ۢװ/ 4,gaq_7ΖF<5/@@ìy$N|_p/nXy1n_hiNJ\,Szths:+VU.7%{?j oEɩ4FGGrEfp΅!T'hXDzQ'u1-wCyMQՊz9yl-Зߨ4"mWcش jNBc-wc^Rרs[i~(OxMaDEE7n ܾ}{hh(Z*z_[Tw mgsph(K+ l 9?fΤqEM=}6ww8cf± ?s> my׊:-ͥ^XUF:E|\-rYC1X`gem[)YVk*T̗hMZ6oeۧ6yd^o"U5B+;Lu2.;hօm{d#{g͵QF^nXc6'Qxg>coFo  \C㡸א+|ʒwKZP $ɫW* kJ~%ת|^ᮭ*tRN|p%Ui~nPNW:eġG_{p%{B@\ r@@.H @  $\ r@@.H @  $\ r@@.H @  $\ r@@.H @  $\ O>'I )iaiCdttm r@@.H @  $\ ry?RWWyg_E%e˟TU54W7i<ؐ3teeez1k IMϊM8tDRKb0z[Mh;sq,x<@GٷttӨ&QPx恤 i i>XUOw0lM8?g+DEA%*3 $iok4PTTQixўU 68GP4TP$c'x6P C2P| M8m3^8mCʜN@mp?Oab)2$]O>Q xH`E@ $Y (/ *yTt/g{6343g=0:?ᨣb^mQ:65t Ll_Mqѥ =rb|zϜ%׹;7Krׇ.'M3,sxomqj߹('(;#7}=0W^N/?vAZ?ߋ^IG{Nʩb<SL y_^4z(S=7Z2s>g:jUH2x. 4Щ}V IJJ 6я!oi3ȹ_"DkJ&J6.덦lN- =NdRcm,rlPQOvЩ/\^0w @RR?b~nKkIQfGS׫ s\oGBbjh LùB+V㿊)XZM;z' u#KGf,b5q{\Q 0?^A1 HTUS &!kzYY6C`D\OlЄ:{^Qq[hޔ/z6>=E6,TDZsS|g:@GMRnP"׿l[6~?hAd&#j1-]Ei|^9@Ɛ6d*S>a^L\d)gps $Z\2Sbcxҍ7RWO57E+ בgI?N¡j"7VJ1>t !,'R}OPL1 Fo:^T, xHLE@H19/?EQX ?nԈ/Pɣ|), ]9\XPHji>yAeTHYUE'? z6*s:/-kW ҆/^,veKxP9ZNjg0FL'y&Y%*[$U A0XosRGBgAMA a pHYsMMgIDATx^XYU)IEA)u\;V۵k?c]] luEQA^}<:'C>T!  Q   IL  Bʐ$  IL  Bʐ$  IL  Bʐ$  IL  Bʐ$  IL  BTHnmSYYYIAAo"@e@ "  [b& aNYY(**7  ,xK} Z4ݿ,5kyHǥ9$]ˀ vS>"`wYPP¼ AA ]Lh1o"8m[_A!)@_4!]NMKcNÚ m,kWV)sϞ6 8Ҁ0z۽S;99Z´l֪ې箍P}.U}s](ov./'oce«0 p减.m%kV?5mjyp9QWS߫KHXĸ_s2aP.* TjꍨF7ĸa>A컘/@X4¡P0ILrs.].&9~d䝎? * bWp;{I-tj. U]"檫qn @ůݼpHk˞!ca f$]hm9.mQ?5s)бVTZ?{9|̙-OLJf>AFfٲeii?yVW>z:q .ժb! <=|P5nԱm+zjC=]m.Ry`ԬaM=Ϋ4^Gi' o5D qFDE/7]s8 -\֪e{ijlq^egubjcn_ӾES|J( UYk[֫{ȡPY֖z:!!<2ZA^F5{ܲ+p6];=yC^A=DA^KBw{?*z΂ 4DNn.sg0yS8}> ZjÐ1۲;*{4)LbRo_]] xfyop X"Cᣧ(-JlfS0Da+*((+q򣬤f),2`hR4}95tVn^rөiWiߺwC&ocb0@ q["ClQSRQWo%G}|_ (,t?55U-1?6a}]͈ OV$48Oo Ooz5jN$&="K2\W'B14Su bILEFTUQjd`zEph8d%b6bw7˥P`{VuScC8jNKi^:ˁ'BL,&ϭ1yp9YYwdfe߶7$,܆{q{mE],X`~2kkRC]?An ݇,aDɳ9՜E+ bP<YY!~~h`ޤ@ۡqnALN^AoypNGl"[Ez:al hvt lָK w/t3HV-'K1{vp0yS;TZԹK%FAAT^#5Z4A d߫@/32A뚙6) ӦH!/\}ߡuw1qsj׀mC!zΚ:G)wȰF"o̚2]S:aowr;u}.v֔qJeoXS߻wn7sʘn`zb CCMk"3'{2յڷ}Kp;oYEsPBp'55C H̀f,\kX^0; 6{G9;@ر~E=sDPE+o2nD&8/OHᦍY275廑Yd`Ƥ @_qO_8kʛظ|o_s u\::z ?hpyyy~,JaGOY'--fL4ڪ9t g(e yI'y3&1SwCŽ'x }3PKbǮE UK1ۡwl MSqI7 ObB@2_YYٺf`+,hOHĉ4SL21\ԫkָ}=:SSSsvLFFFEY' &>>1}Brr ||ậ[TZoϯߺ~NxiGF#Z;:  M Y[jNNwnc߂% %F ]:ڱˮ/0=@%$& V|Q\c۲K+,"QэjVT7$"Ŭ1+IꚨQROn2e] mQ,m lPBɻdžG3dVʀR=̉P.=y 'j5tG"SC Z=8n[6gKu,AzΝޕMAD xȠfШ4`SdE1  L,ckdveU.߶`Yà;6Onzܟ* x ~S梚S,nё&3zA0&qy1 7nڡ3vm\yۓ⻕uƎ}9yۂA%SS!ݘP<O=I/~-q΃fzn`3IE~R %~ǶfLc%ԷG^EO^]Cb5>|(d_iPdbo'&2ոlyv 5/OHg8F:NtNFT  ),WM~eG?} )mq`%k6+; hc{7ЧJ9Y‚BRaQ ѓi9kk 2?RPPYE] 9kڹႋo WV v)(ԑ(o݃wϳr*;uA%&\NNVK7e[2_ =XѹäYS.8r ЯgX#/m]ڙM?l 9>{#  @yT'%W !d 1XvFOԏ跜SƏ4.>{ﷇN -ۓg~ⴲ8Seee*v45CE=0aM.#5kDUXy^\&yiijP^Г_'rO^kiU384 C̱=mՒy↘$} {iFbv7U,4Z4Y2g~*F,AAK`cLRoL }qAEYYC]-$,<&=V̀]GU4oC:=ľ.VK@‰uORBTv•ܼ + ?"Էc=JQ+h\8OLL0Z3 b⺁aTAq 1kT(R'd%]4jHuhcM k\Y|  bbrgAAQNEAA!IAAHAA!IAAHAA!IAAHAA!IAAHAA!IAAHAA!y),,LOOǿ,} ٳ{wVV&~}1cծmƹ?_*7<=9AA9RyyyǎݲeI1¿obǀ0`!<|AAH̲e˸S!>|]sKL``cfup^Uɱsh'O~moC*되\fȑrrNAkGbCL9㞕kP(׮k!77qYcK'&&OVVQQ0G-[DC>>F _ԬY֭k1׮]޽g6/%JOA7%#ooH-w }zE6oMܹPjUwΛaFؚ̄0qb6m-,,U>=D7v6vxS[mg4kBMMM̝;VKK[?RYY9VP~~J[@f~;uڴM KЋ-j y}VHЗSRV^d.԰a# Mk/*huͧLVN5...)1 ^̘1KOOںoݼyWa 2WEYIL޹skrmXlѼKnN΢ŋ!AÆ dggpwՇpt"uSn``dȑ[ZB4ÈW._^d-hnnUUUy.QTz  GT?zԤIZŷ@5Zjѣt%hP`,fM䔴Tԭ[XXSZZYE15ł k655uuHCvE9yy-11I¹g8ThkkAA+ ݙ'e/QظXH4?yi6m8]JjUc***h@({(‚}.LC\2KVBbB  [@ksvVvhQSU^&0U+))1EH8V 55Uuu\%33ÿӭkg={t;wlJJ2\iJ̝4zÊC8rab@*VPR)ɛ0hqgee=ycfVG*jPWSGERRRԡױDHNN>tడCV#l.S ڡ5Ꞟ^% 4BTVM ԧJLLTSSSQQ%--=55ESz%Ҧm[3.^"8ʅI]]%K?p𰡡%1pŃ¢^vϛlNܹ3VQ w^qNJ?RSݷe#GiksKeA5aoߊAARVPrW\.((༊ 䌌CW8!::ׯUԨQcf͚?~y!%9͛0:udddR7_ڱss6m:{\ddnf)鬭]]K 9RšzĠ`jbUy}AA|#HYbB<=ʕ{쎋˃uGu$P~׮^wn׮+idl!qݾ}HW*6߳{'sD/_C`P z޾ Dau ^xyehdĂ7:x(+\/N$'%sA!moz朻 [e%e__Ԕߗ.3vufFC\%+++//WAA\W 8>l99yȭ ?Wx O/_7~(/8y5(0pٲ? aHŰr/2"bsf504̔-R*)+  Ujz("O/wL23VB% TDIoUբ_yPATL_ OZ2WA(yyRZ [d^fLDWxUxqӦXDKK˲eQV"bS|Iŀ'DE$`.5YSwFAA]   HSbV665ks  2U?| QXXxw   $~QNAAHAA!IAAHAA}[TTTRRR^^EAAǐDV_VTPPD/AACʣ/I_AALJJRPAAבˣAAWES'qpw2sÍX[KJؼicjj %UGnݼɹ""7nX5g_W!11q5ϟ9gΜT6=- z AķWi88(,,LNN*3ׯ^=~())k׮FEEqn^xyI䠠zsϟ3g_V!++BBB!>>S}FѦuؘicSB= [I̯܄ XYYs^y夤$-ʢKyIWMڜ;'=-˫AÆUV{$&&";:1cCCCQpԩky2&A%3'''55rn! A/+YYYMOO/K@eS6,gR~(,a@u h)xOK;{Q-ΟkF58a+?."QdT$ڪVڜ[׍q?˹3\ bUKu*RUX)^ YEoǏBR4itIzXl٤3zXDÒs͙=W[Gti@k XĄ9a_G|p۩իϛQŋ粘9DFD,YX kի̉?|5g\Ğݻ^~tߗq .#P7PjZػqݎN:twn4KUN gyk_p9> c6B0 ::RSSΟ?d"6cm`_络8~zq!"c\RZ5Oϻwo߿rrG[VZPeQ*%^[0}22B,--357dͅ#JBsǿg򅡪jjb @2%%%u\Q7a觾O[Bs^69%%**r͂ZA$(ܟM o&0SG/EAL??ݻvN0 .@ Z-A΁J~~^Jrѧ>| 'AK &S DG8$4}-|ADV$&SO͛ M^̬# .z߰'Ȋ1556l8h Ϙ1{ ޴0XNC>s Aj1o($Pej~-:usמ={6 ۷ZnŋGЭ{wQ5/-eF=?1m ;|+3=--0('?/ΝݺbM졪i`` S :}NRAM]]S|E8##Ą} q-[X&''sn!k~8Nj3Q]KK[['(w+#™8}LpC(q(/z}%SO099YvشVwމ @ ffffGCC*---}Ys D7nx_R1QyAӋiGAT)1^/,,葔$,0g ǎ fm:^ͽz[X(U6[mmli|LpPRR204y jX`y13Sn=wB1TW_GWGGE4 ͝;wĿ/zQ" 'EɵC42sӧOڶmW5 jϟ;K14˗Z4F?$yIݞ=a*A PcG۷ĉ㒛F]CCFF֭[H]z'?)-5krȡvLC#! &KHMMa2븊<(qu???$רbDGGEEF6h֖QxWM/[Qʌ)!BӋi4AQ1lݺ5֭>{ Üj֬ h~ waioߪKmÆ ֵ3=oY@K'nn*l!>|eƥP7.yN4N]2lYf}VP/ʢQUS[pرQ=ز'666hU'=wqY!gY:t/+W,ޭK|}I{_h 9p;,[tnj߼A+r ]| uAw5z VĚڌPٳftбOL7:}W f@ݺ={;w6zÈ_BC&L̅}WpaF˰6TEGD@Bb|Uua PV?3g.=a8{ [ӋiGAT]LYYY"c E+VUQQw +''[AA EVr% YL*:9:Ν7aaXZ99yї5%iɊ %W7DԴSΜW9aʼnhETVV ?aX*+)fr5`U4ga+U^n eT1͛6̞3چ)VRWL3Vܨyy-Vf+M# *@eWʃ9]UMMtUO]] KMMM`5EuDecc#+ޥҖ@JҒG]߇6]hE/Y@vRkdqM:*O΀RG ***"C\GM}}555|+g|UčzPjJ8OZ! DbF|nrssٷ@Zdddk^RBZ@6nd7nxFDo-hs AQ.r ]L78Q?&#Sf͛~ݍJxkjJAĿAAAA!IAAHAA!IAAHAA!IAAHAA!IMÇ¢?4/ 3 ω̲e˸S!0GF21ܒ; MQN|򥕕5yyysFo޼qas 555W`>yVV @ǝ%&$֪]"IOK۸i4%}Ԕ*m[Y[+*J^ciiUj1 W/zܾ}A߽{ʕgHKKݴq#&=}W97mp%ݫ_RS(6U!ְҪa WsH̹ ?W?JJJ$#?/ڵQQQ[l?/s^R%999((^Hؘ<-=XYYY[ >?nhhι,䉏mjE_ll<=߿orrr,Wjj ~S@kΫ 4ܼy++W0lQU+W,\83饌$e?]%' 1s^`ݼq}b'WUS[7Vnݺ9 .rr&Lt>Rۨ\sK؅*1տ1n>ey{y5hP6͛̌Lc#c=~ʕɓhiiq^tcGbyU?~ӳc  \|<*U޾};sƯ }hη4xgOk\eolۮ]^޷o.LK̜˹BKY`WPl$*SUUUr-UBsAAAAIIsZ:D4s<}K;{ \9fQjQSVqC) .Bi5X|g*`;ր2 /s9#D; g m^ gEbE3QhZJ̲إT7V--Kc\19nSjk$|ϭ[p^"\XPA::ܕFMMa-ZܬI&Wyҟ R`mgεiۖ* Amr:t.-pի/X-p陳fO Iėj3)&;طܒ-I-Q1רQܹY&MԹ3{sEw*E35o.Pqq}nLLL&M'Nb˖M::njE4L@.]ޜsutK !+M5|ﹹ5oF~@/̟b db׫W5Yr𐡘G]\N.c:VG\~ 1666L,bkXX޽{Νv㼊:;;_HMM555?a9`\H];w%+mllЌ 덌ϟ;8q+/;v2uZNN6O( σddesqiN71sB} 45nݜ$++;dPT\1$_ןhWsejBBz\\ܰa_^^DrssOrh3fq/sn:u9z0̄PVV7=B;nDWV||',99z&LFF6**ņXs>.6}L__ʕzzz#F89r8::ẑ7=@&NN5kѣ'A֮YӽGάsmX6F^ҵʆg֯s„k12;lt똱ǰ["];{ytLllrRRũݻwf̘⼊A#>ҹΝ;0S.'Nlo`ks~~EOGU4ŋs\=:*y-̆qDǀk]n55_6,U+WL8Ҋ] ׼y-]Gpq]2@ӁC:rѣ8fޥK`۶Dt!g=7jHYzZںkV;?:d047buٶu+* c(씔T9Y977~:yr_l Qf[7Ot]jGy[KqEٽWN=.^}/_ Šf@EY{ ]PC"&;o/| (p@@};wvt:СCǽ{vY2sf0} aLGEE[ MZn3g]%ƾb=( (WSgHTg ^ގѣ0"1g$U\`Ľ;xְ>}N].o^vmh.'19R@`(*H׮ݱs \X1^^;vq GԪUҍsKܔ QSB'|M٥Kv`[occkem͂0eGEH|TVY޹} P*XQgθ{{yA_k8iO?E}rrr4fakk5jiڴL```HH۱cȸ8ǧĻk|SRǍk?jq bno8Լyf͚ݺyÒظ\u-ƍ(**I\(={fwPmx{=,NWDp@\ʺn]-PN,˖QRu/_cawŠ!oe3:LP^|H4'Դ)SI~GQQiƌY[)!jXhXfV&2CCBvYoTLY] ao GEEIॿ?-Jر!}(6m߽{ Mߋ0: (Nm=s^1bPAdAFv( J+ efJаQ 'pb=(۟3P1cƢ@Ap -=yྚj>v-ۿ6LH |+?4>.Np["55sK/rpxoaaa;Z:l1(|DZW$+c\RZ57<):+Ϝ9WHHH(t)TVQ.s Ҕ"RR0/㤮ydd${r' !,CX߳ UUUSSE}u\Q TvzUSR"n,hŋTXXtO332q A86El(toUU42'STL15`ߪFCSC]]#*D~M;zzh(A1㡙mJQ6JϭŮ0( xgYA!gQR]w Y.Ϟ;wvذ_$j>vQLjիWfffV9r 'MRt "ߊd@5bJ EOVYkfVőK=BBBoɓbGkjj:lQFc3fߔwmygmQU~)-y9yg3hA#cL>[&GVRT\zsg pի7CEm[7iV__7S7bzhw%^DZ<642.Uv1[z̜[|&ؿog R5Ǐ ehq,?͛}bV&VVlLkedf$A_oE`hdl7?/Νݺv޽STѦ&<4hsN@M]]S|E8##ĤQmOiK}w [Xe%f,Q8  i` //|*Fu--mm^KOOgʢ(g <Wqܒܮ]{lllz{.|EӦM -^һO_ 5=^RNMM)haU?ffePFVV֋˥$"88siie|p7,,LNN"I*&ՍR%++'E9V{[̽ý ًX6mۺ9~ވofURU^R 5%¼<IIݻwyBB±GCB?u,?dOC0cG{- jժͦN[-[9;+) }?be<0ffu֭gΘ*~3^},;w_*1ooon8-zO{۷>}ҶmJڷ?W/)._Ԣy9>A[7ع]+2utz͛7ʍ:t@dd$Vk;;={vC1- G!`UnFWGg1+CVf3_wuuxY;5={Eo(r^B\r-ؘ#Ԉs?6UMXNNTݻvڹ㢇ׯ/5BBB9Y*&@32ҡ9T>ACSCSUTڵǜN0f͚A`+4W^n,Y1n]L LzuuЅ}zݫDۜط90;7 44d@#[O{V--!-_"11qذ!ݺvFG-3 fTz>Dq>C@'MS|q c֬o߾8''!蛋jj .;v4ӳG7ww7,챑 Zq]q<:slY2`:+w֥|>~پ` W_'FNDMQ)'*)+kK!z􄁃oaٲCtp>fȀ,,9^#Gawy]˕ o~kVupQh̵kWK"SӇy.)ѺQBV5qHZ#~A(lghh0R `o/&MA3)"#+-++%555bihٳftбOL=W5jԀ{T.a1i yUl9op!cڴosv`ݿzEx}}͇ڵO?:u1^PɭAR}S@VVVA~Ƿ0 BTTTځWAAQZpeR*DsYժɔrXXyy~ɋ)IKV |YXE/* 0⇁N[ćhVfv9P+@tt'DZCp=E߷)Hhjj*yW;e3J%MBBM'L>"&uPr\EnF( ϙ=!'//:U߯]f>~ų_vŋSV`J]RyQw1 5b20ǩ.?6<2fuu//W655hS,m $-Y1P}\UЗAu*8DLO[[[WWM֩^ByޘL>44ֶ. Vfq9!/m^zYF O?Q.EP_eeeqZԉ&сܿo^(|!C|rssٓnJRyQP(!+jȇ |}IS}+cii|W1>>WZ߹KW.ƿvڋR\I$ ;v 7c7+Lff>*'*Q3_NNXQSSSQr. /PNeRSS=zװQ#ss/s"///00ȸ@֮m& A  2  2$1  )C  2$1  )C  2$1  )C  2$1  )$S'qp鑗w2sÍX[KJؼicjj %UGnݼɹ""7nX5gY׮]s9-=o|3**9޽ sSӧuaCNw{N0̙Ay:s~%>> ΋_!rzzz̙"-^oۺu'\\wNV/:-[XX5Ell v2s^rţGĩJ,7111H{9_)}QN*b1L05%\ĹEPQQYx 8/RS6IOKjаaժU9/B;:1cЊK3/h{wg♯lѼ)PLӲ~F?061Ἶwޙ7o#tڿoȀtuu^n|}.^V^¤OלTxU%|}9 ~!LVVBKK`Y悾 oǏBR`N4IWWw˖M::njE4K.޿o::K]XRE 1{nnM73B_x`\S Ȉ%K dMzXiDeDDcfr饿allAG~Abb)Sg=";yuvvqMjjj:~$sssX2w'f3gؠQٍ?wNQQqW._B;v4e괜lPLh7<ɖE:]p542Pغu3د2gLk֬٣GOΫ8-mmmSjZ{cƎ}95k,9T-==wuH;q$޹D)1Xx76mPg|RӐ}uCy/֖?Y3"Kn#F8(++#\ #:`]v䤤O1ȈE7 ο{n̘!>ҹΝ;DM"defԲ%Kc ѣ՛]FCӑ6kF۷m]d)р)Zb9eTS:C0O- 0q^H8-W@u [$Êx{wN1i׮Y'gqԩbX1c3h#G{^^f اO_yyR̜5S8AqoZU+ÿi#""\aM7rAfu3nw,[.Vж[kծ_A+)+;8{գGת]j,iӦGGtʿ.02}9o,(#G1P\ȑC%֪Ϟ5j؈ՅݺkUTVXV+#9F:}?6Ç 5rJJjrnnNNv͛bc545xBQYN\vm3K( EV=w wM4A橈pa}9E\Lۺm9Kxӌ) '52OIL9s,SZ&8pojj,B:wa٘7mhii^ј P04;+ ,Dŋ>88skYn]\(###((Tb@׮K&cNaehm۶SV~f  eE>7k<1`9@3"wtt'A<㺨X_۷ׯ]?!{;wlwwwc9K_ܵ^<ާo_al&``bܥ-FApt[%e p{9i S=_)]VV'NeyBBGEBK*TV'R+W.|AcX ?}W&2*RYEkX-,JuYaa'u###1RfN ʃ9w<6:!5ZZP00Y:͹!5 Qxc_Sҟ/ZmcFڽk'tfd NAˣR%K9tfuIMI U%C⤣˹TSUUCrn uKM V %HR(p<&`4nҊsTUrT qufA?_A :#---{+kP z޵H8-Whs.lܴӭ[9߾}klRi@@3;mpp6oؽ[n];͛ ϝ5sFj=!Zivퟛ7o1AAA1.6W u=6} a_&xQIs^bl~_ŏb]׎۴m{޽ zx\`m;HNzZZ`P1wֵ[=DPHvfddp^"4h3,,,F88prdoK5uuM-cc㌌LcF_`m ظqs d-rmگ^UIpPPM5̧bT P#™8(7P_ ƜSr}3m,SJ #o-?O Z1eedL mnu\/cS*ݦT ]y'1Y!n|&0.<%|I$hǏ%%%++ {gAZs[E~00 <ćeM***rC/FPpP=wfiO@ b5BOoesλ~ZZ}8kJL///1a|z\HJL߽{mBB±Gu(ÇbիWnߺBAZ,ok۠eK;gc+ccPÕdfVnݺ{v6b>ׯ9㥿.V̒s.TΝ; 'm۶!ܹ}'m۶䊮ڮ}ξzJ4˗Z4콷F?$yI #=qxyMseCp慅vUF(hSN>xp_:MFF:L/1$?[Mp V1c:u0ѼNϠ<1AnU %uX{ٻW6lPȠ2Ç_'v0-X*hWHLL6lH푷w}# -շ_*w C߿J)<'N\ ~6ǬY3޾}+(pNNChe e…zcǎFyz T*Fi]qbzlJ:t/+W,ޭK|ϝ$B޼"Tb(FFF֮F37eԨђ(lfΝׯoo7wNmw3 h~[PV-"鑰aFPcF=jӦа},[JҢOP @u1~O뒌 Yn#֌իk9eM0R­jjje3OCgϚѡC>}2`E..UCGGWTթ[M8^V 0vQtO 1p`te˖cG<`Aoݲ֭eOZz{{52y_mӦ?(Ua ȅ}=| +WhG]lgfbkђB|#|b *ǞUxP̌ ISv 6d+((b:༾\09*+W0X32U)Q尰P'GǹtNtIҒmVf&\Wv{•MMM`]DuDecc#+ޥҖ:%iɊ#! 8)oEQڪb⺄ϔZ0q,!BKM ##ٳgVV,g5kv}uC"x(/8}\ݱ}ݻw =3u ⫓`dd\ޯ+ }46.VMU >Vak FԴSqhhWBTTzѝ~M'O|[ZZ[T'ķIL  BЃr  Bʐ$  IL  Bʐ$  IL  Bʐ$  IL  Bʐ$  WN9G^^޽{Ç7nx.c۷o9/)QPPrb󦍩)TAǎu&߸a}J \c|I^ĵkל?u9s:;P4./A_Mbң099̜ _z񣤤DK2]ŹE{yZZ:%UկϹ?nhhΜc|I[, [O|н95GM611]2Ǧ z AķA܄ XYYs^y夤$-ʢKyIWMڜ;'=-˫AÆUV{$&&";:1cCCCQpԩky2&A%3'''55rn! Ay}JVVBKK@eS6,gR~(,a@u h)xOK;{Q-ΟkF58a+?."QdT$ڪVڜ[2ӊzLLiX-=J/O(:E]SrOW\P)3IXƿaݕ)<-sl-|ADyZڃy6-8deq X5Ν;_5iҔN; +Ew*E35o.P}.$E&M'NEk˖M::njE4,9.]ޜsutKf +ML5|ﹹϯ^ 5/^X0.)@CdDĒ%^,_l^~Cbvq9ahh?B#ׯ_300HLL |ͻo=| ץ/ dAiiJN8xQ#G=zX N/]+cͽu,T@_Λ>X6mrXkY4aRSVZiQޙϞйKדEoUbaF={aBZCq[ŀ]3.b]_rt:q|RppĈƍpxx\Inݶ+hZjByVo^vm n@;vl.DIӦMb ~At+WN: N]tQX(mӶ-K" ֮qv1 -=aDN5z S}?M3$\zU~QFjn£̙3Q2. w]tDG(Oo> |M٥KvTZۛGEE ͚@,[-"ܺʙ3^^ЗšL&NO-qQdkgg()|? *kk5jiڴ<CB|pݎ;EEFű >>>%^[}曒>Z5v3e)fSUPVаoʂ<:tlin^|# *̷smqq[w)ϟ[xŋ~ cX6XXXT]N?tEzX8_^T I.P'ݻ߿_r-WHHH(ŲȨHe-BR[X>RYaa摑칹0XwyYPhL0TUUMMLaZ棤$.+*O}ڷj6>RfN$(ܟM o&0SG/EAL??ݻvN0 .@ Z-A΁J~~^Jrѧ>| 'AK &S DG8SR"n,#/88wLӖi> b|+U)_ߧE&WYkfVőK=BBBod~Ø6}qg̘UC^AA{llly`]8"/'9~L;v3{DWl[@oݺd]{?t0.sbbbjojݺ5/z=zBEռ(K  t{۷nPPVm6hኡd`h+԰bfVnݺ{v6b>ׯ9㥿kPi;w_*D7Ndޓk=/hdpOmۮj@UUv۟?wիPch˗/%'%h.'{~(zI(̱G{qM!##{-$׮]u듿z5.7$$ȑC->tCEFB4?@L^eqyP ~~~H' *LttTTdj mmmy*g|UQlN;ؘ̬)tuuٍ U^J76Ӗi> b|Mٺuk[}zݫ9sլYF  п֯޾UK;; + ҭkgD{]߲>[?ۯ?NNU~ C}!ʌKo\*8ir'efx999^E?`B=}cG<={tswweOmllЪN{tIיfCβ:u_VX޽["~ȶACEEEdK㨩&[o򌯊^Q/Jm2[br^IRU:A(]̈Mnn.Z;VR_vڋWJH ƍۻ ψp8~n-z ||񍓙Zoq0"#?~Ff͚͛77Ԕf ?$1  )C  )C  2$1  )C  2$1  )C  2$1  )C ÇEh^*pgAe˖qB`F ebĹ%ݻw,N:K++k-% ޼yjjj4}kW9_;KLHU6EqFmiiJ3)U>""ﷲVTrs]zҪjժc|Я.^}v ^{Ε+riFLz59roڴK8߻WtP l>ΫB0caUQQAHv3-5'''sA~W?~yIF~^޵kW8~~/^}<P]]sK X[[jՊ1xz^ހ'*Yb5>לW96hyV+V200`ޣVXpٳg?”00P7nxbE8+A::ܕFMMa-ZܬI&Wy_Paxvua W`ZC3g͞:W /ORLXwxo%[LcZ,89bQƹsg &MҩsgvUrsփg6k\b#]]ݘ&MN4IWWǖ-tuFh.]x9j ߗ. y&##bsA9L>e==#9=|fʛ ''ǚ5kѓTk׬ޣ OTGGGg96,#W^Bw eCP 9aڵ͘@ŝWqw٣g3cbcׯ/T<&(N]6޽l6c,4U i]w܁)>:w8q.&@TUBnظ`c`ty \=:*}D! O_֭fK<&j 'ZZZ@o!35b޾}KGWc'\\ +((agtЁu9rѣ8fޥK`|,cؾT3{9ܵ@ٍd ̙%2Mnj'4jݺuϜug2abTh(\]OIo>###SNz*z;G22Zhƍpb Nt9 s6ǝ8|98$::#!!!zzF"YuJ׮͟q$&ww7V*~H E8?xuعc.L2jժƹJ^nnJr<?tzc˖vG!v= #6 $\A_)1tڮ]{lmllY޽jժ5k޳;o!TJ> wo//r4qҤ~j"[;;$i5jPUQ53Ӵi3yyy 1\cNQqqq,kOw|$;D10@ܼigoߨp"y͚5u&%qY9ZZ7666QTTP7<={4m۶zX; $uݺE[X-^t vJAB.g:t !&$9hNiiSNޏҌ׷DS Cհа̬LeeeE!23F+gzꚖVh@ {V>**,H*/QlWǎ郄E9i[f7o^0Yo$EDDDq:hghѣǰ,ʆ>RaJb2Au4lhIhcX KpnM΃w -|-ۿ6LH |+?4>.Np["55sK/rpxoaaa;Z:L_J^~ˉZԫe j 1 I.P֛Oa}ʕgΜΎիV$$$~EFE*(xw 늹[iJ)cii)qR<22=K B!BPY)n"R)_A!)8%DEEnݺYȋ/88螟$9gfdٳゕ+(p|amAf0'6Qުi/dzOcjU+Q!(FlUT}::,롡MLjf)E|q(,EL8@ Z9*Cg_<{{a~xBK5߻{w1^zeffj՚#G͟p)E?> +HLT#T`:o\/XPjffuXIt#$$dMO<.vÆ 5j4;Ǝ?c,,\pM{؊g%`ٓ.rϜCGGg!FƢ|GVRT\zsg pի7CEm[7iV__7S7bzhw%^DZ<642.Uv1[z̜[|&ؿogxCPW<~h/Cucnۧ{766f eZ+#3#IG?b|+@#cc̿yywֵ[=Db651 yA,,,F88pr jꚚ-Z&&& 8l4~J[k"(0ԏ(+)6-`IpPPMyyyS1kiik]zzzxD83UE9n@@Ih=pTĿ$v;eccӳWݻwI%6XK(6mj``h}J؆߯E_).`PHlLlfV& edeex޾\h(ѻϟKK+・aaarrIR1n\\\nn.疘‚~`W7AQMYY9)q;w0bW{CAl?B3;M۶gα7_ٹyyUTWT?'B*|M{^^E$a ;A V3:uݳ{gLL A_~/ut> j ͝;wĿ/zQ˘i7'{^^O>i۶]yk^ʁ|RrRRnݼcvҵ7o`+7,X Yt$(:,IrVںu]]TŬ Y|[Xԃyf]F?y qe|2}(C<'N\ ~6ǬY3޾}+(pNNChe7U,\?vhgnnXc#ݻ8yuِ,meJ9t/+W,ޭK|} @(GO@N֬^ 8Иk׮Dv{Jhըqc!+G8dczu- 344tp)k0W `ثW;Ǝe``п.#2:ڲy}DMM 1}Zc:tӧ/Sϕ7A5 ޽+܅Kw":ur zzʀ&ۦM;4,U9ؾmkdD… ĎiӦ.(P߮]S˩S'|X/GpV վ) ++ ?_[P̌ L!***n@{a㫠(RBU"Ź,?//3+Z5U urt;o>OXZ99yї5%iɊ/+3 E%ú]_0i^ʌ.*s>8vxh@?8eMMME?OZ1cY[[rfC^YIDIHHؼi G 21:J8(p3 >9 ֐*i׮]3|ĈF?* ڵ~/^L2\cH]RI6 x.&A|y]L8U55>Ǧg^l%ʦ&]U2666ʙ-u%++J1"Nڟhɹikk)`>:իWW(ߑ߇6ܥ,.g6EͫW/kԨ'%A⚑FQ%M}rPӆЗOx^6dO@nnn+L%((aHm5[A|n$lξUձuv>ǫWܥ+EFFfvE_)$P;u wJÆƎq_/?uLΫ+G?8+|999ׯ_cEMMMEɹ _A9AIMMyQDxxZz^F-=o##~+Z IL  BЃr  Bʐ$  IL  Bʐ$  IL  Bʐ$  IL  Bʐ$  WN9G^^޽{Ç7nx.c۷o9/)QPPrb󦍩)TAǎu&߸a}J \cgILL\v|CϨ({f*WITL>>gL֭k_ 9qYm:-3gNg٢|U~j3-5Ie\իǏ%%%r^iڵuBl?/s^R%999((^熆:O|048/B(3aD.VV֜d`rrRRAEEe%wl⼤ &JMڜ;'=-˫AÆUVbnntpƌrB+.!4!ݽC g?6E&&\@1NzzĄ"ܽ{g޼G?ti*3##"Ex}xo::H[zuÇ >>>_srRU%fNNNjj B@ T0YYYMOO//񠪪*NٰEKU JJJC^D4s<4OK;{хK[?֌8J-9j pŠW"(<]E_ȨHUZ9evb9wypEqm(L,!E`V@ԋ%wta*Ղ,gf HSR/ʃY{:(`)3 F<㋍R\A|||BBu)K/ * T?u333Ts nZ.֌ѢOw>Y(~z#F#G_f``hcc;yTYraaEHGq^i/z\@S0A0.];wv666hFTvFϝSTT8i˗Pǎ;M:-'''5 OledesqiN9@} 45n݌-8C Eřq ''ǚ5kѓ*Nb[[ۈE#oظ+cFT-==wuBa=w7jkkCL9g^j޺uСПGӮ]{ 8q[wF޿n,<6(ҳgiiHؾ}88skYn]\(###((Tb@׮K&cNaehm۶SV~f  eE>7k<1`9@3"wtt'A<㺨X_۷ׯ]?!;wlwwwc9K_ܵ^<ާo_al&$OZ[ZQ_ȭ0< '.w#0ݳ{ee (q[&'$ģ b QSz Q(sZAi*QkηJΞ`!xW1ժVROHHHL(LxHLϟر=N}) kF6WnД u$YU1=bKLLؾcDdxޘ|X}zk!ׯg_Ay.]ٽlYs{s͚̒@,[0d0mJlvϜqK 8iT\cIJ*ffu6m#-e;vcP>>>%^[}曒glm0**ʺ:Za[z 7o`fDq]5GVe߹KW oz(TQ axLPIM FG߿aieK;B_'FFF֭{Hp04dt&|NW~+]N;xe %V4ڬysW([jjukׄpPPPq[`v|D^QVVVF\ǀb284{q"\r'fQ6^o> 3;;3|,JhBK.TT'R&>>~ꕂ/hcFODDFX1\l ti`f@kJwHH~Z ""2K0S (cNAzs˄&f籱tBjd'H~}͹!5 c^{ϗMm nݚٽ :=2; NA#S1%KLH>t4Kۦ4ՆQT*ƒ`pܕ*ժ]KMM˹hbۗ(Zş>PKPxL GVM  9ݻȈ۷ ҥQiii --cbcPBCBJs e,3jyVgOod=t bӦa @},_ŋ3gͲeVvvv3gL1}A/ %]vIkk׈a)IKMl=cǍkW_d`db cѢG]v)h@.]qӖO *+dĈQ &L09.xwl-\ 9:I.()*A>s|@p{eDAA0S]v}ڵkcT>mJHH;?^V|>2tC:{Lcދ\AaAÆ[#}0v`t[6ѭGu梅qH-']vmغuժ)!JAIIiᮧf;8 M\B X.]=>D?f?-[qXϝk{'Mp܆&݊a>}{Ktiw@@@FF%B&Mfv077moɑ ZZe-Z*FFFF͊6&M[آf)))[*Lzq:klhרȹ1q^!v[.\Lquʔ| IKPqM\^-CRXX(JssrsD˻6Ap* rssl09G8򡣣1{Z]MǨkGt ͗DL[p^RP^ glſ`s?{j[nyK y8:~[nWVV5h_$2իcmfL9id +S#[۶[ׯ]KK'3T|煋hn=*>'vPGB ':īWܾuu^ںI66..D F s˅.&S 4ػgA_~/t0bs45vڙ_Pv >ڷC2sӧOڷP]MMCǎK(˗/$'n%'غf-弤@CSjU[nڵDu޿߉jW.s'2(r {\CNL=BY3ŅYq$ Zff&ğ^`r^CF,4!i6@{Nߛ7EP7PaK~& 7m (vMеJi/oذn?p^COOf=Ayb&{7lyan*bE@KK;"<-~)Qnؑ[ŷm+<{Q2߹sZ.3p޹s;8(PtH޼BnݻވE} dd CR`| >O3֯_ .|x  X[$ė"%ƞ~}{ рBB ?zԈ7`ƆpUF֣{W˫E#V?` vN;zaC?|iƥ+甩702yð͝[Asrr<B+dt./U 㐞޽zac VګgwlEoLϝ^@.'ЩÇ\je݊xEi} W._ǎsa@e{_8icI 0?a\\܀}q?k׳g/wA7m~eQݺujEĦMA57f1fNǎAǎXl`{QS pi=Hy:_]kkkA#&3K\3|TxΫ4Х 2 jLp}\K<Ao߶ׯ9j @mM6uYڵŴm ;Jo]as_ٍvԊzLWuf,O/1cIJb *ǜUxǠ+'t%ؐ]2 J9 q=3+J%8BWعމ6?iJR6fefBɕ,''GUrR]E ETQQ)QxHAKKKUYs`5YRѪ$ 2 :O"]vAĈloLHO۔HhY}E|Ҭ$d9`?f w["IHZA&0`@K)#= +E6r^ ;tyIATP` kJJ޽)ҀO~~SE-d,=bG7+9*hS$T@ s7QQ_Ȏh<=-v(MICBp2rʣ!+嬬dXMT%uFl$%e?T22ҟ={ָ[:ȝBj;O۔HhYB,kǏZ `DYbGRKV ^+(,0)sRaו +mq헧mLQdQ D˃ 9!ʗ4ikkSTR.J||]%77ƶmY{LŮ{#*UU5譽{vݿ/::: ׯ}ARQPPq&@p8>}a#F !e||w_;޽;` y͙Dohhij``ӧqj&&͛|?ECC3~+cb"{W_=V(Ϲw```rRRZYXXZZ'IL  BЃr  Bΐ$  IL  Bΐ$  IL  Bΐ$  IL  BT<}6!?[j߿qc+޾}yɉ'Olݲ95%Wηn>a7m|+T1$rBRRҺukϟ?s^:_ϙ3mlzZ@ i8(,,LII.5ׯ^=~(99k׮FFFrn|}_xyɕqoϟ~!3U/IYu?[OQ9k;vvvbcb ߗ9M)@ rEɓ?ٸ%%PW._NNN".Y쯝!8/o4/O&Mk~]nxxdfdpn98Qnffa*#M KR3'''5rn! Ay}JVVB &?Pjjj Y4U K}XYZիs! dFΊ';yyyOxܹs^`ňMlʑSSEs[s%~-..sf8 b%eJU*RUX)^ 4VW1cuKm|f> TotrVfs/Κ:uz]VP/\pw?[4͞=eVE߾ǏAR|SNӫAk۶-:Oar.]xE-_EWZmdl̜ݴQ<t~~5/^Xp>;R e˖ dMÆ~_RCC9ټ'aC | ndd :;_~M__?))zZZZt!?!ʍ*>P&&&&O53+B޵D99󬬬PM Ο;cfNN6O(44 C*(dg3g666)NQQ~ 554oߚ0lpdW"qrrSN^9slKK+sQț6o)&zSH ߾mnݻO6C,+ݽ{W޽ϺMINnԨl۶khhj׮j c/j׮mgn)E p[6A):x'Qm7mڈz.]M2U0ݾ}bzzFll \a5ARBQP+kժ5z󑨨QF3 ¸***8RǍ߻O_ኁjx@0rss1spu=YVmg]] &y<# d"b޾}KWO']+00WWQq:xG]ƌ{8|2dG߻i7w;;l*@/,X}  Z[nx 60aRSڼaó?]u?ꚞ^3ڻw?."9tsyő}Bknڼ']OCzAr]1C-Ϟ5kڬFAu뒓PV8?:l9ؾjO -Pޥ**(0S3fڶmGPPP\lQ9޺u)9r("oG\=_~t˿KAA\ Zs…J=WUT; @̳}k .F~O<: sB""3g1Iz*ѣ ni3n­ٳV2. o{vDG(?|AT֭{VZre++ƖXA߾TUUвUkBܾ?ٵBc`\9{RXʔS .hmll1%' XZZ֬YSMUԴ?TRRbs@@@@ppC۹sȈ8v#we>|޽K8ad7o#.˻yֶYDPV[lyM;ykhѢ~Boxxӷ^=S8;zy>,>;mSpƍ-40 +&K??$[t]4JZ۠A hik7MT`&= |}_def? 5.:[YD w (M5 Bf}U'us˖.t/c3sss6%/?4oC+#J!%TDDG_z-kXX²U!)E̬АP ; """ssa0 L0LM`ZSzuuqr[S۶mQƜJ=/ hij0iވGٽkN<]Aj~~޻OA>44jOI/iL/JRL %]dd[md_ K_cn# dk jL<]`>-*z4f:SitBppM[>y8߯#F`0qܚB ݻgee-Xi'3N()*A>sn}(C*n ۷qҵۮ{;0|.sbllbkv/^8z…z)EL_AGWWwa62v_~]ba,?9 EbAݹsGwչ#>dblԯI&f;?^ ZZe-Z*FFFF-lnD$HZhfn¹PRϴޫWűXG__IIȆv::G1q^!T*r7+C?2Q.EBCCcuE3P޼6}ԔJKK3gCCT͛ZX4xo_r1VyI@GGZ5en#؄QHj /_]x!9)YXBg=Ov>^z[,ԭ[ Mڴqq9&hX6Wo`K԰bjZA{6b>ׯ>㥟ȲE4MM]v&-TŽB:|Bwn~IdP¨u_z 5|RJrrV~+m]"QK 445VUu]zV_9;q)5ׯAu!·2} 醪 Qbcyu\EÂ/Nf2֤iSkkkU%^`fS4C޽{0* T[`"\P& vvvݷO/ ?_PNC 0($$xGظqm666,VZ4bİݻG^^,ChbtmPZaC?|iƥP@SNl#G 6w۷o z, 5uEת]{„qHO^=ݬٓG+++^=c;uuy:|իVѭ|ޏxm_B߼ 0 R?`˗pE=3#c\q(/5fu5uu3ys:uܯ_Qg͞}!C@f{?j葡!L>?zl#9"8J67l`f>nD6+T;r~c}!h֭k?ť؏bѐڱc+:~qc>#Im\~A2SaU" i(zXY{]^99ժ)ctἾ\*UR;efeVRDCCC/X(X\EE%ŚҔl@gefBɕi(rrr411ҥ+UXNU nFQ~33UTTY'_70@~D+dIE**O+L숶q#4$d1ư[6y;TXŶ/LǍ yy%Vj)MsGA@yG>]M]]t$ȧ%Ҧ.G} 0";YXعbHiJR6}\Q9!!>$$ں .#,}˗n9x 44?fdGsSׯ]mlٸZ5eW~ .ǒ֫yŤm޲YWWWG?MbF111M#<<Ɩr.Wx{{[X45#)[gd onҤi^OJJڶmkrrr $h۷owԴ>rZZ͛_vv@Y{Ζ-._{Yȷ b' ,M Raw1RSq~JJr1~GIt]v522stK4lԈs(|_ǹ9++sKs 4448<񶶶.lk b4h"Oyq"11˜#G:t5)+(ަMnݺ۪3L?VZxMO ѣGcGccc$@m߸0U 7o˗B/%G6a AS\, t^W._NNN"`d鲿vy--mߨ#64/O&Mg߼ 424W aW\6mz58/!98U<sQW9rnxxtuAV' MM}f kߡ#2Yƕd>wOIj\eoD={zP\0AGK̜t@˹BKY`VPL1缤㍚,H@jժW9@.b#9+ [?VĦ9a+q (<%] .G/]z c(@ YR(KI&1#XhJ0=K+;R e˖"U̿aF\9ټ'%~s&P5G_I3J.l ٷo QnW.../HMM5114y`\Hݻv!#ÜyVVV(Fdv󦍆FFϝSVV2uڕ˗ΝL13'''B {ǡNUrqљfs뜐_CCM ۋ)(( 6^I7ErZjsrx@bbF\\܈#Č;r(?__Q\3n'q9l܂RcޣG.]YbcQj׮}Zjm|$**jԨ̂7=@$NNuի7A֭]۳WȎy?[ZZXzJ5Pݺ@P,T@bbƍ&OR)aAddm[awKu4ե)w]z>~&&66%9QFJcK'::\U a͜ClϟA'dnݺ|rΜY2fأGFq˗-(xvs A_.^m0aRSڼaó?]u?^gݻCe:|u}itqdǚ6oz岾I_z tr]1>Yf֯[ aG(0!wP((D>~DhhX]O:5cmvAdzӧ\WȑOvHRqB^~t˿KAA\X1Њ r^Rp]d:I𑣈2οk>T$2b~]8:ԩ{PRcf0} aTC_ EڠA{01 yŮ{*Q7ЩngB]zѣGE%Y~`xXXsb Nrmo>]..@ddds9 Zz zH;w`b&9s6#&o߾~…Q'NBRoi!tˎsڷ|?[~YV1 {֭ƹJ^nwQ9߽{En9_!D`Ç6icy}B%%ELQ=bJ…QTCUVŴʺec///.o~EJ*-[aqq,[8*;J> Yw/OOK˘$1el"Z[@---k֬fjZZ*)) n <ຝ;wc00z{{Xݻ&Hv-q D͛6͚}'Zjݲe[7op[|]F->>` vãwNܾ}]=]/χѱcQq .ݸefaaa\X1H'+~@S,K`w4T t,I{uG ‹R%M蠓cjZ3\anF( D̐3(]VffۨJ^K[¢1 PSSֶ- K??$[tI }pbQGGez&AE`AR*ܿrWi Zy`8ѱ i@Y7`6 -W *]'%ױQ{JZ<곜@G>xp_]C]p>nÆ~|@۷wAxܹ =yz҅ɓ&< P}Ѝ nK;ܲKG-]Khh(nj=9ӗb?y8& b|C0%tuBK.T'eGW^9glcUbbbCQDdJkfq912~; """ؓ;a0b@"hKHgdjjj&& 9 ӕ* L 6I${}VA!/]kPP`aa=?ibػwυ 4nܘ)A֦Hd*sbꭦ&BI!ӣC ضm/54544coB֮]GW(U JPth&KO ?(Fq%]>45 ?jQ&oQD $ E%%h|V˳g>=bH:zm۟~[LT5bwqK&իWv̘ .:mzѳO'@ɀjD> EEO֬]gjZ# .^޸i'OɈƎǶ '98ߔw~VzÞ|QRT|_C2_~]b5TWVcͺ/V~`>}b:/^cv׮-ԲZ?&&b;I n%Oy{?604+iYM)w=DNNvbBSf-޿ؿolj66:;C^Q#ۢoݼٿ_722nؒ I݌̌d 5FFܹݣ{={>EEmblI&ёtr ZZe-Z*FFF7k`nD)صk<0 @GT3)`NP``}}%%%#5j~\`yFv(g:EWܡCG1czgn,bE^Ì~_tY~,oKNWh9Ti|ǥ#1Y,s[[2i8uEWr ZtR$rn)VW@pnɠ(QJwAaO>܋+++@ k> .f Oٶ~vۻ=^Am_ٹyy+UVVzN\H]O^^ޅ%}<11ѣAdϑÇ$]0fobnz봶nҦ˱2=zޏA ʌi ݳ+&1+x秫W555vڙ_P7(x=Oχ(dpOoߡLc(jjj:v<W^b@Q\|)%9u2|۷oݹ/[\vq7ouvmllYt$(:,ib+WlgNOWgGLg>BՒ7jzwE+9/!^Nll̡N8mee} &q.͛UMhNNH¢1z? {p5.{vڽk 8x%Fpp66Pr1򞑑m͹@UMeVN {'dJy ZZZ%% dڵ\Nl::ʲb2}BB|HHu-fI1&/Zm^zYfڟ~\z/ _IJClGTNB_ؿ ʑ\WC_>yqڰ#j>%K+a>ZA @",m T$AoƶUQXX4qq9okXUWdddvQtIq9(,(ܥ WQ6m6a$Ǐ˿dffx|9W100PUS;W̅ľpV&>rrr_ƒsA|*A9AIM}ѣ𰰴Zz6kfffe~UCZhhHz  $1  9C  9C  3$1  9C  3$1  9C  3$1  9C  3UWX Ɔ[:558Gi>}˗n9x 44?fdGs򠠠)׮6l\2+?qcIIu㼾qhliIqm޼QIIؘ;W\nҤp$))i۶ 4"g a>_!vDCC*Fm޲YWWWG޿ukvlvⅼFU-{lٲKé)'oT%]*lIKM9GaaaJJr1~GIt]v522stK4lԈs<ĸ "">}̹ős蜛!!!*%i|+$$XQIVZ}alLLn~ߊ۷2IםQsݲرY[Yn$66Q?cGI(LxJ 7111Ź:a~~eDQg).O6*+/'''snTUU,]ݐM\AG٤iʕ+s^hGhQ 333GCsF\B}ACܻ{f߉Z $4oѼuZZZ&\@1bZj7k6#I>wY`¨ѣmllr:Ȁtu=)<<]=]]}pakQFdU1/1srrRS/&yYYYMOOK=*Y4U *!jժU^s\ ;+FΊ';膞BTPRvfgb9wY(5r LD-@p^B𘞕Cj8Q0,Ub-bf֗yr$,=ʳC(OlӾXRM[zy TNJ%6U<&(?<.Irb[e={ 6]s}}}cbP}Wq^!C?i\]QReGBWILj!f-kz!HeFmk-q5f͚_5u.]2It1ܢ9{Z .}?~Iv8eT=Z۶m7~C+t~7_GW|?7KVLai㟣Fyp|L,\ƌ/^,Z8)@CDxeKaF\4s"yOг`P:yuv~~RR30g  )zD2!ʍ*>P&&&&O533C!squ3 ňn޴sSNrعs3fd󄢳F޸IvUl\tق;ȯE0_6l82^B/"'':u՛*>)WQҜt9c";-mm*?CbR"h$}Vݺw>mϐ# Jcڵ={b՝;gKK+vT0r WW\UVm{Amс3f\>}2ټuÇ}+)UC};n|Yi'?cGk׮]PPӥk#F-*z->D)Xx6Smg|Rpbǎ9؏,]5(NgňjԨbֽ***oˆ/}]z>~&&66%9QFrM/GGG?A LwpDFD2Z˸k7n|>}٥yLPJ-\L\]Oɠ;aňۗ.[ ZJ۶mƍO6\WKg_nYlTaH-Y" _vݻ89޽';<~}vwV}񂞊)2ҁ_IIIl1Av ?ʃ>ou(iT,UJ2 snټ UC 2_lt# ۷]8R ЎkAiiUTVUU޽~ѣG֫FtTWyqqJc:S/,X>\lB!PپԴ5 JhQa.]f-:իVBat=iӟ^Ą,>?Q43duv> ]bzYX^ޭ_NM] eqeEN :cٳ7o,/Vޥ֫掖)dlݺ%.6VSK'Hp?ԫg_| 8w>}::B7nۺߣ?b Saa`ec-빘'rʨ3Eٳp Nu02Oi@O9g\a..az\|ܡCk?j-[6[X4^˯WTT|ŎvʢEq sX >((skmhР.IWqr*b``ct1Oa?Po@IBZ{{?DF LZEbٱcۏ?i'v"?mA`_紛?bm'#1(FV1 ZO.JDxk.\9jff殝Mϝ_\^<ޯBurr1sV?‰{=$ gθZYXV_j˕B ڷߑuSݳp"t  qr<)P5?(.= tPj*yyA @-.\K3U*WROLLHL(txHL4ϟܹ 5Năy) +FV7hJʏFپc'NUщ%%%s7DdX9sf1==ر>}¹_EK|Tnݺ{9= 6lł0۷(죗l٪5gwnÆ`d׮dYw/OO)Sb@E-F$9,^DTSU35-1qDł0,1\s.qqqԤwe>|޽K8adCMb1n|Njպe˖nDӛ^AQQF-Z?UT{J E˿ѻO_Rp"w|X|^;v5ҍ +[&K??$[uooބ:t, zzmР% &L[*QoÊ1bc!C j;HRVffϞ'jQEуÉIjK`_v4G舱”2дYSDinZӧO>mZmࢪ*zzmۢ,1p Vv(]m_@K[kܸlTF]uG@Ae=AEE~vKZiAR!#T66оPO ##ѣG Ti& t EVRv2MܧOsHJJ:zrɢoUZjz҆ֆY¬==$*P̈ 6*3Zq֖1؋OE!gˡe]Pk1ժU)>.E&ͲKG-]Khh(L 9ĒÇ7lY?ol(AI.PS*jHիWΙ3mkXQӵ2**%-BmuY!; """01h<}MP O+0516]B]8]zɡ~ZeyD* ]]=-\_-Z~\ ah ֯97r}̋Wzob"2ˡ֭[3~=wAGf'R)(ydJ!d ؇@Җf۔0[%X2TbԪ]KMM˹hbۗ(Zş>PKP"r˃p* dCR!{}VA=_נ8 :#---!>eLl ^hHYC)|eZܹ6oX:Wlڴ;LtݻӁ˗xbYVP{lni3OU Kn³>T1rm111,%iPsy9|٢k LL`~`|/Zȑk֮35 Hå7np0111b '98| .ݻgee-[ C\.Q 9Gi`ȉfwSZ S2sczs.;reڵ1*L6%$$Z,- ׯ_{Ǿ{;u̅}Tmʄ'Kx(SRƄIYa%LR܋>_A*gCsH_~]>y[ϴ>潘4l(?/o߾52gn@@?3|_u=޵>n\p9p]vmغuժ)!JAIIiᮧf;8 M\B X.]=>D?f?-yq[Xϝk{'Mp܆&݊a>}{Ktiw@@@FF%B&Mfv077moɑ 54v[T222AmM4 lE9RRR8*U{T;Au0ЮQCGG700s?C cJC7~Г&$&dy(>]W& ~')֨Q ֲnJ>>Fc266-ۚ7oqM.PHHHPZuN֮[υ6Rm)J|'1YҘ!}*0.ҫe(SrPaTd$Pw=l.UJB`r^(utt WccЮ_u u??_Hs MD?~&-8/)(a/Xس`_=i~-[i ʲCǏ5z>>>\@Y㋤_zu͌3'Mra`j`kvo+~kii6Hz1 /$'% wѬ&&&;zTx}Ov>NtW^} ucu6ml\\>իx?> 5\L4hw.h#,~3^T֮];G1|Bwn~I9u_zQEq֭[컢E@>YEt͍ sN8^V\r%ؘC}ԃi22azA]!hKD~>}EׯW׎ur|4XsiM|D F9044\n q P-cǎF`23¸ng׮g^,a;p6o _~YTn]Z8ifPǍ7v̬ӱc``бc'-Oh9iG!C!;l4quiZ,g+FmG⢬(H9i_@]]aLy\N:ן#"tEc$fKN?V__߶m  ըQSWWOToPu|_V v̐tO 1xPT+:~l\1Vi=Hy:_]kkkA#qs癥vGIDATUY~~>*a%qЈ ͷo׍5}T1c// GӧM=kVv9wD1mdhd!m@.3ۯqcpEl}z\Wv^=ӕ@0 bsqҬf!*컘 1g!3#c)zRA] 99ժ);༾\9032TZ"ˡ!N,yuh$emVf&\rx "/!OlX~uD`UWS761i޼cu)>R{_9ٻ␐}} BddFѝ~M'OjժҪ} yyy-5߸o߾DAAɓ'nٜ+Q[7oro͛6{W~[{[wށatu n׿sf2|q>SD_$fZj*6!? SRK ?W?JNN⼤#?/ڵ[l?_/s^r%%%%00aFQO> ʂatuPֺ\\Obb"!!!-rv:cǎή]lLL2Զ)%k"}P8y'7伤 ɜ[UU%Ks7d%W_utt87NzZgM+Wy}ua#Gq/III7<<2328fffN(**r^:4555[fW)mA_999s QPP | OB.TSSU6 h ZjիWB Na1Ovؔ#;,y%2$("2eUn=-D*U`Ur *D(); &&[\\,pb@1J**UWPKQSpKsA25f3ꍿbaՕS&x[j;4Aeر^l[sn629J㠓#5k;w~~Avvԩӻt$F.QQQ-fϞӲU+"ؿoǏ ))S m]q'0 9.]"ߖ/"V626fNncܿv:??_[[{E͚}/^,Z8)@CDxeKaF\ɜl^~oÆc>y򄁁~722G_dee=m ---vА}Οyr?(TI q!Iwd"ĜÜyVVV(Fdv󦍆FFϝSVV2uڕ˗ΝL13'''{ǡNUrqљf(۷oMNNVPP6l82^B/"'':u՛*>)WQҜt9 ;-mm*?CbR\$}Vݺw>m$YVo捨$p,f޽WgƦ$'7jh\CCCvpddm[544PhkPWg5M~~k׎k7yu-3ٯdNLغbnݻ7jd1o1$$XgĢ4ml򔩬4P!8PJUv3i)dH^) O*(( ԴmƂ|icjV|sGAOjtutRSߝ?n%.%44cl`nn?=$߾1*UK1hDʻw۷oK ,,,}) Eb201et_䚵LMc 7my |bLLLF>ڂmIsk +((wu/JJϜ40Da"Pʥj~mgܺtk޽ 1 ڶ]~ŋup={JJL_AGWWwa62v_~]ba,?9 EbAݹsGwչ#>dblԯI&f;?^ ZZe-Z*FFFF-lnD$]RRR8*U{8v+))1ЮQCGG700f>Ϋ4DוABbBZi-1|B***&uFGG.fy3m SSSI)JR3gCCT͛ZX4xo_r1VyI@GGZ5en#؄QHj /_]x!9)YXBg=Ov>^z[,ԭ[ Mڴqq9&hX6Wo`K԰bjZA{6b>ׯ>㥟ȲE4MM]v&-TŽB:|Bwn~IdP¨u_z 5|RJrrV~+m]"QK }tutOvuQDgܸ 1ǎ:߿ĉқFCSjU[ntp[>ׯAu·2} 醪 Qbcyu\EÂ/Nf2֤iSkkkU%^`fS4C޽{0u[`"\P& vvvݷO/ ?_PNC 0($$xGظqm666,VZ4bİݻG^^,ChbtmPZaC?|iƥP@SNl#G 6w۷o z, 5uEת]{„qHO^=ݬٓG+++^=c;uuy:|իVѭ|ޏxm_B߼ 0 RAfNg >HEex\SSW._B^yfFq㹰b!fuuԩs~٥Qg͞}!C@f{?j葡!L>?zl#9"8J67l`f>nD6+T;r~c}!h֭k?ť؏bѐڱc+:~qc>#Im\~A2SaU" i(zXY{]^99ժ)ctἾ\*UR;efeVRDCCC/X;WQQIt4%)YPreZ!MLL?x)=73SEEE~°2TRBCԲf JJ`U4faXabG-!!#GF؏1oݲi--1’-}fN;nI^-RKmo> B;jꢣ|&xF> //6uu9KͲx ;@JS#沎 !!M8wA.W7Y@_`'f"&CKa#&M!.N eV(O$z8@lZJp$sY2g XkcVСRB^@h}{nÆ_~Y'8AAPN_9! v*U֩SUVz;Q {&&KA!IAAzPNAAAA!IAAAA!IAAAA!IAAA_5߿/,,yPPPA|NX=ZXDT]cC-њZ4N>ƍ-9st<32x #G9_y)׮6l\2+?qcIIu㼈b6o٬#d1Θp^ܦx@cKKee9իW-, kԫ/ܾ}I%~x=))i۶ 4waRlތ׮]*#wٲe˗ݿwQ# XkkXjUUU0AbbsRc.Ǐ8/˻vjdd$!;;ŋii霗\III hب&>P06&&77ssVV6ϟhhhpn9K=ymmm]Bu+b4h"Oǩ1rԨC_s^e۴Iӭ[c[}}}=U/[Eїr^"|@ҹhz7o[rN毦x/,=z, !_'OrdYorrrr2%Ks7NK@hiiFQ~>eyyz6iT?MhfF̭ zʴikԨy afftadcGbyWvãs Xr<`! XsqիV޾}}={zл\0AGK̜w˹Byts}Kj0ިIX̢*|_Tq TVzC),;M؊(ܹs^`ňMlʑSSEr~ҥϞ`;Vb @%Ԙdzl3y}|ӳ$B_czFDdʪnݒ=KY%[,8EjJj;]lir쌌9OAFds !bۤIS]={rrn?fK,fiUV}ҏ iGpnx'\kkk/Zk66f̘U&Mg^l[snȸ.(Nk֬yYSNҵ+[l gTTTssƹٳlJ06߷Gzzz111ÔSjcݶmp:K.޿yutuo˗ UVs7B7ms1ss;kE͚}/^,Z8)@CDxeK*߰aWdNd6/?7zɡÆ=y򄁁~72*ct;|5}}}LʭMUrakhhȾ}{_r㼊uqqxEjjɤSB޵ ̳B1"7m442:Ӯ\OnO/*Y{sY$acbyb]'f[^o`p4FW^"kr gâz0oִ#%''p:~u( oݱ};* 0Z0־{>`ӧN͘9k۶AAA޺u)9r("oG\=_~t˿KAA\X1Њ r^Rp]d:I𑣈2οk>T$~]8:ԩ{PRcf0} aTGFF_ EڠA{01 yŮ{*QїXngB]zѣGE%Y~`xXXsbNrmo>]..@ddds9 Zz zTcgvP\pi}k .Fi8y K I,;gϵkߞϟܹ A]+zWnz-n[Ky'SDjƆϹ*))b3%T. "%fn;tXjUL뭬A2Z$>TҲUkBܾ0{b4Ga@R= })v$LmpQDkcc,^Ҳf͚jjL@@@ppC۹sȈ8vcwk>|޽K8ad7oj@ܼack۬wp"U-[u& ޼ 5jhXYYYQP$) }; Hptt<WDǎF)tƖ qa W]Mທ/]1ҰbPБ·$mT!((Dr /J41hNiig̔ޏruY(!jhHhfV&2CCPvYQo+gz-m- (@MMM[۶h>j,H./l%&LE1k͛P_ buI^|݃P8-6! e?/ոq iCAr1A+¬Sn340jp -?0yྺ4|n߾bU],bnnnZs6lXK&O &BZ^A7'-s˖.t/3Z:L_%/?7lah I.P ƛO{W3g6*11ӡ("2BEUڵ3ss#"D _N3N m OLM`vs\+WU#v$u6lȓTIKy.22bB^נ¢{~Ĝw iܸ1+HB`NLPԤ_U?"bzq۶mflUڵ Pv- LVy9|ד%W*Z( x෎XZlegg7sƴӧ:+VQI Ϲs1RPs]pEJ=իWv̘ .:mzѳO'@ɀjDLC,E@YԴ>;F.]qӖO|3Z#F;m&Nrp .){=++k>ZÞ|QRT|_C2_~]b,&g#ՕXËoXO!/ؾ]kK2|%֏.N[I;|ޏ ɀb^ϝk{'MD}Bc(CS+Kle5d()) 6lM\*#99ى EO9<@5AX YzǏ {tmn߯[)@|7nlPפnFfFy AȋE``hd7?/Ν=ٳST&&|A'Gy UۢbddidlܬwͺI᧴b׮3sQRϴ! ;AuK,/+5j~\`yFv(ga)z਌…&:ڏkeeջO={ve,f?/e~]R,wE-J?.dϟږI1 PP7lm.m\v--uQ+Ο?')PEE$@OO/...77s 69jە}||*y@)ݽs-^\-N…Q߰j9oĚsQWfvn^^J!*Rbzzzb|-,,˻pBrRf=Ov09|= A[X([:icrLx^G,mPÂ2cjZA{슉a>ׯ>㥟^p`ڵkgB|BU c^^^gܰPN{<=۷>}Ҿ}`a:tx߯^ĸ|RJrrVeoߺs_W$vq7ouvmllYt$(:,ib+WlgNOWgGLg>BՒ7jzwE+9/!^Nll̡N8mee} &q.͛*oބdK$0tr} K L $*1O*.l ׮]=rtul-}|Ovv*dff 4`ғ "%ta "&(y@l1Fp1BQmڼy!=QJJQ9MnQG7yW)sњJ~U/MQ&1J]̲R~V3 GEdSSWLjye&_X_.mU!5,c~oeeS\4%)>babD%@FʟhY|uttP06]mmjeY1 >!!>$$ں KY6^YfO?Q^& $]UQQT4vD4.ʺ\PKd'O=_6|DY' 77WG<|0=RȒ-d >7/1 ~Л봱m+w@T8}||\\~*66?V'$$t֝;EFFfE(‚]if&N~˾Affg͞yU5s\xA ge/''k,H9@w=  KKOWifff%< 744*l+ȵАzL?@A0HbAArAAr$&AA!gHbAAr$&AA!gHbAAr$&AA!gHbAAr$&AA!gXaQu 9tDGGkjjq8}˗/7r"//@hh(͛7137WWW|AAASׯ]mlٸZ5eW~ .ǒ֫y}ㄇ98RY  ۼy1Em۶&''7h@ϲ|CkU H.^_n͎.^ۨQ*~%Kl|-[6]| [xxx&M%G>ߨJ|TXsȏ”Rc.Ǐ8/˻vjdd$!;;ŋii霗\III hبyAqDFE>}488s2w?EH󵯐`E%Zjq䂿۷2IםQsݲرY[YcszգG$|TT&<%}C ??s2"JNL<ɲFEWud-꒥ڹ+(utt87NzZgM+Wy}ua#GqnB&98Qq I| q}'j2\xӼE֭Zkhhjiipňkժݬw،sw,X0_AAa669ؿOTefdd@Dn||.]򋞮.>|0y5(#2Ŀ999s QPP | ,u~D ,H@jժW9@.̕vD#gEtCOxs>ؔ#;,y%2$("2eUUTD]!*D-fgb9wY`K*+X3 R%jV@p^B𘞕Cj8Q0,Ub-bf֗yr$,=ʳC(OlӾXRM[zy TqaJlxLP~x ]733S]M]R=Ra(&&{:l8mDGsŠ8₽?Cۯ)N<#ˎ. ?ۓ eU [YC(Ἀob zW;֜[:0229J㠓#jL͚5ϝ;? ;;k]ve hc**E5r9-[]=~HOOpԩzzP}mۢ;nVt;R e˖ dMÆ~_iDfx{gt ̧@ :|5}}$++ig`.NRdBU| (RSSMLL&Mjff C];YD0gݼis甕Lv%s.g Egq 9ٸYw_CCM ۋa>lpd^D$NNuի7U|SR9suҕsT}ۖM-8MM '6oBP1oq[ӧrAi[g^7<<Qslii}&\\*0ʕjժP}=:::cƌݧ@ n<|o%jcǍС#+ >vhڵ ccct6buEW^ٺe3va-ШH[۶6mDΝ2e*Qm޹dz(% &x OZjNرS33~qX5Pݺ=^EEAZ޽WgƦ$'7jh\CCn:'Z˸k7n|>}٥yLPJ-\L\]Oɠ;aňۗ.[ ZJ۶mƍO6\WKg_nYlR=Ur9 @?%;h׮]wl9NNw*9<~}vwV}񂞊)2AJJA믤$;k ϟ?GAַf:is4*χC gws9|lބˡ֯?~D6 [.GURJhL5jd**NB^v#ue#:+~;P)~vvvqAg0˖X(>ՓL9y~ afF~g<kТ>a]x cZtvW8zzӦ?_z Y|`~(9.hf|XٳfM00[n " }%T&(YgC!ˋw)>u 99[n Ef1>5"/_B20N@/ݳONM۶niX(TxXhXz.̙]2RbҌ. ;k <=D9sML ,^ڇuqqr$ulFOef ƋB_рQ׮^Yh1A^xt~?mР.IWqr*b``ct1Oa?Po ijA-0iY eǎm?ئm۟؉MjQ|Ŋ"n{ȋ5PVh\Na={v9: v]/׮-\s];rwwc1;jx_0 oR9sƭD‚R[2hPPоBo KWPPj'&& khb Q]:5 tPj̃ݲ̠C U"לoJ}v%^R zbb@bFGœFbxl]p"L]X^X1(AST~ 0;aDtgTEzaaoоtc׻w\ ~ -MPʻu`ز |o~^e8,..޹}  >F]]Tdzgݽ<=RJLE66ExRMUԴ? °|pΝDFDűP%-}1JoAWOayEtih{K7nl%,n0/첮{Fwp PO]O 4Xa„IuYE9j>:mX>FLt :d0AC"+3g^5sĤe5iTx$eKczD;.L),S(M5iՐ2W3g6 Tke""#TTUJ,[,03722CCB-,,vEDD`c>y⅟V`>jbl uqrC )ʘ* aÆKy7tȠK~;2ylv?ى0i8sCj w Ǽ}&&/Zmݺ5Ǎݳ{tzdv"- GbJ}4=--miMi U⩍%C1{फǹ+UU:sKѮŶ/Q45 ?[}~3P?!I åFk!S޽ؾ}/]kPP`a{ 2&6u/4$ܬ ]>Rv2v?lytmOC+6mڌ& :^x1s,+kk=^Zlegg7sƴӧ*{ Rҥk7YHHvTFc(9vǏFzJl HLF& 0?[`>-Z|5kיr 7my|_L1}Aۄb>ݳ !GT'%E%gQrbbӫt#EOO }f͜y!D@p{eDAA0S]v}ڵkcT>mJHH;?))癏}xq]X> :LP!~u m=b:WPXаa|}ȸ}Kr9600?~"f,]~m={tѽk~}nݼhsRS3v֭۰uѣUSC$񕂒a]Ovp@<6d/y.]=>D?f?-yq[Xϝk{'Mp WV'=-- 0u;wnޣg^S($L2228/4i2kh{NUۢbddidlܬ ln҄=ae-Z|Ϲ?`fn¹PRϴޫW Ȇv::JXx3Ub׽I򚚚-[3wxyyxؘ̬LiǐԾDF`\~30׭W˰Oq16Ap* rssl09/{:::C+\ϱGLPHyq/|ʺFH6Q? 4-a/Xس`_=i~-[i <?r-[+++pf/~{ձSg63Ϝ4i2Rȅjm忭y׮g"%˗~>/\,GGGvxQ?ٵ8rzO8!^z[,ԭ[Mڴqq9&hX6Wo`X.p0oР=+x秫d k΄ }yz>D!;o?}}:tx߯^ĄEq֭[컢EXK'&%**)=Ebua'N/i^K`'66A'$OePtKER 3tzӫڵg>>H6ҌYq$ Zff&ğ^`r^CF,4!i6@{N|D9@8l5kpu= 'F peqN: )2y"TUP;v9KpNNCHCLAhb ơ§ifM` 궭WZ 'F9swTINRѲ1+ ӆ͛ٽ8==̘9-?g 㲽Q ׯ[ս{QGK ۻg@pًg8pRiFqFgMߴi9?~U55Utݺuر'n Ĺ3 >] 4H#qQXO#F/9̞[OŪ4oBEh WT=]qHH~~[!22RC63&Nvv'ށIIjjdaaiiUo_$1  9C  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9Sܮwxm͹ۻE  "giff9  *  3_]Ln  AA!IAAș/*1ϝ;AA^$}IA ILҗAA$}IA^!  ̗}  _.&L  _A9L  _t-&L  AA!g)1=  ?LsByζ5疎MMMAAAW]LCCCn  #ϻEFF&''q^AA 9KL  7  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9C  3$1  9Sܮwx5nhƹ  Bj$f;֜   AAr$&AA!gHbAAr$&AA!gHbAAr$&AA!gHbAAr$&AA!gHbAAr$&AA!g*$x=y: 8;K|T\YYVe=F*׷jѬR/R > 1=|tk7q^ ع}>;ٶ7~oG6ط3 Cjܜ99B!' 3J̝ | -p ?~l' (z',.sA||7g/ZA'"fz1o }Z+M[9>ٳgC%%jժ  y9g咾^A޵ ʈҔ_D~sg̘9 >PN0<-"n ʎ퉠_zu섇yyocɣ'329/⋐}>~\R;ۧU*bryſp^,r.潇99T)6.@we7Ae}rbӆU>AWr{q`ii֢E mmm.R_}3lUSR^rAx#58IZZ3ߗ 5kjxۗ95kh1e?< iy*m"+RX}_uk¾qrsqڴcfv]V \8DMxᩦco9ŔZBf? .KU[XGƯrGPbo`o`4ޣQ'X"j,K56{z.qr{ٙwvw,vU`I_/ȧ.\r_%"LsN=XIڛsJMĵY ;S@j*u2cRRRhhhQXʹ[N T;rd$w#x$05; 5r‚;`Ƈ1r;2)ܳkg& $boyhei9uܰ^FU?9%j̤j5rJrgLܾw:} ϛ/ok++tSNX1%/\ snjl&Ehh܁UT@iB*Fv5~"̕W`K(J ?)70iJme Kpvv;5k֬Q3}|aϓ5"k"FbcVԸmڎ43դ4+X[O|5qWν7-"vpxz=|zKp!!#|KW())u-]0d"µ8LLL YG=qᰟG޺YAGOPl z ƛugX[>U0t[>{e4`ȉpD찓7짻O\-;ƓGuXd%q-r8А ~$&)먆v !q ICq~)wҕ"Ib9͘d 37`?. 7B]ڂ QB\ 4IS0X!. *Cݸ.Z$љ9FSZ&])נ7 ѓ<{3k7齀g/>Km`d!sZ ;8ܱ{udqP/ @7nޙp9T!]D`ޠ~] ~I0~ڸxǘ8#|@䭻}N/$M㧐H,@| 7seA ;~qSa-.tbةs p!? [`*rJgP Bhgxeo)zCc k?a<\HxFҐ@Jję0 قT9p8dD*XPCpiD P/_I[!P؁vBV7.DEe xyH-OZ@"= uc{@"1gH'mUPSԉ{nqgqC,ї̛>n蹠2{:d3N@~-o|ֵ?V!πƏ D~li㬭h8ˬ]6߷kgċ'=١rLOֹ-W%xQ'=kP(4bعq3 O= u 6,t" *x%"g.ɦ,\2x|Vqq}wŖwn;Bw> ,X6>!P6v{K,{Xj0X8Ѹmل=3VR[Jo)Ν!i-Y>cK:Ƶ ~I p+\բHX(*"9[Ҽ)[ivH"1h.̅q燝ȃИ! Eʇ1v;xE-]b޿/\Zr4{zxuxԹo\[A҉92֝aWY`&ZT6bod߶uc ;/Lf1iT+uAuv.[[j^.[vd&t z!>|m"W.`ޱ{xOQH& 0+ H!럿|o ZTN !d`,TTشvBp4y̐+W( nE=w$Vmc` [`q C,AmIGxƴa⇌,8DQ}+DqkWoD' P8D#s_zhTΠ>讑4P""<`W=;Ÿ=ڵy'H$űk~&&&K*ZpWGOS:tѿo_WtCNAf,1R8F[3'`D:VSbfC&f'i1=]f6VIIur4NͪРsB\ 28hi@ŽqHiCЏ+]e>^ =%S&F Q R;BnڻKA N)^ރGj J]vzE<跎2yOɉGwnkiDdPʼnۉi3"2텖֚ъG(O`)mI&Y 'Q4GeHG_'r>dAS!4m0X)HZu#չc|fU?}HTo/i:U?k5&7bR6'}zEu΍ Kp6y͋'0^T )1iloi=¤DŚ )"٠ycΑ߸gsN]*Ӕ%>|dР }9ox Vb[zmfA,z wryit؁`">2?^h$ہ' Ϝ8))j,`N~;vS C`Q]Ssi/_y1Fg@޼ܗ '_A|d6 {ĄH`@u/g6mmYmQZRbbΣbLRf@eR4cBB?~MTE̔QvcNShevV/w#75sARW!t|7CTNS,"ƹ ׆SP72kB}9q5GOZ@ݭ8SHLWI{.9c q5U&W~4(r.0`&fu؎(֝{QˡG@ [D0Zv.X*YPmvE}Ww> e\J"%+# 6`@oԭU!(a`毘H&<$%'I%=Md9$hե3ᚏ q䃑1RnѼ%+sENB¢=(1D14pDhKC|Pv#dLM$1)]2!hQ8:cF<L-.u4lS(in,?R,[^ɘQd`O$Ԟc%/^ }uӝ$m߮M0_ ?ug·Ēwmm#GAc >()e=HV~ٌpބ>oUf{J*aG‚Nfn}ca!̛_^  O%% b7d2aOo +j3UB>边EC䑴@^S CS(Θ:Bڴ'k?֞6e`;-O w247Q(D_blZH;[L"1x̺/u]*Z(ŠDA\aǘX5D"ι֬^E8X7S.cA<|46.νgΑ3C@L^ҙw/_c >|4 :S#=0T [wȗ Vliotw*Xƅ5BRw>*RRʺ8`'VSq9&6 +?_^qMNйx|y3<5ԨV\ơ堧A\|Biָj8APF{Ĝ7>dbbRll\Vr)=P1Ahoc1/^Qa:fCJvԢÜH@|1Dףxa?_w0 ҳWMu;zّ8pyݽ{ թM:)=Z6u=ic6-rqNDݡ{?ɷ'C@@0yvP. a~0.:P? Ek<{ /i `oGGM1|Sr 7{-[\Jz-:D&۵l^T*ɓtltۭo .X~!%;jQaNiβesqgCW,Wny:b1[ݬ#+y X\d 9]L>hY?7n3Y3de\JrN#N'O\d8u:Mb]#! iнH@v:r37Q\pBu(.!?!t(,gOh∞HS !UFosڀ؃ѯfOےk/s ̵R]ؔq_œI'/ya~'Sc]*G*\%;;;__P;}jVܲ9YUi뮗ޔr.hگ~]'cL"ȯwX{`l3mKYXLЮe3h͟Ҫt^*|z O6~07n[YY+njӷgWX!_խ3~I?|yr.?##o5%CW] D_KLj# <ڷDeJTjț 1fZcT {زcOj ltc<}AŽʸO_X^tk  oLRu5kT/~I$np}Ex_oEI-A J|z- μ:EOA_,]EtEg[0at֤뱓g#Cģ4麼rf4…3ڽIܹە*CN(^{tDEfF4n+ lMX,<GlLa*B;KDBG$uDe޼Ӕ)XUy≤Y{LGPuF_i!CAlinM-@RZKP4v ЁXĊ`Ѽz~@(&t,涟 ^Ҽyzs \Qߋs#$ w=EN5,er/~rPeyhM E%T*˅4yl ڸr!&7Ur,L*swiAc hcm 5GUWT:ǴuZa ;NI! ;R)v.%b1d1U>H԰}F`Ă!_{Ӗ"&P}|EL+xգ[H^O7uؠ ֪vhF]F%O&oC '$&9\R{BFhʍbmz4z3Rqo=2Sr&'1]L:V6?]Tرgl۵h`Lyƕvm\4w*6[ĵXW67/oE޺ !;djpܹ QYZQO??珟>\J@fTsc>嫑*j_Zu,^m䎝H,iJ>ug/A <_1FNqY!I7J||ĸ;qzFLUTI{)ͣPo}yP@&InӖ˦,]@\&4J{.)?ˆς|J_{sט ֥ 8aLOL4ܹlmmmt:^df6D0@1-l SOMrڐag&-%m 0/Έ=O`FI>d%RTZf FP1,[Zkv#'P-7rkRRi Fr+RJ4=rLBnjL)j+8rT lؚ̲{ߡ WhCԕʑHf-'TE0yK`'B7/])eRΚcQY/RI̓;g/ɳp #..G"gO1?Sӂ_䭻6;hH9 ZFbr6%ԗM k"ڥ9f{J~E%"B\^uCѼxq:*BQ?$'ߛ$h>(lפR !nCWh~$ (,d‚NJ ͻ<{Tuֶ$5axrcaKj֯.1 |4F& \c%FLr8k_M{ ^H9s.\WQ./0 ^:5,岴q:P<䣋"1)|zMPQk¯\\@HQ̜iAHiqb,ըOT_T,8DqQ]9[W*Ҽ|j[\Ueٔ =wHV}| F6%K%Rm 'Ґ(8g3L?@ٴEqzՇVkb04@8GzzSbbb_Ye0~ …#%*eo5/1Sv~;HBO3"|P.b0|.RK }v KUbN :v _JHHR~Y BJV1rJH6%X-b0_-|PNH?m`|KTXnS" 5sGo# "20]LLJ|`P{gn<0KL 707h F^PW'%  :/1A@+@[Aٗl Pgȇ +&[$fQBS~u?`0hQ0:0 WIHLСۏ{r 꿉X603쒘 /}}I H`0bQbQ~^&{u>W0"e2^d0Wb~2a|g%i}F?Vn ??nO_Os 7GGe'C4_l_83{U]ɗ"$&ѓk6neƨR\/@?o|爜H;_2"9*1)^ؽB^0B Y١[ZQW= )P=!6W0 AbRJ"ݸyϣ߾OHP `| DBk+u*Ttɪ*ԫ]]"pRz@ &)A`2B W}!q’ 9R #$&`0 [%W3 `07d0 `&1 `0fIL`0 a`0 afd0 `&1 `0f&]ikcfvf0 `dS($?d`0 0@lA/IENDB`backintime-1.5.2/common/doc-dev/2_weblate_setup_07.png000066400000000000000000001051361465446530500225600ustar00rootroot00000000000000PNG  IHDR?(sRGBgAMA a pHYsMMgIDATx^ \L?ILB\ +ljiC.Y6u]]]ڲeįU_ds&w6Ԛ%-B&TJJIJ̐fs.SSͥR}=#sΙss>s| @f!z=j@5DGP #T͛7`e/%6:gd97 h= ޱ%ǍwE~HBG\Z*F4jjcrͻ:B#DIrfK"t4XFKSuNQ.GИx*TC!z=j@5DGP #T͛7ܠ /!lǍrxc.s_h:1ϲ8׎K|PPuGB-t$B4@ABͿoHŒsi=~#hǚ)\# Zl%/4u${e(}.?z P@u5!jK_d1Iy.]+E11dGʸdC;t,j w$/;5ř'd=Pf|Nl:?Ƒh=׎6KS^dգ4!2Q͖\Z_c7b,GͰ5e0#rfX2[zW3..ؑ`zIX%uu܈o;npuuU/ yK옐ͫ8,'yW!J 9,[t K(K_H {KA !yT9LqeNrV﵎t5w)00(0 KW,wQčP( 9K\p6 K,-$&rKȹQE"A׾fz5jj2 zH2CH6XыKWa=ʀK6.#G*JOBFJx0Zj?Ig- Ow5 M3kIP*n.u ƏLX?_]O/R.?z ͨ}h3mYV =M޾<6ehRo&wG)'.5AmE?YB_/6;vQ/)QF#6i{mht<qdf̎ܝm?Y흶=f2[2Yp]N3_Tv 7T?Oob„~OǗ|:{Aj&-^ Xz3`wGuqv.sY"S 0̯S%% &99ƪeE ~ LG&|=ͺ$*ńIT #`7(Ծ@-f_;}3{yXU֎#WSv]s zD7flo>ywX3_nz8;F?ٍ+z@6,ӐAl8[oFHI!6v_[$[$58Ѻ==$?C#;n}-N 9r.LHLԱ5(U!yc *z&3S/Z#\)`)=_|Oަ*K;f9bqCC.)]g_OKrFoҹ Be#}ͳ6'egk[`\~`s4,qHya P#TjfE2W Ƀm199[;i˿hF+"NI݉8 >+J(`lK%{Ө^s.$U$: 'RӭudӚ>KQuձAlպVt:} ّ u~T+vmI؂Vhf&Z &j-xd9&S,Ew]ii'zT zc/?|T_M^Z;C:tn?:t$vJoLg2Hoa߾ogթuVS ֽ'yr[JQ:nִGF˙aĠ^}7q]v$t$Z@^(aioMvRkh߼\2<:`ˌ%[n=sQWk$Y'zHnO:X8u)3>:e&ŴIԫ^ ucAߩ#+sbVJ.$3d D—-  'ؿ7Xp9&&~g`,4z$!xݼ%'F vKb {qѭtH$EnLY`4z$ =}n|ٝd!Qw%N :VGe'C$#{?f5cL\\GIӓ$:]pJA'갧OĐް]}/KR$z/?r(i,0, [&Es>@CQVـn(dl^̉U$i+"wK.{:dm̄Z8_`GS}^ٽ?Zf}L?_>bvۯ9EŝԨ%F͆8zu4{iB຀3L&g}8_ ,vN~E%ywRB~YL?*<h@G' *iY҉%vlN*h̡\ܷaghTm=]z(+9O.=?W˽$OH~KhFYl^>5hT7<$w (j<6+L6L~3%::amOIi| LeulNA7b i'^įÖdt`.~W[Jw1YߙBQko6WKzFIy¼+:H׸< 1CN n)[V8O>h_(-짯Mr(C,v֛0yfo _{as*Rh ~}z{g**e9@݋@v]sК_siS6_|sClgRyưY#$Ԑ%[7.qX?De^!L> {w7^ 񠿮0󠷄Dr3n"h䲏CK̨`&9 M^&3!:PT(:>&1^Okf҇$+ yLq"IG]aE,s+|kVzkH1O\MyeG{ɉ_[hѐ{6jx}VN$_81]]IglGj/+jh>xw'xDSͦhcaEfF󲯋QVFZ2##*J\)`oB@:fkI ]`%HCZK%IxFxǿ@5:)Qf0ƍVͷ }&cOrtؤ .}ZZv=8RkObe4ab6)XsLͣ-&)~ɖ!%T8!\7'Ro<*5DpULI.(ʢ7 \ 福N_ 2xZE|̝euyYtEpRn ֎"q!J5א ʊ.g3e 5n}}FI.,k=[.wO$Y.6g9-]pHRt6\B4Y*f2p/ X4w3,U^+jFh\Q}mN;}TQӋsj酿I _兠]!>H Os_ IȾQ _L^Gn~1oI.Cjuǧt̮NWSr.pzFNUz $Z:lכ$0L y9񹮅Ǯs}p~)o'w!KvUZ`{8m9diPB]<O5׳p<o#O /lt%NC.^rkJj͆2>Z*UL+Zd ^ M*!pYJګ OXl@r6neR|-pJluZ^lR,/nCv|7S=T"G<+n< ؃cIcuO+ Ȧ{ij r? .ntn\._$EU.\;N%j_*dD6^ CG3X3SD* {_!<ԈʡǐI2mXE&noE <̇$H@H{@fܹoʹسs! ^I+VxTf3N%3mʑE>Czbnd(ҧιd*&)+(JAcf7klw-XnfMm웍ߑaݮ,Hfst"ӛH%n9U  +cJbfkr)䷐;X0a`)zT=@l؈B2oI/R'dٶt&B !QSʿFii^Ő_TO7zdӡYGTY~"[Խ[L9H <+y0 M C;w<4YU*yo;hy~{cA_o{pmMn?>Wzbk;W_)9 s~Ta3fӵk7Us!}tC^AY}QB?nhGжISbNi?S+6`qokQk'.t}ݪz? d֘&q?٧FZLyńδ'C5%㷠?2[|yߧNq.gSX$Xf́A+VST;9~At^d2ݻ.b.&Iu=!)H08Nsv#!!5O~_oƣb, I#ؾ|y( [;yH|ZztdHވQIbfT킘 1Wp};~o ADxRܯIToOBBcrkwzlL_p߂$gj,ObӺ#W d= }kjy#@wD:'d{`W'$oAL <_FIBW/M@fp_Ouf8?0kaTU/^Օn HǒG=]դΦyļ%I2rȡ$#鸸 %ׂ UZT1po0 % zd7j?n7ݩy'%sUP Wd1t>Ca7_b;/;9zx `a>)_ H};/xL&t|p9VNb3t܈&{K|2z# Պ*lqqT&#fXߚjn=%'}*J% TߓP-;N7hgJ)%ݻ+̥=5L$No2 [v`6!^Vu%ݤ}tw(&T뗒(2Q;{^JM*bmd֔M5 zSϑW#?GTVT(!Ý~sOni)Jos xTvTNۖnZ!˖.e^.sm'dhY̚۽$&p7i邯'ph߸o 0K->FtmNy1= 3نҳ^2$iuݖPnܵƈJ8|忄Qb.:5quoޞf`Փz+{>حFe] \ᛠMfh=`5j'!{[]m|r$o: In{t5vLU}w;m pοsě|7Wo؃LCG=bL_1`Z _s~n -{7s`6Q?z@V7}&/7dQ˧ճYɵB>{7v}f|qRRG$SxD,2?XG3 Qz!ȡ5ޓ.xw"H.wWz$!ȋ~#i|nPpNɈa>~y 9/y " ĝKTY{&u׶?o0Ve03f G.ҿ!}QD=ZSUwJw~mo3]ް;`UStOΤ븡_'6u9JJ%yU'<򞻺qQLZHx V  3yBtҏ-1ŒJ\٦ePWǎy%89,wf%t"S*N~JSɁPIQ&`i>f-K7|_eYɵƢFM1]#+_t/9>,㣏~d[ʃ*ʫ({ hJcm]n!m"9k3Y\lexi6Q!ֵm' EG] ?% o {G~:=BGA,ߤZ S;1Evo;yie6޺&E%HyhuZPr-Ծjoy7w#8! *1/lƙv~\|'ſnit7;zL{@ z%40Daؽέ};w@'Oqk#4]cK7ݎχzsUrCo^Pf*謶>4Gc. ;S.]Kd!\pkNUdŒsi Z -W1=j@5DGP #TC!zG!''?z񅥚K4&$'w7op2^J$f3ThHU}̑Z;%ܚoBAh(Ɏ tw䢻q¼::&*nugdZJ>5<Ѣ+%p[a;nq߽T/˩]-ok!ÕkL2^nsmן+(F槆]/J9~w+L8#+p]lHz~PD H{|#Hz;KQt&kť&Jq$֕;5ՠ.ũ-؈<,j^{j:BېDɎKN|63g9kslc ޛllrrY:}ìIF|.䄻Mv7&c?eȥa^MvHic 2EQ,ir{h :ǼKs. .uJjC$Q"%0:B#W/ + tw`LQQTʑ{$+rFp&`٤.FGx{ =.Ԥp^FV%ɏ=õRmTv~I۞z0f;Sd&s^ȦcЕkw~X;# &etz\q؉i]S^2/Imr~:ύLwȧbN"QUjO+ʎbVdfAZLţǁȜچWSqt#ޓBЅ0=O;Rpۧʡ)}( H,jE?Z8zk|u{43~Fј8_q喉3gvAU'N!X;VUw+儿@saN{:Ų DSR-3TC5vU(n'ʧ*(? ULe>=CvmG⋚E`ﱌzNƍ =ΒOC&"ʥNK2\C'tpQ_yY-RG;Gb/?U3K^fKr ,ݝgYDz8HK[a{orst7zEr[!{ Nww^<(cr&Ov6u^J]B3F,& $)/9k^Nv;.mG{•_Οi|eGJ(@@A煑6i@7R̞~]eه;o4xbIsS6Lޚ6t.w: UiO(r-i΋]v/tޠak/~$u~]ڇrt;%11kRtowq\#uVY[],gq[}P4vp{8enOqk/ |;[薣'd$;43\ [Fٝw((l)lʎ="3qB~/9JN9_( bĹ}'kY7 ƻWcy\ZLp"rm,?+vߐsc-d|OL9m eߟ^ԮpT^UVΨ26%L]ԍXS֓,utUwvE3>9)RzN}]n^38e>-s^f0;wKh2JPwMW#-׹a"ӱ7慨bU.#M]k4cK36ٙLb)=L/}f,z\Q[n12u=fTzL[Ħ޼;dO z|Ǜ9tK@5B.Ÿm:1nyF$[D$PvK^t,E(U^> ē?]Mn )b7D,:8v?oMU *{ΐ/bSl>4YF&&J? O4h㥗LBvNzXx=&J{7LJf򎹌5򯲋 SYġGoapz䳛~&J#7,͡EWcJbS:NgSKnWUʍ\RPd:BŠCGt9_Ȍ7*LI o]lNZ6vp)G_2I>|cJ̩(܈'Ié'LBE(U>yefgExH_]5$UNx:iB.-2cZRzfyÿngY@g\zIEݓ6QTcV]y*YiM=O}D\%L.ʸ*ԛ<3i={sI3,k~T2uyK:M %%[V[&opϤ헚kS)e])eyy =zW$΋@gGB)0gCG{!d/*i`WDI\J\ggytEt36Jܸ;=^ٓaitsrij?7 F㣠RyI!:p\wbcuvnϨ;cktn?vחNTVL2*mK_2IoDܽ%.g}*~eε% 8}&%kF{->]ﬞVҧ,y1mv\RkX.XB5 mr#%~Vys%n +3qjMG]X\P#]JV 72Ë\%'IQVO7JrYx#5]ʡApϮvLqtq< J&)>='|,Bpfy%LSjUOɔ1UjǢu)_Jfg߆"VT~+QsV2sb܈0YaїH:}$njW/cZlL-h͸Ru^uHh|Vp_ٻ疦1E;.$scewl(["YqPۘ&+}7Ouh|Tpu咼52SHL¥ݍR 8G]2;páԕ>D^J 3FZ=VY8\V{POT;sG &NUpsIsͿ{ʑ x/ES 5RyUI2_D,%dn:1JNMԪxj2?_TaocwG kԸn9;w4awTRG:%LO lMQJ,NC.-h{jƨٮ݅b۫,<!hn w{n'AinDɔE'_1Sٱ7-Qoaf49uiE/hw\[`i^!h 4m +͋Tw],S׮=B?SZ.]ubӭ  prgʊRكf8jqޱ{ϣ5g1Ј CG O<|#eЕKNi-t eW]vP8hokN%H皊 ed;Aq\Oe›bػ0|\5KSzmpD8} w,}]hӏ4kh5*W+0?[w?nЋnV̯LY+ka%dqO2b*8day{q"y%g7ێu:[PrP9RPUv ͅ~ jAb:b\VLc*uL9R2sz 7o&^P< Z-+JEbIY7Q_%yUSb9KLbaa(DJ!ەWM0V7*%J-!kV+HYmx?Tbժjw o<-I[Oy3WEU|):Yv%R ~p8~(]8^'&$,Uz 7IV݌qޕkmXvFԫ"у)}gs#Q4y.Ȩ:v҉KM5z̎ Sa>Ԛs#Qd_u7JKu>jR3uTSn#'S/WF1ο׾} 'Ҹx@.\.0ӪlSXlL{K$>zltl8qYf[oq Hq/K4b=!lPb9hPeR`ǓJbI7\5Qc澇4cǥjP gDֲ$}gLyfϫrO!?F,6nN }/.u]ofܭeM>ظ!3mpp9a4uAOm? &Cq ]g9Ũ2jjt4x(i츖mڷ^!3*=~J3!\[zz ϟύhO7߄ԩ;bvydJ;osNF\k#ETIvT"9> $ J]6G qoSW­ֳ_H0Wcws'7e+PƖuѐFEl4qHjaLID4Fő6^)~޽\G 4o={54*Bq_Yِ ^$vKSԅnoԻS\*yu;%ldޣM n,Ey(39 .IӬy t؊Y?%eV}Pώ߼~YV(u7@kE̜Ҝ?2rOI{^b=XCy]%AZbnLCQFKs iԴ$:V4떝9e$V}X vIe Vw)'=WFʡ(;K8.NSq!Xz,|(]@  ș˼Ǟ̜O0sD$gMOU$,-p$t$av$A!srjZv12BBW/=x!K:¿՜+b)x1[UzB>=q`W$є ٮ9&I< ^)^]:PzI;8nގsŒr|XC 1 q߽"{gSdS"=r(%E[/4+Mܺ|;wG<S#m&s#QԀi)eFbڝsǻ>|UiZܖ9U+xF cBl.,^<5,I^'WU]Wc|L=\s`ba }H| piɚFH\E9ydc)[W=ei o$=@}Mg,v~;KBwz9Θ5UϷ;=PJB!a⻍Oa6b$S񙓶(ɓ츤 Q-vA:nz׮]umPk kQH3Y7hbvxz5]4vwrI+6NNP0}D۽Uz64Tq^f 3!T͉\YsY?n . 98ːe\)uURD#C͝,1+:O6md+L$nOX0z_I:COnFUnqLg3vGTws?O.x%V{yP~iASC.zNdUL\*I>lLop7];.1!2-P=߆!ݿT~6bۗY^a5N4bɜTO9'y ?9-&}̨05[p02i ×R*F?NgWsUקr/l#&cO/;nɳYUkn>֘zͼ#&!FfGLs EPp䆎[l100Әsgy6RacyIùtCQ2o -E567Р$Y»tUXr2^H&73r:_R${jÁs6Nqw,Of#^M<[c0u1a i/'272f.ch\:GQMĢbHМn]UHI_ScJb39{vQYqێI@*4*%,N7e+Wy\dZ\2r kO#Io)%TDc6?G%S'W 45~<̛j?Jh\ST}rSn`܋ JY9LxO? &×LF&c?XkS)4^%n]?8KK1}r}܌.n$tWM^}W/vF3^:;tkܞ܄kٵ=UVR\-5'W\RaX%8HD ^b7JգVdHWhCHBӧO_~ Gtk/{OYV6rH;wlm-;95詨kVn$y⮨*D'FN1c^GIZjʒcéC26'.ӎ[[pvp&JaM]DM)8-ҩ^kT~M[5XHC 禯 !gwc#J Pp(k\:Pi`'iM)8\X.ظ嵘loIlWI8#0.v̚G4bDȱ-ܿSgm>XcEiss%\z" d1='ݼu}| k23N_0ɠr3ZW& |T$"צko0}iLJ7E]a^Wo3j]hۂ?oDjt1>xN٬%skrH_ :a.,^Hn,1۷'8!θuמK@ͽёmT/sssn ol(5si}x.]HQgnAt)v~TNw5qᇽgoNI"!#*+lIP p+ ߳ч>T,KblP'L۹clJu" exݩҳ'-|<:ΕׄUt2m [45 H,U&)ȊjTZ -ZrMUV%ԺKfJb1hh8`P>ЏVf\K_N.%y q4Y U*TcƲ]T& g2-(<"Yӵ5J3g՜/ _ך Z/꿷gZ=;:ViI7\SR1l6~qsojQ ]=&)bBknJ-8ͭɋ[9Œ45;viVY5 >VkJ]jYd---ԵiHh)z^jNKJAw͏_&W< booOJo.:!tw.),HkWL˞=E}\yC? ԠБKg> W$"R޼S*iWSΒ)o?d7j4ߗҦiX,]^^5̪56iTɫt!:ѲV[fKEZ=v:)qѓ;7QfL]:ҞK@ѣ rKk.]ݚ>I.e1,߾COH>+՘ bȍ'Du GT5l &XK{/X~{<xf!bqC+ /i6jjjLp *q2<Ꞙh4maܤou% cƽl.шEBG77qĉX4ՖE׃v7;;vqPIvYR.]VuL:`a:RTXi/&QV7wȈ >GZUL(Ibcdwg,ħrkHx:)8RU| UPkӱNG6-1t$yiqIf> #_i=N>s.ڵmU,\}\Mi\]O0mrGc:7{Uc+Z45q!Srp~iwu.K,8e)̬ E^[ͷ ubW n65ODb.AQdmd2%Uj m^f0mF!{IfFm5Imtw]tpr;Xt=࡭ X4^|oNt_W|0 p?; d  S#&sлSgo%ed>LK韛sӉ7'H#kv(|NF)iDf-Zܴr]۷nF=p#-Ɲ—T3~ qŊ\>4'z򲶶n_Nfl'[[y'bn7w[LDȱQgC٧ ½&/8,Cq)RW%EԘ 1?d 6LDe[n X94fl.޽K%02vV"K_ &^~0A1ZPE{3A]jp#$wLtaE߻bXnduJ+i˶~+>U}ıdˍ޳q=?~.r`գSu9h3b~ˌJO;q[w-4Y-+:,.98aI7~76D1Я+fsDp-7:@q:ovd>tH)9蠥AfɳYr6(} P\Weo俷zKzYBFٳWGj|&M{IxiroOQ2o -E567Р$Y»tdLUE)7smQuFZ%wOt8PsI5~)]^Mhˆ 7E'MlS07tߘ}WL#k[/ӡ+fYneaW>4pHij.Ɛ $J]:LؑUz?ee5˟ 0u_4cGZriudGN…>z\,=4m LæCWlR}nsf@#FkV۷7[hteB[O&TѠ!W2ݻ_Wf_d"](#@N|ccK3 5(u#S:aCpvؙfIѭ=YZ]蚸"Im8$2j*Ħ-{ԎN_ʽ|_kQ2l_}#53g3NMt0S#o5ZQʼ6Sgn &x9/#v16lW3X8ssxh*>hbF}R>R;i.+xPGPz&JXҵ| V#_*k=c .6=t*"yM7(mcl^bL̅'2ǗJty1vr̕7sVZ1/O+OGfW¸.o͍.PDmri羞ͷaT%|4;6\Hn5F&f /(Kɣz:O@Qyǜ8{jk$4#+*T$uauPz8jnd%:'7s0og;wxlG2Qzԥߦ3غGy[\I~Ju'{yQ-D2LR7>{E5kձp~} Ր5UZhhHwM^W/Demyпѡϐ}42$jmԳcg[Rg)Rq`vM޻Б "[FaaU>g7Nf%eӺSy#Voeܳ}@=y}v]&-t%KړƍixƎ~gq?gJ>WRQҝƲ]ӛ-x9_%演?ILlOiθZ[PоwwN2bܐBU\SRMKߛa;yq;t@oIչ[Ě2{|xnJٶ&/nny^$ 3 ^ӌA}[<Ϊ#hJcn4}V<4۔N#^b6}PEN=z붦^g xl4!!Hَa#:vխq LBw&EjJEbIӿcw썴*?d7jK" _L*UjYB(v+L YlvgL26fmRrf*u}#W 1ܐmڪ5{]*z+ɺ42+Rk{]Hw&͸!hdۯVIjQnHKkc{wq:˺>g^4j5Ѻwͤ[@l7TR9nq%>~ްSS}f2K@!7D T5`UX~]:RMv^MYڀбpnHt{MjCMXQ4a U8%jV,KyBvNF[#c.44>5(:R(h?zDs:4U="thǑ#GrC2:4 {:4% ="thbiҋ!Hnfh2=j@5DGP #TC}n@=j@5DGP #TC!z=j@5DGP #TC!z=j@5DGP #TC!z=j@5DGP #TC!z=j@5DGP #TC!z=j@5DGP #TC!z=j@5DGP #TC!z=jI(}..~ƾ^F6O^KIeޔ3R" n&Dn0S_!Qۧ7DB2r=mbǨ#燐\6$brdm+eՁd%^Gk6PVWA2Q afY]#d~ I=;"K5&t s3ŅKUMVsc˧na򊥦) P{I͸z+}]zČܴ&%C˛Gd&nYY%]R|;{>Gr)]nay+6O-&Ʈ:]̥]aۧ[ڇw8zt`#e* _5Yec2ro\'R(7L1j8Víڸb:r[#.Q[9Yq{*x--ȼ^nyGy[˒vgpnEp豋NTrsz%0@#=Fѯ:xyДt2Ȼ8LS˩܇UnPbiЃM4V>ʏrO'>=H.IoP-l߲]5Sg mnb-vd; >caϫ&<ہdnBe=:AT9#&tR ޑEB{9;_~S:IyA{~ۗk"?oFcʿ.=o;*>S=fFw$,-oJ>"m4 |QuԤյ߈|EIz~;#7?f4[5'wJڷ,J}雎ř)_jw>L'Fzv?)iمiRl2=xH'nuvl4Je={TyoQWmHzMV̽&LOv^bȉL]SeOK7X4DƟ٠#Y%/udϩɅmz13?O=}xLJt/tXh䕊1{nKf&Sgԑ~ߣ#C/^Mmѹn+歕9o$R4d)lY=>51-9c~qZr-wg߷n_.ZzںS%3;cl6s 衣evg֥;^$<+eJtخ~u?ʽ#83>UxQg D|XH;C)r'#EϾDMz%z:٨{4س9?fvs'fYQqΨin7}[Q#LtrO9[D EWIS5hkI;.׏dm# o:qh(:#Ei@^}ޱD7[7Z"kڭ]cJ޾;ݧӨu*ZeE͛g)*/y?EC,ٿ 9b9YaSG"._͔tݭtex[dƇtՓ̛j}n'*5:XtN'ar^lݻ)z(-2v{dO+h5>zl=g OȹbCv"-DHdtU\,gǫ]r26ҍ$Kg7EMoa2,5WU.i 2A(})޸i@f䯗{v"|n䌼 fғ*JBu]f//KFPyЄz 7T:MD1^STfeO?f=?XǤ>+yCGy/3oZXʾ1/=.A"fH*zӪuk^=JIL+zݪ 났bbSy:{J<*zNW|h(^.=]~#x&ѩLI ↯ݗ z Ļty[ن׹k}j͡$W}m׆~#}ʭ$B zx7mZ,5LF/87Nϰ0|YiCV`vG:L,72$rPTyHIfTl*-HM̐tLƋ"X3qݘN%9 upGg,uvCWe+ o]:{[snx FЛ\$>tM*QC;hy"0U|@ J_[oxyw>ĥh%m\$b>, rxl)S}y^-? CaV$7nDtjZ,E?!<}35L>~熺 HUdu>冺[ޗĤBQf|fZ=1*d,}[&aÑ{J*9(H75-3o$ k7[gf )^Lգ8~g=3֍9U*Wnzdҍ{?Of\o^+MYNҥKr߼y~7Rzd+ 7.?]O'˱2^s䍗'\+n 3ZlnoI)RRdNʕý 咍LH'O<ψ'4ˠoޔF{ ]yٛ]_.<͛gV]^s_~X 5rW*(8x躋ѯnY_o^1.2 u~u2gIg+w;v eAd}$v;N{v~Е靣35pfu%mc*3,ozq[:dlU_OLznTs--dzuvݖnICK͓pz;/N˹VUk2uka/%jid|uQe\Uv$–)(lϦ<_nΒ j.yd v%UR }U,b` J`z]ǚ!|9[WBi&Sn4D 1}^^oî(IZtmѬsv_=ZkZ$~FQyJZhth H~ǯ3327-^.@lݳ%;$̭VfZJk.y:W+}MoX֦kGz{3~'~_ET`JOSOr%sI;h#xhӱ*ڒLxvUyJ 2^>OE"\I*^PguDjĠFmңL$*.1xho}54+s>N߸KQZZJ+sZB]=sj8Fud㟕_$7r_ܦD"z\SڄY.u8&)cAY}Gq,]%":b9z`W߶Ĩ -+?.3vT)*p ;ASYhx&_I.T|n];i clؓUgx+=L}X&\s8tJBBN6C{G7va:ғ$߈DlG^hS:6l׊GKGit+5AAf& f/&%)} F7nrAOnx<*5WNarIkoJy&pz"8)~Cwx||ʻw VVkrE {JCUeZGh26|h?tX\RF5o^Y3uiӪKQӧT+ΚmyOz"*y#3zzSBgKDF4-'6z->Q=Z/Eԫ[jtm0Ռ1]2'7f5P,*HOϼrwLJ;[X8] z oˏLnQxz{7:ѝnk״nMwUv#yEWs:D*y,7rlYz;VTKЮNt/ݎȍ<{;х,^Qd:ohFcP۱Ԙ%ElV%pʴ,3DДkFSR$Bz:cIZHJHZnOii7{!+zFP#F_lȟMFI^ACLro{CdprU[]nf|=ɄSy;م,^=ztyS{5[Y&;\zռ/AhDgǛlF K`xAսQր%0{.:[>HzI'y|fنjP/Æ5_Jڱth{_?{*ulۺˢ;yOSVR𘞇%=zLCkcۢk6ԓOym;3d%}??-HI?CG_ӧRMG|1!B=| +Zd&?QkpRi/ /B1U,zΣ=bU_Bsdh< MΣ^sJ" r^s{bO{kZ&&CΗw~#A⭦X0UrmnD3Gcg06Da#2?實щ䉜;E;r__LeOeg̗)e԰cnh[L4>pWlr|,+q;w4FO*ȯBnͣS[3襱mZ~LE۷htZ', 73Qv}~4st۟l8sUd SNVg5zi8y_~#{NB3pm8YS\y>A_bv\ᅬ`icMLu>v/קBVRUʐeB'i_noܸiOP2V+)mGρeVFGoplЄ?{Vmk}ыRIKlf-5[+}+m:_3s U$"Q)Jjf-KFp"JChV2o$tbE)AL٩".Y#.)VIX“W5QUG,mNgz,}Ap!$"IذZQmIi+@QW|"}7Ǵ yx>K6R/D$$d%dENȹܔߞ xYMTridw^ YIR}/!%Jv%U L`M(n R ,~|p=4B)YTG) sJs{.]yM*i u:ߔG\;oؒZ Prq)LwRKCm6O_?s`Ei2cٍYUqб^9{Ec׻ tu ݐUs]2InjQ6(-8[ O$DZj@5DGP #TC!z=j@;G˂IENDB`backintime-1.5.2/common/doc-dev/2_weblate_setup_08.png000066400000000000000000001262731465446530500225660ustar00rootroot00000000000000PNG  IHDR^(m sRGBgAMA a pHYsMMgPIDATx^ \Mݺe/d$1 2̌m C B0}'BEQڴry{a4^˨[{9=9{9 ^V# #Qi" nXA P)eEE&S~cɑE/15#KO[KUEΘ1ct:B!@#ܑ .NQnnߑ|B<4,&-=+&H$&;ʥEk{L226%i4:N|LX4?d &Kɱ(,yB,E!hH,&.!Na%,yK ȱyF",*Td0{99txdRƣ3,jfxZCxЅ}x߁rCMmpjd%ulPUkhh4r8<\P5'Q^^ uCUi'_q J/zVU(DDVm_z1)&.VRڌ4 ıԸwh|(Y%`2"*i&&?`ieudL+lب;&/܌ˏ.cJVk3MՌ\@^n͚5p +DU1r19q?m`U%2'8͚Tv>@D B;4 νMkj;XWV4&IVRi/'U+$"uk}MŵI)Ӷ@r? w_a!)E=xyd+= 7Sg.hT:TVS*T E _PU\A@Qn'ݘL|X $ĭEA֣~_ykycD3"bJCn1?-Si&34ooo2)8WPdlζeud*8(x>o2n^L[XBffl_M4dF01fSFcZhjFT$6ur?tWSmy*&qEAҿuuAeռbދϨW{vbÇ^NOZqXM~Ftt@蓂!> ?$w "uX@9U(WU,|Ϭ}tFWU=!r 4Xbة+9B/e%w,+412x36Od>sWkkNND<?꧷W.q9dٿB&? ~ -NrQC1Γk[cg>á).Y W{{q d-+Pn޲rד)/HR S{zj%%DћX2(}zhwg^K>Ϝ(oYEQd=737fA|{iѮ뻈}%Ud^z_LEMM;^b0Ǟh]U%O2q4~ɖ%ܙs/u%\޲j1KN!T29sj <ǐvΣ˧ 7LJ`ku?ܰ{Axw/kՊGQmki56_>(\CvoOn?ܵs;,ذyů?nBoK" 1c&^]`ngh~O^/B}\ubnN!q0cVb&.e!hHJYyiؠ?O+~_{=W]%+}w7~?C9{_5{v8c\<!Vݾ}>{ӭ{VLC5U6}& #хKruJAb=@}ʵ"ܸmsA!It`'by7Dw֕goin.T[|d6_X\*;Wh2Pncm[w`VT=^w~?ql"BF Gn^݆nc#=8#~h`a8l| NC}uuMKDabgMV}M/|UNsVO͜,Tҥߍv7EPP-SspmjĽhÏ~u8& *'O+7=]҈iyO?mm„ OƯ./۰ω}Px|H؄lNx:z=} nc*r̙˲Hm44zzs{[+S3\XaDkC9!!kk3 K] A::WAbacdދ Un+}}FGwViMg֥~D"X{O,wNXȕ,RӠ^H I6IN~{뉻e|Twݸ*fZtc$GU}H+'">ou M q!MM+DQ~f i+𿁞$h̍z)$Rx/ -;P_GgdPht5c+C%ef)OWw3NMV} ]}~3.;\3mwŭ&Oeal@:u4ݳ >iؿ;TNWdN7Lv3:t||yk2ҶBM:TS \LOnUO<9^ ̙34ahtaNxRite>C;'F'0gS.~ҿ ,EV Ng5!~ʸ={ XKiv$O2D/d<@LZY18V]G0GM~vn#ֲ*an 'lg s|Gq6Y*zܯ_?YhwߞDwY3Nvd^qΫQhcԷ[=/۷GϊS.Tm"OQ|kANJ+h/{ve\{,0Z9yS=|W;Z(4ИFcZhәr& MA uWtS/hv8=7gK~pEҫKiL9t`ґ0iH/ /L$.~y?/QiAc pJG\j`gpM/U"~X߰~'o+BwdBUfeNk4;RwMMM" u{9:ETeɊ.r7=^+*pm=fḾ ("Bty2nFm}/55٪Jl-7EPaO\Ч @hB#L;@HGhuͮP 0m:RSE-E+cT]sa4 XȽOSO{;tωߡe8@3f 4@h4hB# F@3f 4@h_^^7OH|QٹdosiZ'Bx4ȐsLK=z3(}\m ]'$yחv/Yuo\b?$Sb Eǿ|AV_lL`hoؾ%1A~R^|u\YOW/qҵxg ?(J!`BpRo*9y^AQyz?!i~~ we<]:xn@:٣&71-+*(߷}z::qO8=;vYScGI._ŋ(>Iecm( ?7d@.=Hx,[vx}LAAеsG<gsnv#o3WK?knf鵛w:}3d`FV>_x>|71P,>ġW/+|+P51׻瓔Tӳ΍R=պX,Ɲ6 lh0| T=s~ 쨘xnjh_ 99t&wy|ϓ]}xwb1 pt!@&>2q9N67k[׹&KNϚy^o~wc1Κben&/)++1uӃmr9΍xuB0*澅ɒ EdJzk +5|&ހeQ|BHKC@O22j* O ӽqNd4>L|T,vi%q3K8mCg[]=mjCdlZXtd g{BR+A-X|J{4+/')+K>n4a)&SRf_Q6yl;Xm*xۧg27yvܥK7Xad.=녖Y?G[Đ.{QSSٿ 7qys~G˟0l45)+urr45(*N:  \Qdn3q3o58 Ӆ ロ ;*6tgp=LLi G^u4A/tqp [\Gt"ި_$Um]~+| PŇ-V:" / &gyE/  ct ɸ %1tzgWRdK'%$'PQ]M^\Kwkkݨ!.&Fw XGN_ [p+_"tlfV^N8km%[APix׉_W#=v]omi&ϒSSU>^۟ >Fsˆrsߑqg˫+0>>V@sxO #&J`ewU\|\su=wUUUƻC'ίX2BAAA]UM_6r C=ߝq-l}jZyדs~;2S'[ <xj*zڲƍ(:Gx5~Юqg,q`wczw xvjlp!n5pOnf&ݺlޣ ?z _}~wact!FD;`4Yل|4@>=e(+ o;bGoE[K O>K9?fVTٲi94bx ״׬͟gde͚:_?PÌK[H gm!|;H8!G. 9of&;itr&$> "$#%KE &XVF 6-#ѡle p'n";m7nGTTV]W[s@dkUPX4DtF|P0UYY%A{.ҖK,z >4 #f 4@h4hB# F@3fZn8UE,u}ɳ>H*27qw"ɽ NBɓ(""彑bWhB^SS]]2R˶x%πoׅ._*[T\J %>*OtԽ[Nf:Z L%d5ztl`2 cg]U6B= ,5{e)ʔC\߱5*5TKHښ&>=!aϩHR0ӦVPhb'KScC8bnݪ.\ЫW/[Z{1AGWo LcZnnnd-++:H[sۻU{2̌#w+MGcl sS0-kw|AOVܤ5{GKCUj*':J.}0:kjW DEbPoljcTV?#jxZ %h"3f_ūWB\-ctp\ĶdA\lCSSSΎ,} Ik4h@ΞvYVJ V61yz76JI5Хȡ{;u81Y1 U5"fOz}z-`"4*2(<1TiT5iHJPVU7VD3-cܲE=p<QvJv4ݶfJӍr4<忬 ԩS>TSS#;ɝ=vX zXE˶$6*8wd4:kQt] [my2n)33s;v600"oSz?aEED nwsMjYh@{q\E p}}}Ck{&'+T%MXbFㆦT\JS%_7 eJIPH(- GiӦu"K^Mv=TWW9\;4 4"Ļ/B D|%jLJJ7n!@|сY dW!7>.r|\TvmpܧkZDߤCԐS,6qԣL95qOT } V#WpA1\fG1m. O 2'Ѭ p,j}BN޾;}  4hB# F@3f 4@hiɟKݠ8}ʘ7LtGv%[Y+wnYb~\Xbr%W^/iISqɝ 7lb 2}AgB ~ݹh^aacpoUSӳpEil=եF׮4rz6z4&Sb2LvmT)L99blձ/-!00sdJƥhIG?L._v-611###+KA]}mȀ&4s; u|Xc=72Տi "r p!ƣ2bB-b-4TA(M'knTU?Nu m-~8Aۈ6V ~bB]%0utlRv2,.F1[^\UV!T%U:rJb\|"aaa@ '?~h"#* x%o3t5a!'FK mBcQQsjjn~oCCq8m(WrjڂR!_QέDHW"554 D/=WfHTQV?]UY%@# @޸ӴY9y5yɇvm9{i]DR*&tE9:(*y;<)@,+2Y^!,,l-eee2~#ZP(7nرcq3oh ^ec4kӑ}<-|e8eee8.***-U?NYx-.Y$8%.}P$bgh^as˪JZ̴ rj,JVe-(1(ħYEu;cMAqJjƹɉV SxQ\ށ&11q)22R[[ QYYz'J~QQd+AbPh乗<P^N8sa\l-H*F.EBF$bUi*FHMW~Zxm3f ͮ%{"n/±? jjjd>{ +oyD"E_w"##ǍghhH\FPg)P\ y SCNd-|^o~L FK4hB# F@3f 4ʹOŅKn4l9̿{~I҅Ao!ЌH,~ M\xb!S@P)N>Eb\FQ(8KC^SŒU-!ຄ\*fluKz՞<kh3}B\cd@r:!}Tqd;F-ŽGD]duLǻLmeh05by5ّ.DLLIV]x>V븁@\8;=G69qMTdkm5iQ/E2cQm5}0Jp 1n:}4%T#XvSjBj8׏;k.ׅ._*[T\J %>*OtԽ[N:JZ:4L%d5ztl`2 cg]U6'PVmu}u=yx>| 9E3ZR,sM(cy -S'5ʿL,fUbf=ήqy,҂F{>2F_.a~ݚXt,>6c/9}!g{.?oN쿔s;>&4fdddeeV9~hU^]+艔^1VnO9w{$hZ1E߲+/\uwB P]SeVƺhtdlE)Uh/;; 8+a_[۽G-I@ P8gN}n£1ɺ% Cyw tzNJF4Ե/'5SZKel-2 5s :Bw).Iɔ*1QrC~_"wGuQ=1[71򸓩HV1Du99O>&4ɹ0 k̐xyn"+kL:De*YI1F8)Nt6@MU]b@ _h"e b1Ʀ6fi:Me#B1E Br$Lٓ~{̥U:tsRW񓻻g]bqgrce~Hƙ#7n\((ph.gُa1MAq9kTQ~rJ+n\{|[Y?n$9oq_[gq'RP%yumu|Vi>d EEEΩ x#l~*46GUj$'J2r* 8H(H gwiҪ>^ut=rN?N t jD̞"![X4*z#dʊ"^SQؾ#)BUCHMQ̴C= PI+-z̉;3 $!ޕmս#1Tiݶ',^5ˌyOٳC^_9`Y T]pkScy76yrJu W7N<Nd_v)ϜrU]ݭ;o^WGZS~Gv10̃VᔕḨbTTc>NI-)+KO~ E"]m.vY/r?ұCؔ\Բ$3Je+h0ԼԪF51DEƨʬ9v6NyVTϱ2303 ^㒶;|,xƴdH&+h#9w ohE p}}}C{&''9Gu6SXDI*­XYCJS)H$ʦBP?@+TFGT*UPpBq@""ĻO&%%EFF7А,گQ/ga&qQMJţp9隳7i85:Mf_&G=0˔#5qOT } V#_:jt=~ FHZ#Ҙ6̿I ƀ't|4uqUQNE|ф=y7j|n@3f 4@h4hB# Fɿc&;K$!.ֱWoi Q[|wn1?sϏvhH_+UT6RTʈ^dMJ4dcrJZɈ9|2V} _u[r֋c'8 ֏ZWK3jz_4ǬK.-bΎիןé[7|'<= y^꟟B'į =V/\UFR];XmfխkgN,bNt:ŒcU4wD`R8)A$y˦8'w(ɩw 9E37էOqs0xv6Sr@n%ĺO/\~jի읾Ƹp 9J8mr9\|ŭUZK]r/֭ǣlڵDŽƌ,2`*> \< ,8A} mZWyIO|XTwY-}v**Oo{J:[vx+8n.\HDBAʢgu(Y)x[DlZ"Iq*[ïxS̔ pB:$%Jg9+ɴ\CA^_Z (EvcBknn.߬-ݯx-~H}yr߷fW ?wƠ2[QWƙXE+EM'knTU?Nu ="Ψ0VC+k*%9ӪgcmTU.T* rZҒF-BLm Ɣ<}340Oh;gg]bq`g0$7Wz6:vBNɒ$hBϫ!a.¯U6Q"KIaAT~_(-m-^po9dvua955UAAA,~R/zzuOj-$%ZmYӏ|?bfEy&~e/w]E=zQWf#O t=rN?N t~y:BV! +gly:t+R?ESVVdP [{hoǏ3UM49eBU% -6jRz>,6ƸOvX?ޕmս#10ܿyLfH_X^?G|4?MZK?̘.Xc3|x ^G%SƠt]AE^p Y}gVgER )@qYqvgf 1.׺^+&ZXT{BDQV'rxzDݶ yCgvoZy&~+sIؑJ\ į8.2,]@UC۬[Wnvv*t 1i ]vbmĠT]vfZ"RT\*!TTMՒqJ㘭QeOFim *gڙ '_Zvw":fy 4uMS}9H, BEB]Y>TMTĵL!Ҥ"C8 >i`!]һG+4yіN&T{ܱ# uI͔j^ 9%cek&4zrWֹT}_<8'5.@mCi+PRd/^n#CwdGFFס"k}Ν:r Y*i/AĵZW(bceIfV (*$L/ab1o mLuw7"lKL:>|ő4GM;*9LcYb_>&o3R|˭f3xo.g>+o`HȦ ^Dk[h+SSSpk,}0_窺=/ P=5p,+j)/*6Pmu5U\g}zt5rr2y~:ZBqkl-c4:#?yPhDq[,eEjpTaR tlcuSJS9HWÏ/]mN=rޕmս#1$q%*ynk MG5Ov>t{Ƣjd/P]fԆwyas];QOuV kA}7,r ?{$oׂJBN?=s*USM<^ܴ!oՉ2Z\SVV㢢"QQQdiky[<# O76rܾA!a :X[.?+i0Eu|44T`DjZچ\}/%ϩ=}Qfkijaf"-hAy!mwXic*LLW:Frzy(d֒$0Lg|A+TpHL^TkHs8KQqWpM*\Je0 Uމ O7P {"n/jkkvGM|=D"E_wPMJJ7n!Y_-xсYR2M q㢚p|(\}EdMZ04N 9bv GQG>2%?j4 >2&- :Dm~ IqO$?{ O_Ž4uqUQNE>OBN޾;} 4hB# F@3f 4@hiɟK}˞_k:w gg3%G_`.;G%}2? 'O+É]"nUЍUV/ YYYdT(}xbd|dv-\vҘ +ۖmz*6x}|;b㞬NU麐}Lhtsssuu%o#v[S&)8[^!BLuY;nׁk:*\݆@B1UIIyоy.P5Ux+g=@7Jڪ5Uv;HJ%F(#`i"ni-&*V/7ҵPS9BH@yōfOxpㆍRL8gg]bq6/<9r4u]iyNA.g=ퟶŗŤ˿zq.c0$ۣ гuЗ̀i4-.lki>'$Ap̑c .ݸY28t`d_>,49;;*((:C#_|m/' xl>niU'#QM@^QU+)[URTVVLkQQZ?*5qE匆zB5帹XSUɗvKCvqb3yJE%dՍYbRQq|QTbAT1QJSIXXT4H\(eS{ˎkw;px~JFǶޑEg'9u55heΎeʜU~GmMGeFm8~{>|ا:#~+_4s׮zCuOw>t{ƢoھwlvZ8 h~F:v?ӫKF[WU'?&Yw%}e8eee8.***_SO/ Ny7>G)%etOH6EnvN.m:Ӆ3T iT1HAW[i0A++VMʬޭCQڳܪF+s 3I=%o;|,xƴdH&+h#OxFY\Ш ';|M~"usd{&''"OF#" .brVX$Ԑfq]:"4BCb>CM(F]o^ۋdyQSAAE_wƀHqW'T"##ǍghhHW (ntu֫UJWy4%?QpܧkZDߤCԐS꬏fيǶoRof 4qOT } V#p*8'is`h |"O܌ dF < -7i|n@3f 4@h4hB# F}jԻdLfH_X^ɪ䰋uFCZ"m?ls |x d"l9({6oy妃|s9~ʌdlK\0l(EnCgwFo4l[?ZMJ4ddhVCcKƛ:L8sdϒqvNS'p\1rQ2ڈZܹq>QČߗnGu?'f/97Bo?+i"|ûb*"ĴoD,E:@)q 3&Tx_D 0L \*YȆZ!F8iǂgLKTaB`Ⰱ6px QYYOvhqB$Wx|Cw]JH=!{78M'O̾I B[}WqG1m. O_h \&=6J,!}w2&-/ hB# F@3f 4@h5~"P${"2FjVc}j?N}{*sy0hd^/!EZ)@!}%]^\r៓Gɺ݆zIE%ba١CEe5~q1Yz򼜜YY)3e>z#Džҟg̦?MKO|?{"w{xp /26nXGqׄXYDrYsy-BuG|7++bqO/TDI4MV>ퟠkM^vF q:' ;^4dnGIEmzɚ nT^QYE]xώeb1'((K3$umYs>'r&  mt&}~'SOU'B:oﭴcH|ڵ|IS] ]gձA7ĸ9wM.# 2f,K8CN UKqb5;'F*SEJƅEMaV('$^xTl蝻}Dil l\_8}" .߸JdGǔ:~..*K.s nQ.^m=se_[~#*tUVh`&N>_ Z2#QIp|N=m\#KWbJji5fdddeeKvmjPWCqFvy? Uܟ){̵!Sxx?ZCz1*1S'(|fLxg@.]&7O߼A^$3ƫlJOci/MB6Q.Iɔj#ݺ(93[`&Ю*f+M }=Fk. `3% TYKOmcBknn. 3K㸨D2#wT,TDcA̬ň43tj?0zZbM#G[{ӥ|Ě0M U:Q,R,[LU9;8RsWr(L|Dצn^WSm\M6}]ȣ՞_͹?OjdW}wJ4 5qoۆcі}tեi%U:DcV5d'> ղ2q\"K[M%]7OHfnS 4K7ӓ/_ ao Bk K39)z!b"|̍누BHLPht|AP(T `P\RRd44 67503BcRwXic*LLW:ІxFY\Ш o_ /#<(1+HOb>vDb<.o.vM\xB,cw8,7!*ߞ;>L[Wh|=.6hw}TPPqݡ1 @$A\x դqE@W~сԿK.Thd+y q9k SCNdf+۾IQ4qOT } V#_j<ﻊFNIZ#Ҙ6̿I ƀ'=-qL(~ )@hi'oߝ̾I FKw4hB# F@3fw#H$yT=%s;j#RS{Eew}=;ƗƜB,㇫h=eȍך*d/32Qҙ -KZ?gel AY\Sk垠$IekÐD}&Kgɹ{ $pV^lj;!a g1s_.IoWWlKkz으K _^OKn"rB4U%'ZEBH o"|B$",MTcFbAӰ_ DSfyIͰ]o:£1mM܂l&Rצ?sJRPj(S" } _gYwrC~bһ[}+>4yі{Kfɵ;#@ZLG *kI[TtVT=rWR*KCoq̃V1577̿q\T" D*B1舠vrAfb}_ƙX~:5x=-Z~ߑ}{ ${AtIґeϼmx{/UkNɳ ''fhhtȎ ڽri )&:Jdma(Ng2[v`)x"&@kpnVmj24t`fGOĈ—}7)j»Z#ΖC wu'k8{g]bqv)YGR2~RD5{ܯ ;eb1/~C r?smQ8`HVll[qVE RdKl,;hL>n:d5ERL\wt[Y8raYZۇƢ"ggT1Gt^^U * 9y<>(ɫ謎Pyv(I)Ӡh5 ^C]=ܢJ-*+./g4T#[asMe%>o M̒j?в3mJ(-EeA!8V+s1?XیqVZ2giSf2< ON(f2h?l *KrO@6jnrT̼n#0J|epUt^g.]=~öhPޕmս#1TYVsjjЖܺ]vnzkϝ[_̨ o܇Wi?֘ZwoۆkWW0g)fwZ+eoJq&YLsV5Zr23?ʝ$0l˾jdhmpjYY8.FEI<[U%]7OHf[Nuv'<+.)%1"=]mΝ^f(P 1FTBIPE> $<nl(sS# 3n 5::只gtuG#0 q9k SCNUm_ߤn h4 >2&- : /n5]ŁV#+ i5Ic\2&-2%79.%*;@hi'oߝ̾I FKw4hB# F@3fE%4M,K^H$Ց y{{P:aY};Eo&oic⧁OIQr3 C`Eeں2UTTUVVVUUTUUTTVUTUWVUTVVJKJKp>7T6ݯgT;ksN*[$U^_QO;ӌ#g*a)+$k}+8*TuBoutT{tv[ ;RLdڽAO?weOgƧr$O pަݿ [_KGN_)Gֆ[-o05w=u|/DV%kű;tpQX rb%+DD]F+ŕpC'= j27NL>ފ|~ݍ+_ɷ"ãbB< SB#Co߫yx՛a$5x!U7O54ҵkJO 71jLA{\'%#c!TV+7;Lۯɒ;w%xO }%THE nX#K`EA'&^+l z,Z|טEf.ڵޫqBSK U|3gϪ6R}ݳk5:۵rqzvsrw?}S!BfpV "͸?ݻڍ0msW}]XX)xU3T{ :4U5/qPhۡUH 率%u'BSi|1kY1 3&Tx~7MBlM\WBh \`"T,Tĸ;87lJwI(L.|DW`()A .=4Kr|ZE,GK_'e7-߯3J\4gg]bqv)YGR(>'K^~XL(:x¡ǓvJC,!>,l.ga=M˺=ZV@1:p0SGOen܌,awądu+Jf@XTT윚Ζ1Gt^^Vu?в z$Ӡuc65eFzk*C[YӀ?4ɳN?  VRX]h8&W4 hTA!wf6p"l:XhXC7ٺ0J+u?G[vXg6=z VUcxWRD6:U(R?SsjjЖs=NQhi᝴.3jۻ_vn<ϷQuaEn9΁;{y;-_t2|g-F)󒟿ӫKF0^MWTk[Ի[2gγdB EEEE6[^6f&7ܥQ>_NIYXWt/K= "Ut(T `(BOHQR6T 1|PYoAK!xiЪxϘ6„tšm {(dmHpwՏ7$~?xYS򎛿 О 7eBq@""ĻO&%%EFF7аmn BC<:~,&qQM Zв(\}EdMZ04N 9bGQG>2ᖑi>Q}/dMZ04~tZ}V1cLh5Z8'is`h |"OgNHYZY-D(Zw'o҂ f 4@h4hB# F/wY,K^-H$Gx'7lSX%ٷ#YFXl7v\^~2N,4й7J-+`DF֕ŨVZRZZU榲uq٘~m%^vAeb cX}C#i Q[|J N_ܹ1sϏv1Sh`B>[uw [wzYkrvr;]̐Ο#g*wӺ7~j?ppMrqq[m\D5)\Zu"CF/޳co\ ?z^ںzw~%F~_CBoT >t / [Q1!w BDIѡU1###++̼]kWㄆ&Eaӯo顧W& qP$>(_{or3i3F'L+C@ xz{Os={AP:99&Eb1Bb>_@nNH/WUR"":*njn -k^XQo`!]һG+&404J$3t?vZR㫹O5΃dz&vb0dzXieyEG(o޶437r?´YMGtJb4PHzX+ǧ+ѐ>,}-LLm Ɣ<}340Oh;gg]bqv)Y`Hn>Ell4ud턜%/I$[D>[H VD,%?҇Q5||}>,49;;*((:kLѿ.fʴ{27v++#D,[z!?xbR˫V42̇YIiaj +q9AJ +! jR+4*~ ;w3Gt6  :h*l]_Fo5K}x1z74yWRD6:U(R?ØskWN;X=cQ_5[; ,^1ufjz]qqr23:r,$ِ\>zpGr'i#^WḨbTTY2T;ȮAohܺsƏo޹KR|1!b1%gBHz*A"# B2TBDHIS)ACB3Q544*++Տ7$~?xYS򎛹 ^ۋdy ""ĻO&%%EFF7А,گBC<.OCn|\&, PtY`hrJwŏ|Ǖsi>Q}/dMZ04~tZ­FcpYqO7iD.F..** s -7i|]@3f 4@h4hB#Ɨ4E4=D"x1iVk%bd* 0F:gh2PQI,1{,!+YVqyeVV󌬔giҳ2r2e/eGTM7q%ّ6n^d8ph"8JQ¶ύ&ن@ƖRi!iX@yqs_!"2B'_.LL4\#vo cɇx߸vSF{zq^R4įY/p . q' +\ǩUY/xDNjĭ𨘐;!"ԤЈ{7"$}%] &A( ݟ^Q/ .߸JdGǔK|2Zqݴ ڴ4I:/9*ź7;L k$࿮;6iSpZ7Bu۾՗dX]٥$¶x@6Ǵ322Bg BӜ=hPk ?@D^9?#WS.x&|l|/@tqz;9 vʟ+[Y[N%Ս *s 3HXYQH|ƻz;!\UII,)1iy"BR o`4P[CVOWx\>3&TjW3#t7Cq[;FVKelcD2D9) 91rrC~_"u1^ 8Y$=4pZ.z6q mcBknn.;:S}ޝv==MS`0zrF*B1bw[&7+g8Ff|0+ŋںzӸDc$lЕ4,Q< F{KO#MaDB9=6e+ r d W6gg]bqv8ȕl<ήߟXf1DZKb_ۿvL[eMzk ~A.g=-ԾĔtXn]e#S?99&KrxԬC7Da955UAAA,~WËON42ayӨ\xMUxíi@:Q{V_+s*jSYۈ{\(QDڃ0ӸSԆF v: 8T&f4 m`r'2qKaΠW]Qʡq^i_ޛӿ٣瘑]+y\𮤈ltlQ%99u55h\A?S~[5{?زs=uz_̨ o܇T*}#:{-?mؾzȏo6U]>Υ-;njxs'd.)TU'?ֺ<'/̫0h"%="S)Be2P̣41Wx ?Z1^d*c3%s@0!0]q@> 74⢆Feeexx>١-M28 tjq{~"b>~FLňj-+4q{Q[[;>j*((  .JjRRRddq N^FPgVtuG#[) q9k SCNd[5[MJd^5qOT } V#RVyUh5IZ#Ҙ6̿I ƀ'te2cLP NfߤC#%A@h4hB# F@3__=X,K^0H$'x'7%+vވTeD# BC,,BXl7v\^~2N,4й7J-+`DF֕ŨWTWblYYEeUm}n~l(D]y6_w[Iwn9q|PYX'!.VHZ~ґW4:kʡ ;RLnBrL->v%UGElNVʜy%AΝ^8of8}6>s;j-yB>Z-EK,9[r(_{our3i3F'L+)iaRBg..4ٳt  ijB_'/02֣LEU6$ˮk#]ZC,'*$'.-2rfCHwWx\>3&TxcB̙3 C2vt䋽;I)$t,m lv0UzΠ"@GC.M˘P(B\lZ.`jhh|44雡yB9Sݍ=[SKڟOMxmm.gG1_> oˌ6JZ_lR3enܼ _͟6A1:p0* =СÍ7:udaaA %4idFaU|qPN[o1m?.ޅ:5 sL&(aVRdڃ0_cJ\NP(J`DGnmRJzEFYwIXXHL!}¦Νm 7n(jС"B+wG)ypRa3.^md%~ޕmս#1TYɢʔ9.-9`ۻV/x;|jPҭ׮<,bνkv%}uJiO ISbPRs4*>tbDiyܹs,m]~!gY|5 1!~+4=zGIIߟ,ڣw X~=ԩE޻Cc^^^UUɓ'"jd>>CBͺЪ'Ȣ$-7i]Q?dr܏2dQ+p9?iLKߤCԐS,6QG>Jtښ8M'O̾ 5__wn4hB# F@3f 4-H$I(z^?̤id%[/]Bw;Gf," J#d0-7S:"+}="SRea[|"jdʪj :,)ҟgdeOfs7˛<F\>}D{;Ϭ(Lv!#jM?kB>k^kcn贔{vsʒyw=k[${ΰكG"5\zĎ>w7N:.K[6% cW---">K^ٴO#z󗬍ȨK+s7 \'%و-hѺ-ΪmjUph- (-S6 w>ZU'{.ˆ[QO+ʜ8J)_Upv(OU}$pk &W>9%YJ`bv*;5TLʀq((-[mI8QsmO֨"~n;ܐb [[B qbmR!mCF_OuTmjYf_u}6cE h/J9y}g.>w=;C{3.::z\z9/n'(:5IڨwΚ3'򇮉YQ>AM6X\G&~۔yXIjˉyRHppr3$==΍L"D)NX*]kan鱅ZtM{|q],f'\ NZ5uLb h}-2z19ٕ 9T@嶘{ͮ9vD I:=}X嗽r2ծ"EFk*NJH,jԖ@ ܌u>,Nޤ X*wjLKKstt  DWχN0)Y7cs۬ 6b])4Ca3D~^FOnl&`*Je0 OO%.)%b@gXw0U =~5݆[{MjGR))2^abdp;&^  `Q^̭̆t4feϙL C\X5KÇZ.dT6T XMWFU=aZ-3~gSiգ*i۫ r I=GMQz}M}]Qa3/~zp9^@ے6-Yp#|g?$OެBQIOZE |KP/C a#i)~t[xTHxԸrJWWWKKK'MDվ $0sgqQ=_$46HKbeUfK-jB,2}.d<'7Ⱥ:2G`%XeAԺ+ָ.c⊦&1O$Ғg"5Ft睿<#φ⒪F>G &'$\u? `?WP@ᓌ<kb:ҩ4-ZLBlngeٵazh`IM!NHS|{O ȸv!YBĚX]˺ $4$zPKw-ֱ}ޛj:ծjʻ{psamq{|zא>U>_2qw*($KOCj wqQ,,w׮n:+M=+~[$`hzg jZriT g¼C~Fmoo)ypuh{gGB"jf)PiܷwԐA'ї7.6ϜL;0/" g###a" %XUU" DRâ[y) *AI*pm|8%ŗϛ0~{rB  HGFkfi]N_#9ARc&$*AI(N՘k}84jl/qRbĻ0GRU 9j?_k*:15" H{  Ԉ  Ԉ  Ԉ  Ԉ  Ԉ  Ԉ  Ԉ  qj| Lj?N/\DEs.OC桫 }VxW˨ J [AѳԌڿ: [ ALnuo`ekʅ;{Q\KYF4s|l`y+&vUn-2Ǖ5Mޮ;#A/S-otȑx Wy9@>%),J.(*){WP$e=ʅ\Y1'/pC{a\qֶbS_Ggʛ}&"~Y\\9w{d}yȂwp${n<=9pjB(~>rB4f޼w|Ckk^6zSDG525w1+&=ùbt8gUؾthp75֤^㛍$O U.,rU:kٮ+l4v}gމI(f3AXdДdV_pn_ IL&Fz]zҏm2mR)&{:LS`%Aum% CI0:ypo:13ղyLK\+ ߠO=A?7?˝G P v5},=k+F6c S>X6[PgċM*UF>I__Y `X{G[v“FꏲbK[2̏*J DZ>X_/ N0EdKp3lzbѭַw۪oP$;:x+Mk[b0Lܬ+`>e}?c!w6:(=u0)롳 %b=jbl,$pOaeIL /X>q@ p)f A\񳂲:AUqγ 5X&0K7@8=M(A#y.|tlaɣ X*.[Y84z5͇?8g﹭8dͳBw:\;B9Jbcow5}tz:|YxQl ߹l#z)JOScZZcpppHHU&RKc 3D|1$1;'n&P^pf^D#[pj~DT4YD߀ŠZ'4 j$Ӑ͘iBn-04݇ӧY«]`!!KK(J_ 8> h VDf ֩j)T $ D˩VXv\ּHDĻּ)mMIߴXu# CC;UE=W>l4<^ݼi)ee 8p<"yE0#wyLX~aiYU+wt<'uItFUUY(on7FeUaMDdqPzMդ1\kҥ2T훰؍7o(:4S103Qeru2Ղmךl7M1Oأgߵ*-dA3؛3Y,K^L M [+hϯJt&ܺ'5LlG95msa4۩sW^U֎*]T Z3ۮ򇟛u"~,7K+كo=X.Wd*Y0\<sG}u-r|dw*776 }lYt:P-n.N΂yjnؐQWp'婶0NԿףVv ´r m uNmhfӀ^ttM:wք;BM"]]aiXվmIJ^lHc{r-454tzzzzThfʘꉗO*JpM߆]^'7n'5D}uDPYL.I(ʼy^tl|G$r܏v>|"(51zD,D>X!_i?.^-~c)q9<$%vYevaHsQsGds0޶o/o:v(}@QWWcj=zvo['&_8۫]N*zeWDlz#,Jx "}g__4;47gL'q'G.DNZF_dTpf]28qM[O׿V]6~?r倽?cā}]'^q{7~ٰ8alX[ q36Hkkr5$"d 8^InDD>*nTRQ"0\xUbno`~?< e?b%MMWC򆟅 #596ؘjp+9`ȤO"1y q^]}36ɠC{+7WGWhk~)ׄ2wjLKKstt  D*biyLaJqx^8[NNe[j[;ms&`Ue@[uKH$&^Sz)A}wkjgojdWXXa k,!tXT >*a1T"nfurdT6eDȮJ%8O%=. '[NRV^ր*alMYްY{hfO9=ɩ{g݂ g7c)F`>խzOJɬI yjBA6^v$?@X]YL+*s{9t|F *{T`qƍ ,G777M؊xEɝRSw<{[~ݡ[#,U:X;obmv2spH&Z*,Uu9pn#n0λ{g&=0m-3}نn^~Q'д1T &(`qY84 L5M87$J8mxNJÇ:6VZjEyYܢlݏ4c&75221Q/tϣ]jRTMjLp6͸oCҟBSWm=ޙ珠ڵCMywϥSvعi?mH;@:OQTi4Ynpw:!/~~޾~fS1˳SKA 퐁J>Fb~/ 8Fr2q|  |7ˠ3 Rk4Uj5|2faoCQ HI; :u-8 T*I8ErxG@j4ZJIH-T-Yq}u,i5;J>k֬c5W a*m6P8WRWU>QaqD *޽@J{}S,H<}9H<Ꮗ(,jO+ 0vb`buq<+W/SKp9>~`-°,%<  8b"SyD?Ee }|G~nz \4DBK^jܡEvӂHcy8±yS*NrO($ 8H3\NX\\|uPAA6AJ"kj$jy\f֭WA&6Lr, $HBo0A9N<˺KjڀDRݿEn廿?h104 >W#F!Y1'Al693wCZP 0@sOVka|ߎAZGoP1Gs7x Qg% uH2 1"DW/3rkuVHbJ)1c\ TCRpb|ֺ 8NT%'+j8)cXyRT\,\@P0!@vnjAwJ*U8+/9M c P,c-&P~}}^}a4O 9jf AHA{MiA9־c#FՍj,*B[=nbn=*GD?{ekO76ju9Sh[ɟjsv#mr"(tд,KaA4-:f1{N4S@܃Ԃv+WkWkשb6*;vij19_0KhJф4'ԂSywҵC t/cbԩg2庚+>$Z#J!Nc&B]O,v'U{4PȗF-uպt޴QoZR|Ǣk/hyz,V[ J6oa7Y٬Eדr!aMKqݚ5`H.  ')QO`6'Uʻm (H"cutkQ9REظmv-bjGzW3ƿQw7d;J< }-Yh@VެU <<:K@8ODXf:eK u hnPyjE4ۮ:++%s394$I!dܮSݧײu[eV'* R`-r}X8\+yh8ţ󊊑-b 0g2Aq1sd6pQf\*< kE,B]^aV{L\.ۤY }Ƽ5:Zc撍-q1IRk4WCBB^TK cRkc)%ȯ,_z)It A}~P^JeFc3>zmK`^obt:z y 1|h9$N=k}@軎vVn'FV䷊6lԤMñ}˦.ԯ߰YVժEz86jTa vZWՇ.[rU 4bm'n4$* :Dgn^g^LI[v\-~nĘ)k: @KJ8p> N8mFV_2v k}ɢ"cZC"Zl۲}{WX4/O4 vC VBe!ΖtȺ&dūm /J)/3~L~5i_K>ྣǿ{BnUyZ ^xA9&n KA hto.xÏ8E6za˦ Ξ>جeNݺye—й+ яHzToܙm l|B`tAKst@NH \E#rZeo8Ru@GI'n賴$@'zwT{"+3s1-Z m&AYjp*囎!8Y-Sᜐ g)b˯4a[S*U'[6E~cNhK[wg)ƽL9z9t:[^|1#Dy@r@ Mڶ._[0d^}{v184 I?ȅ֩Mb0efrx`ʁjoRh?hԸƽRHOg}b,ٲc^7ntJ8MCҸ/M0% xZY%Pٮy*spwt~r]=xۺԭߠfנQAHukWU;.,noĘƽ|` IX#ė7rcbhTǽҬ30$%4Ayv d^Rb1輪y˚y705TQya㝀솎p&IUn(}`Nz!r}sGzj@;} u.]I9t̐Ѭ<{C__?q/ lvu&ϏХӈ7& k[dT6ӯ.j(*G ^1, *BViAR7)_=M.%u:WW;~79ȶ]k)E'GJ"_Zl>?Q``#{vyF\q.dHNf机S.tyfzb 9(VէlM״w1Yp97jc|}]!̈́iklױ`Ga*ceoZ9MY{=@Fw:/qgmCuˑ]Gm),Uk]f_k=u_PХVDwJ';yI(*D7;ma? gXӦNgLݣ"QPW]-T7>l)TKA >TR<l⤤{h᰻vAZs:~OHI debF7_з0O\nޣ>|R]3-?^b ieVߠFV)j5A>49nVnŹIru A>-2D(Dir7BY o7q0US/ h53>xS[vkP0>ːWbb:}{X~{6&5N!qd2F#ڀCʍz?hpn=z3Mrȹxx^s8hĆϙ=qP:ux+.×IRMpͱf k啜&W-ֽuP! Eƪ=nx?o]- !q#6_yr8\^~\`LԫsuԵ[{EYo޸6ņ5i q t(4t233 ^DqTbo~~G %˒* *Ƴ!JgFjITՈWoo?({$tԽ]QD3luC.( G>/؈PRo{n-)3fNzGS>΂VJGl>sO`:J\Y}> bQdqEu*-p%B\%[6%Duj:S͗jF{ZNp&`)#9>>F(_zZW2l!b7^ۛ$МGލ3'Xn=D7iZNށ!;IGz>'łGDJ3PuꏜYoԊR͠r~*p.W6~}jځ-jԠgI =V 5 <rʲ6}]%(Ԡ]iB8WJPj8d]Gh%$P|Z<kF}kVr. Ԛʵ2A!@տM=(=<@]x.KJ%K߹Tx:TD%V P1P uEaP PՊ\m+m _p!y r8#47cdSC %*[=IH1!@<Q7]eVmV yF,>&w_ <~oL_GIv#H ˹0pW n*pa-/?0MAK:%%bcfzZU5 KSt8Χ$f ; [xd5 :F⎥7,8赿V#:5G^Tˁu_OREgg!o|ڬReZGFzs QE<%A }G@֨YXq5koڲ!3BQ=w>~ۯI%{83 #>D=;G)JJ1쯋fpu$l5ΞܽWZ9˕ AC_$-~>#NRJǿ>;mqO*rv~4yΝ~qܻ7ٵ3NWe#J_܍B^xǭ3җ,Y5[N<8eVׯYi {wZrtX jwLnsSZےQc@XF"'b'䝗;B$Q]Ų^H":~K8{d1{ʤk?vHٸ\$f/=~GIǯ=w xF٩(5 L$uA= ?s&C͇}/&a;Fȋyp1BYhW*3\7ƃ;d1 o{l؟q;_OZ"š@~%,SwyX_ uE5TmᯎM?~դ 䴸$Ϲ.Wb>}y %.GyТk 垿n{uXw ^oJCɁz)p)Z*E\__+(`i7xBVYekM&/oOh^_@. vmRa-ۀa;vnݼc떢Ba*Wҡs+/]P HNШgf~٧رEN Wՙ/#[ xYSǶy(Z MR)45s׳ )?o/Ŵ>-m{ ֞nՇԧŜo WFnZ)|w|n?̛WXXV]NZ}oaqRIޚ0qeg[' |F|C"((IT:+`nN*ɛI[Ds(šgJm sz" u'4a2Tq,paY9N A8ъy'>};S8O6pL~PZ\o3ڶ8׿ÅM:IdJ.zx;;dnݴAă;sZeGڕbombm[o٘B{roH%Sp6 ¨M'oէBԺ0do`%stf]̗8#Qga-TⱯOO!W`֫,ù!\eH>ds`D>p:nemo:0%ی*,msɗ(ר%:>_a9{ep}DpT\X/$U괜D/_*uݐ/ja딦[b]M+IX%W 1/pP%a'|%l!ĸYfvRza؋Z6'7;:y:u!r~ko̘hmG?|JB דٶC'в Cf?&zaW CssrکV ҙF踅TJ V39GF{WރWaOЬmt 1wd8$C`OX\lӲ< uv${e@q[_zg/ުJJU}/]jq>=4qcj&F:QQC+R0_pٳx-08PEFl̒J9{ھNol|(-v! ó=kxk=p@[wcOp<$2n3( TǏi!Hp(++QWOVk9\^$X-} ~"9o3qz߾kM4õsA*WZ,/Ƹ]V'7r#?{ 8870(3(4t%TI%ኒ⤊Jc(UJTjќEs+e,/^A{eh^HVإg_#bZQ* ;םK_"Fd TqHDC eatAHcO"a %QA>T _D8"휝q8Y~nHq 䫳q:MFw"J]NsnNiqRSgBDLZ+)绁FXZi_tէiڶ{{{ԩc~X`Z%>pVd+.T89'+Us\8m/UU~co QQ BqJ=꛿g/Um'+֮u߾'4jםG$EAt^ę6]?nq03;TLX:Ӎ:m6V [U/|O[lq횸+vIQ=q$-T6~q3տl̾i+[AzN#ǧT:q͹Qs~XOOM4d$_d)-K'_ωhݹ[Ν:u]nOq+cPv-ȫ#@ N)Hբ7===x:y{|8yjTÆ޾Z [Μ^l`Hu$rRJyyKJ7?~:zp=|PmJyg^fҍDw-{,zT  o :}аaMFcmwn ./2>I{7.#:xeȁ;3/%}F+޶( h'*l޹0txK޷5͋leթ7-!jNRO@!~7?v )>w$RBc_yk`Z?2Ύ=U;XO[(i\mEg~;0͸%Uj筩G[|@ ,\cFZ!1cHT᥵^}Cwڌ#&bx~x~BҞ!j~\eIߔ~:<4͜!O_;j*tιyA ,+'Ѵw*iO¨_cY)Η@Sa}j۵siC͊}t*ƮI7_>,ܱqG vX|>X ju}C%.yo6q~ ܝkָHWmܡM&ի)}֖pD~~(uHft"lZwѱw$HVw}EQ cE㳹[Do(6@4ükh,b޽c{~nFмUԔb[]Ns/@fW/ƥ80~1'l[s2E=Ủa/ r ah~~i9sF3NemxC.ǧp9|BL;wLKITlL˶ZsSne-6g%8z,nɻqJB1+:7blr-]Q@sH($ʑ{[vKkPAK͵3lErI5} ( J`~E8"W{jBIO;}l6]صՇAJ|a+)B6/#ҩ 6\2S%vC1_d+{ҡ{q{mٶ}m[l޼ʍ6 r!1|P'v rw4S?LLA9ȭc;>R왓$kz]hN=]x.'=l9oמ94gϼv$Zg_O.^*v yXQtb#)ګ]ϣc7w|umK?ndAIVmqm.-{6|}̎>2Vbu='V^׎ct*_lV4ښއMH wdY'T';T8c~x2*Ԗ7rmFn,ߓ*tu~YKe;i*zZtl#%F|~wM?m5IjU_vkʖu^r3=ZE8)h:΁=޸)fzV wŌ$lDo;dz͸B+rߴun<<菛$֮sN杘 ;);h x#!&L_)jI[˄Zi7bE4-~brkWר.h}nacM0d1Lm,PXKW!~C^*EVw |^Lw@C-, \atܴ3* Yד3   22 t3%Ys2YL33rx}L97#zRJj-!  !.^9+v ]Ț@;jZ-!jk?U:=$Vw!X(eP !#4:lX3 = ;Uj%Agl5Dw Rw@j)UrRwDouur_pH&!{WHH6w+-)ȿstp\:r Ag;I*y$l4:v:$lv!7w5~_!*AAXՖ:쬇]WwZ%OBµ>I(/\ow70'NJu%?/X˗ ~T*5u6q/s[/0WYCI-n\[Oh9?F)\WIj1PJJBK*urAbAz,8d}Ub[;9v汶۷v?I`Mc/])4rdb.N˴jɻTajM ּiR5Տ-'o>jcdGI(E.NJ4UھFjs!N(|(vISFJې{'a]?Mu"W!0jħ;y{.~(ك$ޠ ]hΖl0>*q"5֜i.xUүҌ =ñ"sf p C"4 9-p:VҪ5.yz/N?ӸTdlR2E|:N8xRUfg7ܡNTiFƄdH\To r!?2Y36BOJ%1-=ҹHժEfH𿮤nyQu*!?U#yKqPP]qGc#~"-Xz%e=%J'J4}lϨ'0R~p)B! ?җR`zFhޯ4wbC{l߀͙yKN4$X~niq É!d GyW9؛)ׄ8 D)/9%  NyR4((L2ې y/ '֖cXg"9I~Vzcܖ{#C{y{kzJIe8$p, ͐T}w CJ *t:Tܜ; {a^l69[rrWVt]'5%|h:/o_{ɸw{penK>[}\!ZkAWRRSi^gh@PpJgWNKnĕԿK >K lK=p,ɪvgAriջ p6y7 !)|DD5UXVWy6k܈$IVkT괴WSȋ_ht@=<6kVVs26A ;" \ UZf`4ͤɿxR*U*U9׊''DB(@gPP* jo-~) u̸לڬn!*^v-T§JPlXm6=;<̓DAQ 6[FF(>H:J nw-$(@ AZ@  ta R *A85[,y9y{j}ўEaFv{^AfCN!٬'$>p.j<#%a0PPRxiAQ9?5d!C |>yy Q1=H "@_`R@ /iA@  ҂@ @ /H "@_HF|$Inay8,"WTϝ=fEL% ,o)Hk ^<Cm/wpGY1QlFB8$YLBEWa AJy)i ͘`lV |(@ RyyA\sJUٽO3ob  ܊0Γ6%s] YS.d9s4M_8-Hv\ߠs ~ [9[!JcEp-jv&8wD QNx-h?^-}0fΖ#'8@ooisig*jcn?91|ܡtRa1Fuk.&0n#?]_xe_LpO LW1wW?x}+4[s{x SV}?869ș_[d$RP kv;`d~ֽ U:l䚬bЍm[|}nޚ'U{ߒ_y,bki\"SƬh2e)Ty-Jݮ`5K#8CZk#~~ՙyLWkEs4+1y6Ϳ7]dzPr_"#k:3Eؽa}'Ǭ޾l۫lQx}pn_?7GkQ/{꽄nj}`3J:5G?QRqaəQ@ GЂ:nj1NM%ş:KeL^ĩqb\zq4JoOo~(m!yv#E1khSX 1JSIo:bo6OoW^N61{owզ̉{.Ȟmh٭uN3f-XιNѡni_T_A0NOu社Z-:LںϾ9;^_v'ИWjD=j[)w wv]5Rw*G!S1m?EOdi$(o<z%Ei&|>XPXn]>%+^Ub F5KID窴Ve5oUmgS^d®G zS)IΣVĵ+`X)w[9s6[x/lI>&B5SM] ;3NM%_MTEQ <&RY1/Cb~lmbTI8 b&eM8}AZI}kNs(EϷs ) gO,Gݰ|a+}՟:AUTtcH$OTUn*([8`qVLR|Gx5N'W 9Sؓ)$ClMxezH̫/A>L@ RM x"V|Bdrp\A`Ŷb/}m Yѳuj[*Rݿcϑg7}lx @Nm1~ݤas$H^|*J\&pHR,i9 n{W IјnmyIH{5Ɯ+wlĦϵ)<8'$R BE4&q 3m:L_4^̒pJ (5njo֮rhkQ!SkjM;($(u>aY? l|*A(pRQ2B6A*pAY p ($򕟘szcΥɫ\s}@ rhAP[2랏XDuu*?TdR߿jTȝ_c)rb=Z8]^VEYêav4WmfT$+V9wqwH"8]PW  {pp#Zҍqbw]O@RR \02Y ݙ6يmҩVWɐ\HIG ,AX%\`Vɧ"DԨm:4yhXڬ'~l#=*cR,}ܪKi a%߭WӀu*Zi+ ];n}HJ|mݓqjcAJ{s?@ r£iAT RiɚS?|oҌo(򢅻 ^z/x;Tm_XglſWK9u3g᭹MFS07Z%e֒}?Y_}0=?>Z5䁀y( Z^7! > M<'3Ux+40ۊ6┚ԄB+H6Ẳ͇8=2س{ḟ=+mfʹ›Tj S)t p)$:p nЌAVPGnn4ZWL-8ɣ%&L)Ԍ Y5ѯ, ;B}Ɓ US8R*TZӐz=Nx(tBq Idכ} AʋRd%ta (oޟ<.3M7EYpBJ#Q ށ!I"PW H;hET $~Bl@Uf$d*("BK_K)Q5ؐydz*! #G. Eҿr޼9+ӡұ׳mN1'<T{#GR%Bf&e`/Q}Ը']6U*alэQT)$7=,;Aga@>h+N>8][NeIj_((.N(*(׼$ZU[sϰP y[)3QkxԨ&sf8m*հDDzQMLh*8΍xuBU c3z;Xo_$y $ t:5MN>Ri߻ijpDg2ւ໡擤|Tr}7-縐 }->.;+aW{ǖ{*z_/A6o8fs{{zR/CYDoаq HCcQE)p,ہldJܐ%n8HTL`avԍWSR.YtĴcA@w#;7+=5Ui$T0h"`(iI  ݭGo/;s 4hݬeZطc릳O:qzbb`?Кn.i2F$J[,&A-Q8e'oP:#@;{Ot\ҵkΟ;c7lT^‚E9r@\߯Zdq8"p0"Uat!C^MMz;P\Kql)6H(|T6b Tdd1"9ȒiX*jmҴY$[m_!,|W.]lѲupHE6a IS4;;DQMjϻͣY0m/!(eM\B30YA/z T$11͚DD7i\l2p{]' pAF QA^'vKW,_ D//-uUxpvwlhU$0y WyT:vHur!QNMX?KE3{m,Fscz'Zu RH_{s>7BT*ZٽsV>vVX;G+ :Ⱥ^,&2HhuAv#9=5%#-3gLP1{vnȀԔ '}-@QkTTp(w{U/E}3^լemiZIǬ7^2ܬ[ #f4^ R+U<֭3uZV(HvfƁ=;#}QWQa"gŐh5T&y< ~r8y^ &SUYS&etA^,0l>tF`P t@jר!SLxm_*^284GQ^M5 ,˶n~؋/U6eCҵG̛3rڣ/GI*#|+8Nu֌p  =$) jU%϶^HLԩʸ#R6Z˯jeH%pЁ65k׹DU2%sZjݶuf/Y vG8?t*:8ZQ@Z\cg7ܡ_0{z;r())y֪W;wX@yPʥ NޫϮ]RSde-Y3tޫcz_aXl3شEg.yRJUW\TX츹 }dzuԍFÆuPQ+XV@yܶl26iNO7l1{.UjEA7̬BE\]ɧ?_I?<UӹȤԙjͬ:,Eطu/ E9po?M|xS \Ey @(E>),FU5~(߶;yNU #AؼMw5oѦG 2JMySޒYH*~UA)BgרUe0r-ZJTpjٌFVøKR[=ιuM+fE{z&A$0Vqy/ė*n>>w^:O&D*{N^ ^bd\c- -7ΐ~B"WBڠ-Qԇ2!_P*6j+ E;vMp͹@b4PF6pxlvCG'(X.??/08ݷscƿִYIu7lwۚ6}:kν>zx u#eh#+,Z 9 r1_}lR6&smY Z߁EпRx <ϡe}yy%1/ GWkW ݇W~3gL $Bա3G:x cXQxs NZ;xj˦7^١x" wUGfge:lZw?Μ=Ev -)D//o}o7HS%\~ՁywK89ǡ}*e[fs_6En M:}rkjg53Z.=ymׁ_W A+;7d WXPAe NP* FyXFB)h=F8'sBS7~ O??skMbs }Q NQz._M8P|MkxXlzԖ؄=,F4WҹQx@o&bVTB=kV *ȫGhrժ>1z츛7CBJ~*6y1~6Ps}s\wԮY9;|*8Wp} 5ZrQJaWdU;纨9KuP Z)4eϜBcִ0$NJn\"m[jZʵ QbJV9,_g&Y޻cPޞj Ad籸ѕ'|+Gϟ8J@ 9p+ߛ( ^y5Y*t_l}ů늸ӗO_3=" ;Vh=l5a^~slgIH">;qA=<<&vHH#5y6muؕ.۾y3I*cksl=~hroqѳ{_CMg~lg[}||¾cs+0 Ql??^sgNhVѶnߑȯ> 컫"'e7f'^v566)9n7i+>lW_UÃs䯛9i{2MYȵt0>Ǽq3B򩕽kvlILZ|J,V,zOrm3.X>OiZR *C NI`ۧ; cײ#uZᄂOԔ"s˘y+0wDd2tlԾiv_Ux¸;u!皵mڬ~{.RO0 #$)cSa..ȗFMqzO:KpB+Wio>|7nQ4"ﵳcA=(ԪG5<~^0oշYȁZ!(xf@n@vtvx͢1/)ǁӦ>މ6}f_uU.?#*-=nӰs. z=fڮ8ז:ynۦv6cU( j6z5,ʣ;7(Q+T*3r5J9rE_~; Xޛ1ts>[qo`0Ο~`.ЫxؙY miKn);uCnp=/6W민CIY L4.WLfƪAĿ'Ղ 2m0PpKf g ɽਫ4͘AGk6$$o7$޼m~Mxx *iFV!wbڴNvKA_}(jz|7 23==88!\lV۹3]o?9z/w/EWl}֧AfgrŰ^t^8wVٕb72Lss8W=kr]%Tenո68<c+;EQaV ;|muZׯn)n#ϓ>4PlP@A*RDeȋgAD {׬!OW suiӺV$VyJy+S'D\0$oQ;{=ť{Uk)L㄂ rp% lA) +sޅ/m_\|1Ĵs7 c+QJ:Rb^ۑm6V%)1ZH0/OT^8{ٷg'M, 4I~YH븒6ߴ)}|=jcAN!BUcŊ**5z"TU~9V" A')'Uؙbz1˓Ue4e7&KQ%.W*Z'nw]^oTbᡝBKg?Լf߯9 w\e‡ i=q@:mV3皷luܥ{/8RٵGII7nC0uwo<˒*U6qRH|ZxB&od3 aͽbǔ#RqZ֩^149" d ē*y(J^B(]^ԹMLGm[>od2 =EQp ©ӯwnܹl=x2OVlݮbagijN6qLʕ>qૼٸڱӺwiѩetC~Y_ݖXٜ lcvٲ.@YW4E`nj戠𪒃 n"XB{o]ѹ!0*.;za!fJAxi CGFN_:8d {.P ױ#Sc&68/o_L4NwLRf̏u_rm\rpոQc ⵫}/Fc! Iz?? hU4J+P7BFMYo4[w~Yhr(( B#ߏX )I [JۋeB}8@j9돜jf\P1u/@+jJ%\WZ:P}w@tW>21U8B˕+J:oO. jI*+@m O` hJm x2|g wGiٯ\Ra/}uȈAΚ>)=tz1_<|dz yҼm5O)QՋY="m_K[xī(L8ڎ VA ܯ[Bȸ$]+VU;G{S< ~9{_*x@tjܢ? /{DhNzK{ξ_O9̗r6>7{9U?uЉM/wycLx!zyI HG#~?b\>6‚mGǴyP~;_Ͳ akԂ~5ʉcGG#Wh`w[1W,^Wԯ-5Jj/wޘâݺ_JPҥ/vo߯]u7ZlxώqMۢ!,Vh,\G% &(JK;jP׬6cC?!J\Ϛ@Ghw#v¹mU0cΘ1(,Pʱk}570M] G&jKkEhy=F'X}k:5Z<DŽ" ˳90LZ*.|# Od^Bq#9yݻnծn3m<ؑqW %ցtjTU{6LoPQ/{T*Sw[V$`Doe^t0"VW)ڱ!c,9܁`<v_ QGע, 6'^*SZ^"YAȽNp,kVMOrPk,H$4hhrJR~>TC7 vO~9ܤUc;ڠ8.bXW_3sy]z`RRH4nrԩQBSzξglj"cwӲ{)˕^'jSQΟ=[N$'^Lw "U<~ӿ:5cG&`KR앬B5g'mobLܑ#n™̄kb0}kSx-6oU&&~Hh>C_:2i<=mYMsUOxT E,lvdpGI"/juʵ+V?XzʯK}ʓs~+0"ڶemZj}%L شYjۻ[OP݆@A5i`~NALRO]jyLO3uuQ NLVo^i%]a:xi_,m 3}<˾62?796);h [Vbi<+c1 ~3Oww3:{}{wA 5 ^w6FɊ "fUh BZ;|pUl~$aLoƥLvŎ[n_Ad5M&?dIS8rh,Х[fzNvFVumសq:n}o6N~>g.@JY'NTMïg:){ON\k}0v/ߛͨ0њ opqɹ=b"j_~_!=xk!qYK9<VpI~*kWb˪x#ZQ@)-)XyfU]MW1|>=t@oْEC+)kՎJL֨q_{|ǡn[pVJ>Væ?͂XV6"S؞;tl9 zfiȖUüT8}qԥFW:u)+92_ۖwrz]cY cf 6L2M{!jx֑r A1z6ޡamZ=):wQͷ3jFnVSsA t&uW`65 LNnhqނƤ55O v{0ʯX1O^!2 ~sO+b=ox[l1qΠ +x8\7*=2,B`S-9,Vs@mЖA暣 P!((d5 gT HJt~cJ;NwxJ./LlmqlQ77'ӄy< as 8;u*\buo<:s+o+0ଅmE`^/k`LUAt+yȓ6%#=#/^PA v0(a\jcJ^C`3yl"WsBp0JWح((^~.Fek(̈ZCVӼК< 27Dyyɗ BuߍsV$+ O6Wk|S}kiWhPww[^mIR`|+eh]Zc9m}A*zѺ2u.@'@#Xԟl>*m˯)}3.)UJ-)@Kv[KΑh8,zxx@^ "# k;mok ѕ)A8m ;QfYES>6`K, խ0$-X\dY-z{Y)M~56]`|ã['gUyrSWv_W ^zq_̉/֕իjWĿP&AoQM4|yzv*S"NI~fUVO_UptgŐgA XdSI4>c:mS>dڧo0+ q r8?';3٠CJ: :$fddm9Hrg8 !Dkt!'Ղ(Tz;uS6_3.;7oάU+5.oݦOSg=o]Q͛߻cӺS>iW08afh9=^ gLٗ8DuFas?l;]D9h1[3]|A`Amwժ* '}[RLJDQ6c)oNJESwbO0ith_CHyDuh`d$@CA"&ef޾5BM9{?;wq+8 TfNJSտF5r3/hj} gF11kK6sJ#+VӨNA(âoиitZa U+xm޲q$ck7n$:V%z2{@ $ nЂ5}dz* Ps0}qajN6Vd9wI'j0[5 ]؇ g7] o"mvE;#v:Y'%qXmѸ{.&s#u_SEN;D;o^mKkHJ[).&J9[~S^PL+v 7\c1=c/e9*y9 ӂA]63P,O1t[#?7w o$ƽiVe2Zmyc苣 R)t%ߪONgk$$Ti4 6!Ir'`]L>{a=:^aR޾x>,MmL3uWS}lڰգ($G_n GFyc0{9+hJǶoHͣ$JA< 8[{wgvAOƱdb]>dV%qL*E=ERIaWj1r;D4\cfBA/2zZjtV}@N*O_kuMqbm_OB«҆~:P1_d Lbf[%ѴlbhNUoAj xTA Б-TO 0 t3T Bv iQ#e榁49?Uԋ[οv{w U;߈CPFmx賤ZyGT]h|:c_W*gS+^2ϔvmk2Ƕ;k EVm׽g6iܸq  sDz]}x!l{@mG7rPSIm1ݻwزY&11VRm \X0hp-6k\~i̐9-=O e?f1=::&I(1 RY8\|gX^i)V!QডSq$IC#x34 q;mtڡyM7^$Trn4j)t~ =ߧk SRr!^ӥ~ v63+&veԸ"nzew0]={v4&::N`{bNvXy=BAoҸZ:;9, RQ R$8>!:7xUK(O@Gk'e_^pS>r7_Vh߱9!E0[+ڿ|\Cb<-nkkμT ۜzy:IL8oZ4Q,*;}J-=/'L;Cyx{z*0LÃoTı׶C5wu ٨J65j7uJ5!X}n| 3w4tˊ;(JkXy3}jĬװTc0xw>;/n$j\0G}5: !ލlD(;۱oI)Gvr& Ɣ|*~vj|K5n< 5H0[f_Ӂgh Y4D cjܼs߼fᏋ60*z@/[xftUair͢7uU \:m;1[fR:mw"a/nkg_E1C=DZoxGV}[I^~* [ospz%n 3z v*zDl.0?PxY_Ϛ?nmofTtv'$Hn  [4JHŘrjr?- tiePQXR-ًMoZB[͕>\C*3ߘg[sxKaɕݔ'_\R }:22}cƯ:5!vqd4A#[2K ?1'c⏦|KvsSg|}Kq/}5u_=&+Έ{gPEt(b &ݺݹ]kvw! R {A]߾籾3g9s{:fY+URw#[=2R%(a2T^Ь-l*+z`9ѨM[GռUwX)Lx~}i_y‚|y2Ku}kRպdResrr5lD/SXڶJ̓AO3s#uۥe^ˬ>m|NU;8"!Blr JEJ۶6Y Vxgjȭ 1,˹WjЀJ1TdFu1vOɋ2 9Ԥ"cC~/j~QJpgXf1w/u=5#5r8oFI۹cu vu%Y@ʔ ' j6:8TWXu|ªWou]MH1mĔKTg~txqFԶvI7&5,C]dA^h#~.}> / Y3ڧ6hkFC1~ӡI_6) _ &# 6Жe G#Gv̽[-'%n7F N.7~< cfcH-c!#)b0o2WUi9U m'HZsÑC^I6q>5 2VodM̮ EM*rƀJ7qGd;GּհZsqZrΞeqqV6Zq1TGya&1Og jV(0Vrږ:0Q4:uMcM?@]""2yE1&&< aεjvɪuc'L }~ޓACV?q zG׮ޞͻɝv]d4yfpSϞ=:wݢa{ 0fVKӭu닡ț.fBRFȲΰsTVp+0ӫ6 qfHٻi2]FQs)oY-~Ѵcncq{7KJ;g}ܩ.ayJʺ솇`-ì񻯿ή3"[jH: M R٪ȀUO9HBvM:IMF 'I:7kڲ\U\9mŒfSaLfvNgMYyuVٻy!뿙{%³k^fOΫ\OM&ܻ:bo&~\I;ZHZQCl\^*r:ň;FaT@sS5ĶJڲD|'XIyPVB҆7i5XDVۿ$~TN)̷ ʜQ+ly&4PɎ~oMb2fy Ktve?[iRcεy\F=(LuRkcKIА(̼Ys6MtSD^oR}Y2'7J`W/GWȺj|rɫS2[9 &Jn~VJn`kmc49,T!˾[ #[Vۢ1f.{lUJ@WKq.镌b{YõW٘wV0*#TmTkR?1e:0kjD1n/=R^3EY- F[A}paݦKV#ISPƮvc3勄l`͏rkÔ^f6iҼe֍w/Ssӥeԙ{עZ̷_A$j ʿcYaϰ l|ף;:?쪏XRF(]$F*G9d>,+|̬0O I@M 4Fiot1w,pX 4vU. :cIAgAOW(dT"hG!W'H`Owo1eCf:!6bwiڨk0^ Ԇȿ?Rɓ޸vy}E֍6|ܹ3'o߼v Ln->$lqK.գc@/]m\s{=$iWJ1EٍGLj qL { KϮ<I0T,pM5rGxs pQ & (֩6ϊ6F`G&}U &Pd'i5KQ*{[bܮ770BZ(?|AXp٦Z˹O_>{d:朤맷/~:`b4iyVL ˘- /^rvܺē" 뵅"6ՂխSMl9'!TYٵ ԮiM!)6;HK`qI•ŋ~5;vlqBn y\Rl,i. a?#2 e-(G,,rq, Hg`Ax1#v^5 a7Y%=|xndؓА 矒:4$0${w,"%d*ޱͫW\f՚k=JVj3{V^fM{b J摣 ”};\eoxqo׬\b+;RfZ0(K&@[映6[bcw\9q%GΓ^* ιN ֚& 0 |/W醶[U d &aQCY$3hS%.-[xvn{eO`Dػ AluMnr+C0p[i1bܩ!?ZXX*9YP̰Z TYZE,a&DOc~XOBͧߪѲ=m(Ɓwmk`wo Cvj >oZ mZpw'GE+CQgȄĻBwN;o@>!]Fʪns_V^ٯm;:IQ;6(}"8 (}i{e΁ eU>Øzǐ&J9x qKxȜ;ƛHJP@<Im~=l#=F0wA޳&Yh;`_^=IkeTr' Z}ˌJ R"Z-AFGܷɯqw.$1%J^ oE<*h4K%*GdUkTj(a}[Rf%5: tjR]F!zi6{B.+_ fkqͮ(])Yffrj(RzZG7y7CKaIiG1$IҲ^F/oTyw"sHGYfKy'64IuYsRzxQ]ɀ5aբ'cKyϜˮի&ǘmz4 NM i6=}k  !7ZVR0hR-|ST t)QBfr}Qs9#o ]b4|'w&mYv#f*h`D"3^<SWvȮN.[jjC{./q^^O.7aӬ5df}Yk0j=[N4W 6IP&P(f|[j'=+%pS^zb3GL_V}-ݺoxj&sYpo;uLo'\X_"qI޶qOeN \/F͟?W)y!M~]L&B/Y0y`;Z_oFԻwJ!TjMyt߬7)""b-(L̷%EE1.""y`߫7B(I6DzAxT1Eu,{~=\ݫmIu' q-e)EH".orM'ym٦HiԅqL :A+gn04>}3snM/m}m¤kF1/8;+T݀K|mnOtuz#=UE QH GFފ' ]kxzC՝ :`KJg]zi3#C&A^MPUD M׊PREaA%jbe@ts31PO ~VZY5'._~k>x蓊^`+v~7]ѾxR;g?}`y]Rf9${a.V8~wϟ97yĆyKi{N')\2MLUXJcې}wB,(rmD}=G[f2m@\(G2.<+V%G9qU}m^rmgIAr,Kyo'f|uQETb%OVع9؁FYCSss1LVY*~rXse; $DFqW$h0a3xaԵV3OӋK9\<7Ut{F χgЉCjXG&U|ZK1iYG %.19˸)1B ib Z1L4i=(F%-<#䶟rf-mQ & Αz^sTpǵ0+QRuf0J(n; QC\Xf8¬rWQ"ŤL)燹kkN}!lr pYP"nXXP>jIڨӓ&)dkxyڠ3p9Am$ $+d(`6QD:gԩ")喝 xl$fpl$29 F%BiS1g&!X1~X}i;ǫUDKm!UT J s IH!:pV9l2,X=CHrxfC | J9 F3!Q*>lQf*!òQk!P+$ L&0A\nGT7M&a R4*\8Q  ,SxC*rbPD_EDD? 6$&}Kzd *Pǰ   ǒ:KvwReN'20+"""9Xl \nJ~aX \]X:Uo2l_{{iG4C?EDDD4eQе;#fT^ٯg]G,V*ccZPDDD䟎)ܾߜSf'i13RHĵz"""!AA˲@Ɉ-eDDDDD-("""""""E_""""""""EԂ""""""""^~~PCDD׏?g PT&rPD/L堈? f";EDDDD{ʈ{?c^DDDD俆EDYXT lccÉrPDDDDGԂ"",=!;""""""!ȿq /"jA/"jA/"jA/"jA/yehaDDDDDDDDWkΠ9 MFC˟%d.?F rYbM6*ED@rRHeREE=(""""whA8u^:L=͹dLA KI c@)(KR6(Ux,J8$I L&f#ŀBRꐈȿT DzzeX3DDDDDDr -ȳ->eWE}%EaG8B!5wpP\!_ ʲmǃ14W5 m|(E==+ 4E&X+f򼃃# C%%%g""""""5~1Ϙ2Ec3C^[,*֓R!o] {Y"ūsܵsswTEQWz/&^H+urr/ChX:{߈O*kZQݠ?h~޾6N- ٫CI煭LJ֢;Bca8"2/.9Vz嘴h.uksC>c;( qLE-CIqEDDDDn9sݹC= Uo)9"T_o jCvkUֻ}d#h^+4TkĜ"[պ}7|u[}DH(SYCw>.(DbߠȿWKQT!"""" Ѩ]ݪemw[k z@5=Kzvwߞ;{G';ۣ];_G6HG1{hݾEMqpZa3G]=#ݷ.աCF7M5(#"-hQ"{kGx,me0c(K2#IeyFpL$-l~ˤ6hvʀqBB 4+X)f`<#drGE!(/mU6 Ű"""""""""o"@{ȿQ {ȿQ {ȿQ {ȿQ {sX#.}<4vH,C3,ͳ ð*O8hAAp P /?t%$Z~_P,W9I,b P~XfY!~aV7A29  !R ZT#D={0 \qFͣ8vTEJN׳G9O$kGp mk)qbB>,R`A`G k֨M&E7~;EpIr'SR "x/Xfv˴,'R Wi$z$<jU@N^ T  UZO3 ߈J PK/XlQ\ʣGl&SII;DBJ_R#B':uTg @ YoFg߫++)t?WB9s9/4y$Z]se$ c68ɍ& Q[TB^SX<2 Z^>2H޶C>m:ݻAv5]?M ,KKJ̿%hK??F WUCk_ܽ |;5"5S!`Im9/H~FN6-["ɒRy&:7 7LGpIz~hK5?.p~@7~Thd$CS{zy _^_0-( 56#D-("?0w7:6Jy;7tXK*ה6:MiH(sS4_|xAhZY6,?ujJ|)5YBդѠSgFkiɱ2O3 =||7,qT>:,+trQDLhTIsY#b#b]vaQ7ݹpIpTFT~<%8Jb43@['xh-MYƨ~EE|c6ԅ:ǀV- * zE:l&R@I)DRFk Miۥ%eLFu:*lFեcS]?dLah>FA"ȏw,#,e&[K[u潩f@A4Zg]Ҁ6[V2+,ҕ酁O!H I!<_f:@A+i}MPJFI+͂ &fM&F{1*Nv^hF57k&A)` ȲǕUĔLREXk` Qۚ^a<_hjTA$g}AO1<$~M)JA96oXxK,B3S,h*RU79J!ih}ƚ@IS!8!Ā:3 $*kNf0d bʚAB)Dcuwз%.x2ARh5-NXHfKd(=H ^2Zp1@jf=b `He/uhm4ZH_)5џn?n<"ї]6UєYo2.I7v}K9A겶,Yo͞YdUWfdOH (iRW%mQ /(Ph)Lfez.gk*{)ƣ1"] ~"N$uzu>F_h³&uÐ)j{(ReO#ohzi߾X'O_KY eLL }y.5"TzVjg?I}CEP9XrF>׮EV.?vtK엏jjE5'oE}uBuÛvi}HZ:hReY@i m_yԛ.d/bL¿ս.͖^>bňPͬnGG$E6.ERaL%E%<[cBß=y}aJ~'MiY% | d'H>EUVCR>FȃNmGLB9H xxnUr9Qe|ofFiO54fW'9d|]9ƭdUӪz}<H*{NRiSg?7Ρz 玆F^Р߾{EѴĻ1=<h 0eGӞ?^h .kIOSob| aE9lRcp]qˌM'G˝Vƺyef.PΌxtQB4O2j|SkEQD7$n^UrFpMAśpV-}7#ƒ?L.K+Ztfի݈rdO6h1 |IBZR~ujEw߄m_r3>q|fỞ8i( Qo)%znAf g~ݽg1a_>X2YSL:I|Ԙ_Ab 60)!7R/Vs$hUgC"y7i?%cĨb)1&:gY޽^}'{_wdvmsuVAfG]wwNjKϷ-^nЧ3<͹ηwo!g ȏe LI!a&2oxa7U/[{{+.嶍V%0l!JWB O]g޹GIakֱuLjj]wd|ײ}.> φpntfcKqpŃgmyw6c,ݼK>w~L&KeڶwONմƕ>]js`8dkc:k+~ ʨ"ʮͲvQ 8b9ҶW[֩Sti{ݮUvλL;3 /g3ీ{hէEVٗτenqK6k׌{)*GWo3ZS[Y%@Rs'v.>;j|~boƲ٩\ͳ>;tfV{Ð 갻OZ}g-{ YX2zn%W:4k?_ݯm>xEN_DԌm.Fi6٬/dhL6_UZ;Ժj(СE'm6hiO.^FM)HbYKpΒM[vieQך1]7>,$}ב8콨RpZ& Fcy-ڙ I(]i_3hq(*}D'>:Y?Ԟ7e:CߘhZw PeZVS|yE G?ʘFVOֹen/kܬeWmnϏZ@ swTDDπ)3/qGVJ\>x7w'ׄ1eec>œ6Զ_v8B)e!!@U'EO|g">9 ^"Q4{{w`8Di_LH#rBOuf$,rYwiGE`9Hc^B2dhL)޼֞z:E:uqɟcUTm17]C$Yg<]kJ Y+%h׾g+; _`))ټ{֕ (ڥhD IT B Nrd~ ߖ8*Ue.k )ڴDRPJ =q)K޼y|s}@1s;|`{NCվӢ=wkť~7]áV 0O~/S ?UkۼyQޡ')QߧE5"6C5N]rL;+eh/소kBtۻo= W͞Z5C 9x[p1喀 !]ϲ0sVqSHpM-?"kEi{S%$z\W9a GbM՛4!B&$a,a[LYkv?3UJW_l'R0q'M6W~qB9{w_/zLJG}D9{,T :LJ'Wؘ$IU=%X0A4la /13ou{f?xlXׯSgN=)M~q=կf? ݫzIA4-s >\cMFgsVDD?ƤP潸{l݉kT?[b8'UZew2ߐ+z*R^_XΉWxJRhWGMH4H!a1[Y\@BRP0A lT^-xiT`U%G?V`o&q wGWrx$rsAb$Bg< )w 4 N#$0 +AQkK`N=^~&DʹՕKd'; }@Ypy`r'[|XRg_Ce c,ReUⵍ޹BElJ.Z&dgSl 0BjRau2\Qy G}VHOG -/O967n剾yr?2)yDXԳTI(r!D  t-} 5RAU(bOdٙ9z5uc>*C~G(Чlm푔p*_{ &"،@iKGvJ[۩ܕ)  [0f< cxskA޽5V(;WIEWW1C( 0bAJՖ㯙 \7&r^ :0 Ҍ(+<~ @, ;G= yrHarV%8A 9 9?i{BBCŨ5,ݤ)f(]ǎsTkl'܎w &HV(Ll nXDLQ0.RCV!ۖTBA0;4l6?$k60rH$7ŴL" !Q|:"fle_8%[؂->Ym[` ?+y^7Q A x6׳k:b%(!R #&.M~=*90G%hh z CЦ*4 ?'ş|+ЂڴpuϚl64M|b3@90C3VcgV 6aNUXF(e%}&S#ե;t.cеKٷKt>~8 Su&%J*r+>R#QRr(k+$Dz[~|'eRLj'g60š6Om%Lu^Gi(aE$l{Vܵ_9f+xP, F  R#RYrޮ(jQ ʐhE@V]tni1r ͘j ' otgPpZrC.+uإ~Fgs)].oH(*|&W! )RïW/}~^7k_2%U,ʒ[CwH+-.0S*IRIzg:LZ:[C/yW!ȶ[ި`!%laϭ\rJG6"@CGNLvK"UnH-E*Tަ mт M׮[OAZZ vpZ®o0 $ܘCYJm%Ѭ2ɻBۏ;N][4N%+ .$e8idփy@}(*%+MfLjң݂#r"7@o6ލT2`ܯ@ieɌS $`2ϘoOA cT-SEԗ ]]}@TT `k{^Gm= 1 jX#'d*K38 c*?-i?h</!szT;ј[\ܭF*6wx7P zL@kmT,%Y()h5?0!7nxH[F_xuuYf xW|oVD [8!rB*1KJ n׹ә&CǯU!.@y驊zT56' gUIM_Zѓ٥?qLZH6ىs7O4g'iXCydS/LK8+|xwS?'!ǵ PZDBhBuۼ%C%:ZHHlʩ}esPJ!&`wzQByR!čjNȉEB4A5g8%|B~`s8hN,Hn_FnO NHE {q8fҶ)NԨ ~Hf GvNKV[RܗGWٹkF_S7 6\' ǎpd . aɛV@Vao Z*U5geF@SGq6L~b[S9 Fm9K*i)hi퓖;Y7^V[\}I[+@ m~1Fof۵\Z޸!z^,4Yk6ϱjLg;F#K,ԃ`HiP5`!-\Y ñfZ A[fՀ`*6 F$ͳ4p܀8T-7(WogD_~#/]MRi!w<|}BSDX*P1>U H.τKb4{2Tic&< 2 %y)Y@jYjW H)T·;NFPDn?0 0g^ޝ[ɫ9E&@Q8eZ 4w7/Z\;$v!Vܳa11Cfw]{!%6.HN0A?>HTWjET&oj:)8s m<$4JgD|H!%EoZ-|-68wY s3coleO*_G <"!wx:md{ց/?޿Ӕ2_ʵ .#/P\q}Y,01TZ JW]eؐ[[T/γ@V2{&sæ|w=0,Qf\ JN.Fam_l#MT﮳1a!1f7iAZw!G'?> {|n|rv*jϨszprcRm&+6"S7N7ty ̉^|"1,U46dKb.'|TN\Ukؤ}w./Di_#A-p1<3ҖAlsAfRjTQ-tIIX{58$eMfaؠSg52ӯmE?եkns#$O@oW:p52<$>tPx\ G/Z:hdvyRcG?,{:όtT i ^J! "EWt憰wFli0(+~aF0LjWrlxQw:m18RSw#Nn- p՜y<_|dVQ̐|W ׿ձXJ+/rpC)98͐G,i} 7 IѝSZ@L똋6$4:E.6jۂ#&n6QpXӽC^~Q.- 9*]Ky3V&B?88[k%D?=~#͋xD )nj`{u_|" }qJbJ]\fRBڀaXQQދx:NؐӍC]Vag90KD}4'?{ ]78{筸gL8@~(\emusJq!Z$%<_+ֿ ;rI(uo]-f{T.aD!1ؙ>ۇvQdaF zbQּClk:Ǐ5;+C XeZ~'0 Livrv5w;98hp@YGZ>+iYcƔ>iqJ&i߶ svD)D:+ԈmE?=nՑgC7sx5?c޲=:4y23pp7Yya9F d<{>;'_+G{`,Q7hP'oBRn9RiNMX_yM)L:NvTV3'ƆCrlIʅkrK̟MED~iժU|wIg 9\Z~bHDR2VNYE[3H [FRT@ NjBy.-")Rg0 76*FFS ZI_FYД޲I/(p[Z/@c`B`'3z )tgˆA8O%6*'_UJviIL"I+\!ujCe&m|Ƶۗ;5d˷%0KB# 8Tf+TĐHmAKU_V*H@D6ĘA[9]˘֝=OfI!B3ac0m O~斅NzpRz"24 sQ!SQG2hLcқ)^fB(#S߽}7($7 |' ;,UZQW&чX * ZRC Br/C2 }BnYY:5# a91hI .8lyW*ULkT4I@0)! 3X"p Z3H ^c X* HZ  hdσNva2%oppOF+L6h@ @ 荖B3$9Iyiƨ՗mTJ@h@jLƬwxʠ7X|(>)AjoȂMZ#)PUB:p)0/R (AcOzbWl\l2;tعˁ}uZ-|rէŞqLK@xN9~ò-J,?1Cfܔ T ^G} xI{U.m-;eֵ CwݶdtDA_T@щQ~Zc@ӡ}qI1ߩkV[H`FTu윕i+fZ{91 Qm06LOLNm` Y6)21qm>+{jeP7̈́Ľ>jB5} zM_27}|֌LۘN^^Bgbb'CN^δS3T:*,5wߗ Nzw-FΛn(sI񅇳.Eh>ݼG)bί>H""[0J'%( .kkA蕯E!(hA*U7i`t̉#\:OH$LS*m6oUcY0(YaV9<˩5<@J(eDTѹUc5CkƏѩSX "ڒp$R\2C=]+_^$*D¢,]62`UG&@v!_Mn2dARkʄ2SICz@ӆBM* oرg٭8-m`(SjDDQ, {PjZp\Dg CA3 B hZn r2pgYVNX/ #~jmփfMnÖ[&aK[ HC2iMzH1wANBqdgye @4Ѥ[)7T͞5iwe(PiX  /~iY_$x a)Ap' 9ʹ 5jC6@lE!(OAV%Nb3Y-0Lj֮}@IRIHA_N9ow;_&M[Tj7?`A*^kt* -z ^,.<02$&.sb&F\Je? T"W@Z'ր,ԭW?uIKJʹ7Oã ߯}c7_LMwY|'GK=h7E:s^^x.6gxAx!kVo8nB-%jw1dȫ[vfv޴aIb1:W%ӿ\vc}ۺ7"օ=V);'?ɦ۰Ky üz},zZM6ۿq Ԫ{_Hy^j]LƄmFQ|6"PP%0 0% LhZ 7{prZn!ag Z" JD,J-82,*xG'o|_7nmV @ vG^Iی)3%m2zt{Fs>~ Ag8 _=k(Î>JmVG#$4Qg aDǒz"MIR4`5y&Q7Q, ^d Yf@?+s@[𖞞[V(Nga/‰b2yG5H]h%OzZDDo&MG/?`64L&~D`x}MƁ,mF]0] Ԥa /oAUK* 1i|& I}X;Ѕ֒*XwOR}![c{6p}Gol-h7~δu>ΙMǍo&cHN3W0+Z2=Aum@O|`985tq]}2u/fuWL&:R{ȠgwL5P S8tq'yPXDH4˘yIYGR8)|=g&,(( zXa.m AW$_.3Y~uO }|<]8~81IDAT5n^&(}׸‰'h9 [o$iP91&2LMÇTƊc+J,N*n^UZP[G~l[F-EOp@k#4xH 3~kα6Pԫ@)&3Y|n0^L]&MF"yZYOZTUFXZxI07X:-~/w9 saٺ#tB p~5x 7Gs+X4gi3WA&Lsǂ_s1vLX簃,&iz '_6-}z;*SWԳcq r?;H({(un bON]7Z@كb(|`sy~/Xbي&1*p/"Z& W3-q)_g2[!E4gӥw-]uW/xȶRz H>Iݕ{t 'jp]KnX_gAٺtw_;WBr۪ raQW>Z5w ȻJ5xx7WVrAWW`ƮR~ "![ʦRX_i,m <x` o4@? 4*UԬdrP8Ҍ7 NZJB!iMcyt0k1b?6FD}a*{[yW{]0ؤ/st;i2($F̩ߔyúve:ګ;YI#4bWF: ԯTXFcIwdVQ/ 8 34>tƐt\{|)1}ܘʊ%?;Oܥw)A1ZU؋D ׺:+ɩm!}x륟 :5zþmVrjXxlZ{Ʀa=:t%e8u\jN6^>rzOԫk{U1b)P3v>e-7ʵ\B# c&իoۺ,ɺ7:=Yo "kt5x@J" kn߮)$ApZ~Ϸ_GkScti`c[Iw!Qja3'u2q,#-`'evFQe_ǿCU˜`n5|JU`fM{ 2kۉPJMعCe׽૑)k7'g69ӘxQ8qox^rP ih+#V)kU/|">d{H|rEuۏ8_G+Ľ-: N4kb[1Oyo#t濑^DDD6xk4yi@Bun*{NvbϬ-֗5wgϸu0u뗖9hn޾WҦ{#I C5*x;*}ZH4HfGKgtתXau{)NXyĜkfN`Met3Kz%Ii4}Wے3^agl1c5=nXhqF_sk9>]HiTjػc'$kШ: zbCg4ܩY!v+fP\|YuITQzj_H0:tQKZl,HYMR*?rqOwIW2}^^èa*ռ_V_ޥ״oR4!yno<ߊ}ZCٛz9^zY[}lR=(&aB P9w~_'3Őz_#z}m:vy}]rdѡI)M bՁGMnizbyGuAmkah؁ ӿ';yrݜXPvpz`ݍ\w$͔+Fx*'qRn!)% /n _ܼv"ROqdb5׮gNoj}=vzzҤUk5^tȫY; n{7e.oӲ='I2N.]Țx";LZU.~7[rlۊ]z7/Ws40EHԋ<89z]ũ L4Cd$D9ʾy}!p tFhKk{ʘ_d(R.z8bպ2xԖ4<`^wTI׶DIY~y2ĤJ-c^S+7u"$Ip]T*qZpQgSZ)ؾvى,|@8 ̨I3SnO"eL%: #A[S]j… `4KKKto jTf~ vSv=w]fe_Di`L2ȴGI"OStE]Fm>G)/ڿngZPDb9}@?[ͳnWLRs6R;˗mʨQXCFu4,?'9V6e[r}o/J>>v eLf㵹qo !VW<{7QRg GόɐfȞ8!NʎJ7}r~# nni~e)8mjd2LJxޚ7gihRcN_z#W7~35f.@ ]4骮c^~{DRٺh̎?ʭ5/">du9stqGL&[w:ECP\s Iއ7ypM(t Ҙ5nſ~gDy%!nO7NzL}!DS߯P;)||{_r^Gi Udxβ!l]kLoxtjϚm$9,?K-R i4ަ&ܥzxjβ/(0x6A.+~ۥdGndyԟsGol<<%hFwO4ЃW?T!IN=%ijР~rg`ŝg^dBWhѺ]S{WjѺO:*ڕ̤t }ݤ䨷f7l~[`BdeomteݗKإS;=wo;Iɑt .)e]WGD?x*9RJ]{"c4ɋe-=tz=6ņ= zy{ .69.>5uz&|:&=04䒑Jf =goԌ*3ҎӂB#N-B~*8R^jjf%(0UiH%)oE.|mCu\#Tܺ8X?Tj;:&ޥcn^_U1Fɰc@ BT߹//[|w7;@P˔֨yqWw7mZfH^_ZPD0qp>Um@]##aKSSæ2dtXfٚ Wrr6ӛ HfGH$rOI@`aʳDf.30֘QyW$$fXBn4d`շSH8IP -(?v eI"3Q[%!ˀ #%' KjuW)gl$m3cD"!0@GYHa-w-?K/l(%=`)vN^2Sm&+ 0 XV"dITt!FyOPXI0 =T*hg$@]j&SVM;Ο%K?@Y"Zf eT"Q?}}=U?ȑ_aus:d)!9 o9]n#l;;lcA, ^Zv1QͶL*8AXۦ0 1k SWUɥ=[ -k$ZPs^*LK+ridXCWMSLZyJ7ԛH<IHwJ*#%zظhnޮ U5K4q՛ϲlF6irsoZ*`W+]lu6vrMŌBZپ7i?l]е_t>xѩo^k' tFi@ZG1{65?GR5\>#iaWGgÒCkf~mL! :uСUMdQp;HBmGl)]VRP\>`;m,wd1}&S5&q׽ggWM=mnTfv&ٻ*+-WnWmMFf=ei>'he}R`ۤG?\FDSڒ~Amq/+1T񫱮O3>asWi'HV[fw9y7:Îwl%QS }ޓ0h[uֶ;jk)~wJݲk0f|E|Fm8g`3Tuܖ9qw];N~>#ןqie6y꘢Kw|c3اݻ@ª972$y*a)*[9hH!'Dg)x~H zK#L84 LeWB͚Jfm̚8ak0n e]5pV|06ۄ$<J Y!T)PZPt~3PE>] _`4q;6} ?B1&E*~|Ww̳ӥTDp\ G; 0,P዁<_j$d os5H- F =SNӻ?y/ )WpU 0V> TpnϴIa԰of`ḽ;ONؕ& 2Z䣇E_2iSJf w<*q7rXS?ogB2D~'aH`hXl;8] ,%R01bx. >cᶝ64pm̫8-lr9Jн#Z׽dQ P4iJbM8~$R=  맏UKg+򣦕g> S8;X^5\iQ49tpgQ_ؾzdk|w0{*w%2)6:&ۣ:cyZgsf/wi dgN2@a +b@,OJ/qF P} R7G#>6cH1044'>~2{ިiNjBr*מ=ѕi ^ _xF`Tw5[azf]1-s\2Ű,W!2u2aXo鼖pVXcR͉*3">Z7c?^|-=`$s¶}:N7$rf~Pة 3`RDʲ BH"ڌZu {Q\w']jds؛Xa/)4IȯYU9X1 `X6&^i/#5`ӎ0b³'ZO_j<к4 :z uj=ۧwuD=ɭQ K[(XgZi](H":X\mZKQ! ;Dj0*iCm_|%_U0DS`jƦ9\UW37l+\~c52upꜺOZp嚕kg7GxTnc={&䶶J Aw/ ]lC7[=M* U-2)quDy\8`k,[/]͍,(V6&ZstE@|.8ʥ+V/m5M=qӢq΋ħ0#BVry5<nȦ8lSR{ԩ;ݸn頓{Vt1pBewT1D2 Z$^o4Fcu1 hWSSz>#K(Fd'~n>c7_m/ FaI(| ACQvR@RSr͝; 9j 8VWq:pE_lY7ذsX6rWq)Lɺ判}KW-]aEhvnY}Ʒ{{--Ysר/:)dr>ku[lu[W.s/}ʂu$>tua4?ⲓG_bCfX&罖R՝da[Q;Bܽx#*#&+3[ d'T6v /JRۇZ*ncg#%`棖mݲz[8|?g;К`n"%$W}?d6f~32fa/8NDD/D]g APFm@:gɄӽM QFCv#ˬMH=@*M}Ж)J)կfd !t-F99ZzWŏdǘd<0c^z)z-R K]P2 )gkUə \5ZI 9 :=9uiYY\3+ NRj+Ej̜]J7j[S6ց|[ rFnvL[5{̆-Mz&d8UjTM6]KqO.ĦqrR(Z`iij4*AK۵y P.$˖bb]r[ BBw 9}}yI9PQ3&΄8wsxֵHsOspc@9o4w>QRkL[l;+>m^nPy2@8PwWk$e:/uIH^-O"|m6N+?3g緛8{d0R.No)^Ngp!3'*mbȁH'H1gD\WݗP ڮw>eJ݄W6ͼ̦tXHנN߽Z_ P'8&p%=OwJTu:ʿA H<7KW1tRcE؇"ۅ@pPw_(0!@UADGAcq;⿉slr舼\=;^z־K~k5.W 9ˮ\  h^BOgwfenř9q>h9rfO5ù3DNqhjA$kZWH"Musj7YJxp-K ;;oϋWۮ{. M7svD*0osb&!yTօOB1՚C+oT޶9 c[ @AS.oIIuwm|+xP:22C>PR`qO$эǼF&8#0BA#_AAݸB s @ ˬ~Ne!3gy2 U% *FV}xJ%˹0@ ($yPP(9mqs e)yQ|n w 䁟OwتD0y /IΡ U v Ys);6|edB @  P,[f*ݺ~͐HNLmjֺ}&c:j#9Hۭ4+`{KYK@e%μAH&Ɉ,` R/i1A_.WHWs˯cS.kEAhH;Y@ /jA/mO">{{=aReoԴy$%&k=| zkV;*6F. $2Gv{P=|)/vs;UuqMͻTa<us/~㉴oH8$Tvvu$Gm}C\j\x4}^y.kxj3&26^ *^Rͫ%r)pv _|% RKtUS\+#l4]< -Sx^^T˖-#D@b*N-SNCI@тoGbe*+_l|ڶ<)=r\|y{9\TyLMkB  pWPR!BkK*!L%ʖȫWUYbbWB*/ J5,x>As)WB٢yIdO*T,[\`-(Pkkd:?lLo%'wCG ;^~KF*!b'uxNߕ Eg#L%<V/V[K04\#}|jLN^ [] Oqќyr i}@hV<@trA*g2BFR㳎B .}9 U61?6>#- $+@(A8^w@q2J>x,ѣ78ݖʤIˆZBKqxY ebd7-VJ^C, Ȼ my*OZnleˏٸ~R$m3k-ǵk߲t~rLSjF]? X!BXQz a됶V#טdf.*Wi5 &[thY1djˎXifRhڹj:}Gz ]rOVѷg5 ^>BX$ZL>)Gřvvyɫl^WL\b7\ZHՒ=hɡ-7m[~aM.^:vj-X8"&P ~&=|U~Dz Zk޹=qno00~h+F{}kG<۩Loܛf.,(jMbJxSۘ킌lK*R琧^)12%1yQL B$:1}K,qYYwW̗._Ya6tz~?YjiBDHriqj .J zdBBԢ%,f`pX 6ԫ;@`|cW l+X4Mw_U.ozOmPNϝ: dϦ5=iSΘ8d؞"A>Loa9M&U޺>ƭCw:;OY3&0<Ӷw+?6sΒB[v.gͭ˽Z9hLYЁehi#fL[ͤJ-VfwuO۔ (dif3'8#:ۀ bclf mpΤ\19R_h>39LfK+Kt⋾Θ`fA'sFˣ@h3Dn?9a؉?.Ӣ}WAQ O0wKnH{C>TȃlXRB% stJ.iݪ" gba|ty6P Rt;ؐl18\Io0#brYḪ]i"&Fgsvܞ~2i^RP 87GmX8Z@`w6g3.JFE.*ŠH""3~[V10l$K ,iN6֕)c NDR*az#os`KXܒKV$|l(oS,vg໰Ȉ9̊Kc-WwΖ[x$ڜLXYEp{)\akkNskNF9n$Iq$Ɣ];`7 .']R󵆖@ Mt^*C5Qp_l1-4\ǺMqKMR-Zшx w@Ƈ{l'1) 04MvYs6PgĹU$Ђ`- Γ3EoK3! OjtZ APh C5Rd1vCҚrVJ- V,Ċ+fk[ei6~a38N;.ژHE+ܫ>Qrٚ6ܒrTu[5=2G<6QWasM2\hZ*Y??T(8>l~"D]cA-V*SOT 0`(,x JAIc|*6ےbhC@^pڪ 5rRy;Ղ$I<|u"r o`›7oرu-V-!z Tj+Jm׾G2.ir#:+03Q@'9HK:rރJo4oШN&.|!V%: &NݰקNFuz콙JA p_r Ai\tn!ui*p Vlq9@(3˵S X9{\qJ5 ~qFOCLorc;fN",IU?zhZ׬\U2+ Aqt*2=-q8AMye׫Qm/2u s1 !jQ-jV-3ofUׄ0QQMyJ *3zJU+xs՜ĄNQF6K60ƾ%V:K:ݖW&b{CL?V_;lHA ]e؂דw`H< 3{:mer o1*fu^I^bxt *E$>h4'˲ҩHPßv7{ouNu9:︾@O?/6|2rvƃ9{"l}W^nxw]&)IK=}g-nX-xc\D kϹc63Dr<LO;Żq(Jbmvf Ղ@έؘr;05=mU|8b<~ ghUqi}G|WG8(d8&& @0ˣ H7 JM$II ii̜4~kw^KԋrNQ'쇥W$yI' B5=sϨ,d4d}C$?CS /?O`m=8)n'SG.]mė6'a92!sC6%V06;])֞.B+@d1Sg 2}uL>ITσo ܷ{ݪ)YP~FhL#_4ZÅ@RT)0RniTA-N7.8&]H qϽL?J*ң2 Zźۗ]$r%0B'<ڠWč[O)usO^,YcPYODBDI[%:W;y{V/LZݡy^fOT nY1 ̅""-kѨ{ɠ(kB|lJ\:SiY՛خ'^yγdٌF)SiݰoOQ5 *\mH%/PsY Rn+'o< WA|i>q#hpŘm'A %<0lY d ko)&:V\04!jW`99uR~zjBa?ŀk˗rtGPpĿP! |)O(g:,1ބIiqyVd O +_ x<9͝h<`OuQRҥW#%^=-/9v9%+$/t AYٕF{'퍏|޼o/W+T*0@Vyx3&PZG]X:=ѯ@IdjDn>K0iDŽYRG!-Fy [PgD,Qg97֭[-~:D\G^*u`P$"G]I/$b T@.2v2E=~@' ,U1Qr;WɢEv⃏C%C|bvف Erd@Jw򕋌) д_,׼ Pe}Wpl -`қ٬y<N47h\9TQsA) 2j8yYO!^'d^ڠF:U*֮YKQt DZ {7;" %5)fD: \)G7onyFlZ A ?HYykhh!;{q0D嗏MlN"TorF:wrC݂@ոAT@KJj3}iʉ/ Zkf9 M)V@߶z+]W3-) jZG^?zW*}ŰsO1uOB(TBL>e'N{j%{ 6͘1yI|nw:Ђ$l˧O<}v>vE ފy3&^*ؼ]BRRcN/1eI7\^EYւ ŻD az{³5s'Ϙ:y=Fp훂aJaiS&o<҃:<߻xkXm-[sJOsM:m#=uzSl~^{ɩӢ,YQ'̚>⍗̬ |l ׉{ B!/6_lj>Ů{ CFTEwom$' R~ZP;E[l[8!]G  &IR7\amIvioSER UI "M'n,]=oM 7z(_N=e >F}<۹o} iV[xĈU{ U7Fc(9?<r88MNCӡ\j7ǚL/U&/HdЮS~ߔIKUj2mq}UyB󋂤80@'N6vcJHȫ1 ߼-ijJ&Xm~!-YBhs4qma$ HRGǔ3Oq١+?5oYpoJvȂvs#Ù#!ԯƘyq7.b;xdsHTc[]ebe{ƂF)4 IvEfHAِ 4j-!{yӼ@YvfP*XQ3Z$FY$dg\6&-Vy7@9VTr #)cd<ϐ}6[0iA*ʩPJwF(!(Rk:phuAPji(zW-k5*RRkt]p.7k 4j|J z}8Ҩf!@.p掀{n 'M%#Vp/pgµ A1w,RQ tAn~sX#?DD @1bȰW@XJLxTN":4jk2ړq%޾M-oT&sMi3X&ⷠJ|sژozh%hW,F#R 'Yjm.qf47i|R{-İ6KG=*臜,䀤K$o}·?YӨsQ;bg |5W^D"3lAC>7G=Љ ;5Zmi.fKgJ 91!$K? KA~_Q@QZyZIF5'_`w !pu-ȑ+ ][ݟ!pNZT]'NMZMvTITZyT*QD٪pN/O$AJ yfP`i<=o3[wD>K0 +j5%GC!+XmA[iwD[J4@IbC $ ZZ@ $P B YOՂr>lŇ@ ' jA$kIZХzA|4LFZZ -HQ p2@ #@YAQT>ZR| @ K'P B YOՂ|p}A@ !@^@ zZ@ jA@ !@^@ zZ@ jA@ Lӂ( 2 ߟae)ZAj@ e-( դ!8xБ]VԢ* Id }6Asy#_˲B V|9 SF%_b= kYD2!@Bu=$xO|հ篭aeh 1LXQ$sdy>,YD =hai'pL6㰳Jc }C˔de/$ +8vEvc"K,v!%;d>Im`Pjވ3pbn9Y 1֌h9k{W.'ĈWb97° fs vr(A 8AQO!^|YPW6;֣xڞtfWMzvlTs{1E`N}+%D 9^4ugtZ:6-1͍ƘY5鑿u~tR8Ӷ7:.W"v[D)>ڕ??3`E޲ bNz\(p *%"'ۡSlRXNYz 7{TauZUlҰ<8̃wKm>dlP)oXTBz;Fߴ(vd~PeBs+nJ;hP{W9hdhSs\ihb͙գ]sHNm޼Az|BlD$ h9rż|@ /Q" >22NR8N ]帴aΤiE+wre{.d8FRMWM1z#75sNj\3m[ËԪ`&TJ{tկ*+QF[1x<s?3dBfDMvg)=f vˉ H?m5|-jFT̪9{"m[-Yo ,W)vpfܱCn'UW78誽ᄘw#U6gfNi>{-{pDO^>%G|p/dKa{vrlb\_ J.^z{hL*XbHz0Ҝ1o SjKkF7 B'w^52}9,rLb`al[ *˹\ !*WԂ(q hZe5YI} οeE^TQL@<\3}Qٷ߀AИBK1i(U OBh!»ΈI : AEޱ./w۱ظdSl=f=ebOa5,d\W8A $7R?/T[ʩr@2mp޹ojuټNR%KPRgyRral4+爎[ٱ/mehi)M ט^}:b&OйپBqx?[i^O*yT^=tX!}\Aqfý7|7z@lEm>lp@<$azmsL󲚋V2nTwIRW)p%Cw8/o]#y0dz I ^=7):JpݶVS(hߚK1J뵸S RjNI8מ!sSg^0US{UZhsYr̶ތoX0aܹcE"Ʒpԙ_vnaEڔh'=9{yq# *c혽Skq. 9~R6OPw @ 3D!***h _T+AKr{DhJW[1Pe2|f]*rtk0Mٻ64ٯ Rag%j[r[ǵa>}ǡ/8ʷajl?jmWB1(Ybw`u\:fڷŮmF7_x[z|y6@|?Lutfl:rW_-m3fݬQQ;NXȻᄣsk ǃ+nv*mF ![z} Q2 |@P W:/CQR jHp=TjVtjy:v:Tz@t:WNrPdkVԻ Es,_j`B&OvZFGb8yN<h,8~qvM+Exu?\" ס@ L?#%oOOZwV/'Me TlP'~ q $VN֨b^%Aي.Q}mKL:?ljXY`H.-.*S{\Af܀:0W;'""b(%)}Sl<^ڄǍ": HD!@-@IJ Tv;F&M. JzͭQVg^n@ 7\#;ۢ߻jA 1(`VApnQ/5kWgpB4kݣd)0P{I[*PI !0Sa V)( o\ɉ/]yg qzw.ӌIia8_&OV(m@ jA?FjDҪc2M[m7S`d.xݽvԡ(avzuAz7mkFNhkE!#d%ZߚNSTAJ cm 0@ @E4-q(@9d1 xޠpi(EXQ*U @xPp0㠍&qK1S>>VՐlS"s s!}}-֍[6D5k7kٚv;}  Cs(@n3$Zǁ ɑ1-Aq} @[v{n/]8׹[a(̕;φ6u[MxGdPDѨө *bS.!e1dM>I ooA $ "rWpjr2q0RdB'*t4Cˏ9ItJq=3N_u MF<ZSoѿ[ ܆ ($^!H0DR<DW)ݸWn^fXnghj3gV 84rHm.;.vٴhI8`Xq9T`.kwms;Cw !I;Ђnɂ``Pk50+TZgDf;le˖a$'D!uTe_Ɯ9qTRQKxrʭfM Qekyc-ۼK%J[wE'oU>kkND i۰~`dzؗ4GVT!ԨQf1 cJ4n;[7 jT.ŹV)YӐFm_n.S#I^goרSH9^3G-C zxx T}zQiɉ( $k"ٲ?*scdp ѴMo:umױK{ '4!@*{%kЪ>!BzkuªՕLZbԘV~{e:hт65qv)[®ޏh?k&C MCD+_AEィhG3/0ګۿreĈksM[?=d;Uf9، @ H/",9Tj\s cG*JGXBߩ.>ͽ/*u¾H%oX^_ǢV|d%냵/l}Ĺ;fS#>-$~G4#H*cWXv/;Zu-~-xƈ!pt0Ow=/?D@N?Q3T9)gPE$d[L`?MӒ$J(P O:u+ͩ8cv.ߏmd&3@ fRG|(yz[idxte!葫s{>&NiY% ꝫ CYMʪKN9;w7 Q>MWVr΄WAW_@ 62G X`]afzYYDQ_T{ ՛KGW^D߾W={b4i^h1r~򓯽I%9q=xRa7N]9KvXd޻>w15K- +@&{y|?]JGW%1Ћt# %9`tcNyZԴ!HB]ha%>x B6-k>K"y4]EU ]T:H xQ7zrM*SmA_hztʣڥ~G7h/(LriZrq^mX*w< >))5>%w3ae3;I;rRz54|u`m.=e冶 FL=Jט>R=peF:s;~>cx=W<}ƨ Yߩ c㦎޼\>B Y8u5HKNJHHNg48md;'݇s^D Ӂ!˻|JwR_0u-5919x=t 8\ Y=A\è$p_mQwLJe)+]֜^8c8)$ ߎl /b*g ٷ}t,?V t9^ݲ]^37!\,m\  \%;S^Hq7:w5ֻy23Q@xY:,sE.χ/(L2iN] *$"B*kWz$?vϱJz0$0IHzf7$y@֪-\1TGQ3ޏMypI>=6!͊"o`/[ijs Ģ`z}Y_|\;&JSԳxԤ@F;|׉v%yL@22rxuڔ [U]>~7* ^E#\^|BZ͞<6赟|"u r8O|N"%|l'^ƖJ( 8x]9\o "po"#7+{;1$郇޽uLJA$-ɳ 1:޼~IՓFL|QB=MLÞ&3JH{7=M$q$=ɳx;#:%6i$pO|v΍۷{,jLJؽ;oݼyf A sk*׾(dk:0]oeFJd]aG6,X]bZQ~ٽ6+[VݮJ&o+Ar5*;cQ(dH 4/WVJ][P;Ų]i30jc ~S~m D٠(J',7+py\r .7IQ?+ @65]=q*R׵' /8Il5g=9TV>[d/+J>xt I>~jLD~S3t aR;G/Ah\LۿHqr p\p>U ΂$>$%6TK\WN^}Tk$ -9X`Ėx3w/AM'V|=7aɲ@mw)f'w&$(Z/Ij/k~v逡ྐྵ8RW:wB% Py&`{6My =_{# Q R3t] e%![s&S:Lv?.#J{(k8u֙?W @Y;ǰsGN^N t=9dLJ|d=PTMMrϡ\2EFq\qHY}E<<žXSp`W}vaX~Y4g#E$ӤN}kX3O@(neg4ptF 3o?Hn$IPgdy'?w([bK/Vd{*ښk:UZl}΋${ ྭD|qtZ:W/_|ϒ䑂(R_rs?ŀi| jY"Z_ ٔ$I%H/ZܰJj)ᄎd)hHy܈t6^^JղO]#ѹX*1hJ9 H^`h31 W j&&Z0Hyp)]-_z3shD~ a$DpMw"6v<$ޞhfP 4棣~|Stu0~J~U}K+ZG&;%Cyr [B.Njx=:RM|uNJc!Mzi\ Ѷ\vD%대[6:W@4l 9h~~峐Kjfr'Cuvo=Cht^Qa2{,Z5[vrMjKS9 l5gNүyjk0hJŐk%{n$RUJ*^\ڡyCg2VM[o'^Qni??AJSbL@;+(Uȵ׃Z \Pєl6sXu7e֣uk{kG/7tZUQ1Y;–dX5oSٕ"dhz -~Pn5I8Fr~AIn~&xIh]?yQjj,2r{5x`8%NHD]TEq'֝آaN d]t[VPR˨%l#<u*ґJUUڲ.\ȣJNCjwWI`u@Y x94 oh҃oݺz\^Az͟:aUĶg n޽}ͅ*{!e{ǔ^p3PF}zGZw }ޝp zҹiI9T@_cƖfoiD߻uVČ*w&M>{Ͱ#Pb'f䝰lT|kxZ"=|IN^kOvs':wͫw7)gC?~Ѣ.Z5(dW$Ȝ5e Hf!e2`O[L%k y5; T<6c6".8ҙ 햹 HDt sԖ\t(S;^HAZD}n>x  $Jy

\Wr;s[E)R] Oч.[L߻#(n!RGݕ=o|jg?bҡӶ] \7%AuG @]rW(SBʟ_5񇲪L \MSx9%בAy@!d&]Z|vOCx})< Pf&7tŽR%OMp5,A>C7W 1)]'~Ԯ[~!okFS&d^EZ7lgP\i}<ϋbVodU)t_vAH]0 ;Z0$iC&JWw_ AH&Xճ>8~aγHzpjApP f1HN@.-"(\?3wXw'k IR((cA6d @݀ ƹdEY׃@ŀ,&)0$;AQ r @ Y9K5UQ4A-Հ!>z ||)LJZ-, $@(>^>))4eY !ŧjAK*JORA8 ּ!HXa6]0iZF$][C|2C dW^a_"u2d2G F@h1JyYW (@  ZPRj( @a~3NT*@|Zfmiv*PD@/lxY"@@-@ Lӂ^>>Ο% Ş?LNLIO##WVM|\B|r .ھUbVBo#j.ˋ1Cl6ĐE4G+M_һd>)0oG?8.jASdNICRA8G {pa@ rϛE>g_BҎjy9Uo7rQ{`ﻖ}>e-0+MD;[0~H>cã@ M&('R<~4[Q .!躊ظnUjjZz8u1"PRiɃ[YeM*wU輹 +DܺwREqs@ @LЂ@l߲@-۴s8 NDQDQyv~7o\%H}YķԔ=;Lwྥ@EyrAZhSc<_Hvߔ?.4E>h$C "yIxo֪M n\RBO߷Ŀ.xpZE8‹BMJVՆ G~{9]:\#1R((I_w@ $.u]˖xȡG`8I#HRPEۿi|(dkM l@ ?E& $v8<6;QqvCSZ8᧶o?zy`.@ /]8Wq\8y0SJ/'$C FDٹ T*x JAZ?;^B C8 @,G>*8I=¿*s G,r4ir6Ce& Araf v;#C,)Fs Ab>C@MY䕷Ө[~M<>jly#8-V m X~E<dY@HP>oҀq4YnbH8,,Do3Z )KlY;^:1%9pHO[ ˡ0I,4anӨCZ"ooݔdIIpcqحN@ Opf9g"g3 @ _!3U lݯ}g۶:YԳO?~`YZץ_ߤޤ[oOtXp–lO3vLocU^|nd0da_&_SƆ{)XK&|NۂbE.}zm9,95'(lMO.hqXhwMxϓ\Mzc_Q#c<ui_GE\> |1@݈QAp\멐WXAxwVnwh~},؄Wpp}ɩbY|yfk掚1oÇoowh^-xF?nr; 1&'^!kջ}@Ҟq$Iw ýjwM9dGv>Q-YuF`̑˃=g oHL^ͶQ}{ )6oІ[5l}m+rؠ@ /Ԃȿ PԋRRr"n]ї6{ukڴ}B(Fs&߿m΁}6\sIYܳ-+St [v9F~LJ Q<%Enp=+2pرs hAluzh ;qhɻOozcZ>c5_!JC4:B*:opR^L8ĕ.0(JMv!~5d=>*s 3D#H.)I~ǏyqϾMi)d@>Io8wX jHt9*UDžN 鈟6oxLiG18BN9aᮃ;T>$CC`(K':rV?s o va_5eW38ܑ鸼HijL#6\:{Ů5|^\bH8&Qۚ;G lEC@ $Z$QEdd2w(F(kSjFwHKz\L&+ᇿ۶Zϵ'< {Ԅ8F# f!%g_r 4\5*լ^qo)~2w4O,)e #yѽ PY_J9Pibw~>pOSՑ%Nc#skM#׽!k4n|b7n3@ _/YȣX/!8 W?EN')kC6WVV7ާA'qv)&+1jNCSiT CnP jHFIcsjom6QQJX&R8fPNC`r,$+n.IVPAh۾U)I";!_ Ӵ\REQy.Bz ΞK~TJÚJ]YŰ@q찥9%]6WN4rjmbqcJU+Ԩ3؝ w!aFloƗ h ~[!dmeʪ&-DD덋½>bx>^T!M")=G Ln}R/_<[rlt6mvh'4g(/"Ÿ@ /D#nݶCeo\VkQ r3~ *:?00PB͜:Qv[ I /ѼN !𞙻 mWYu-XS]nL{6-!S:4PL.dN_%9pJ%O[[i^]wUEpcnSyH^+J8> 7^гCOlW'oo9TO&h{l७?X=_eC^^ĘgT da(0~.|ܦ%ar3lI T? re'#N\t:$/vg&s`6լv8Y-6)VsFۺi}ߓ s ,%7_u`ԁw+ѷUZ-Y ~3a/ZpbʾFH T(G̻`q ymi|A@ZF>݈}-TP-0xןĐ7q BMLώ1՞7[2db.-lX¯X?󩞘mrP]`Pк+#j/ZP2Sl_TZh4M|!24Z/VC9cdlM| ̘08  |e5nO~|Z8`n?blߒ-'Hm1x:*٭,d՗:Skuwii-?ljv% @H}%*4Q~ܡk]^s4V Yw(!|YÔ@  8Yw R[,o*>ؐyQXҼ][yDbOyEuP39`&_]V J"cV*(v,gRohA&'v `! ^cxq} d#J3*ը[V*VTbA!!v0 Wտ+]$Eř.n~ԕuIW fg꠮'IRAbDk}:+ L=1"ϴi-"P0/7l%+ 3|qL`<Z=^Pf QJӕULiݓ5'uO,rqZӨ3S!R{+@Q JT/@ /O&hAByhߞURi֍6Gf-6n'WOƤ$P++6hq㗟/y4_;z8iDؑȫppv̮Uw/0\ݸ{fx3OXSd=PYm!"8+En>ݐS0{gc^ 1=IvŜd8K!.$) JS- /" QPѲX6-PB̀ ]|!2 IP p/jf ;a򊥋|?b QjJb%RWrV+A$*B8SZ@aVpFltѵc T5w_܏|MH>b2,(ҕ@2\ҺO?Paeys /h>yO *JRDeR( C0R*S>QPrƀ"ԌF:CEEE1I74eG11E[|A!JSy4ۍp$D3z+ː N 5?L2]OEaBj#ndP5m%M&H"XO@B1 QpSPR[]v1Q Il@9@&LiLM1@QDD{򀋿~ŊvsHV#9kZ0cVK%>fvKśth}AJh^9[ ? ( ;Ղ"hD9I )5Œ[C@8đjc!ȱO&-ժ[t9a@P B _:,T!Mk Ynr< gj-inruϬjsq9"谚Y#tl֙1BC@j78p\ngK"OakխؼAR)?x#?>e,f*)v Ubt/ծ87yɟNa.;X&7lҨv/A D'&^ĶK%uV`΄ 3OxGg) H0{%xz&v` XM&W5oߌͳ7E,D1#/Vut3t2b>7}gި~jJu=4n4 [q%b5RK쁭"Aelq -"5zhI=}jϑ'.tͺgFI_}L 衽2빺{V]޻g|X~nW(ߣ+]v4{ÆY X{ߺdDStJ آ-eiG=qnݬ {v\+;]0\e;Iy Ikc}tL' pYea8% ɋLJ&'HHl>[`hX~N1>F6\]u[NE t,xçUH \/u >@aN$W9|!# >1/tFkG, uŰ)q/m5 ho^ȂWp*rͺAR~`(H7)r {}sg4aQ +nini=*EhiujM&,LR88XT WWDXIVCHy~4L;ypӜMSj6IK>fÉgS5oz3K&.3&r W9 y̙׮\FיH"Q]M/穛e ެ!,8N&u:Xz%U`+Gԙmti c6l\?]02 KCUmV'v.>-z}8P$ICuzR"g9\zݦ6WMN 6e:|%$!d"|CJ+å!,THɱ#gU'~2\+$0j%P8e=?W:m$m[0kЪQZok${% fF!W1<PD:%l=⽎ n:\QuN:l.zUkڿyd+ 5ӵN!G*8tbkY1㺖Xwsu$ 3W֕ [#|E`O=r : ︡/ 9lg'(Y}Wsi wڙ_W5Uw7{%y}]&˸融_\wvot*j)CeNnyx UZG{baBW+Z^8:V_.]:q$w :N(}2 .}q/ZCw8q>@ Y+VzS?=wgx[5ΈKvw=|9k. 6v5%]ޝ(r:eN5ݟ՛4}\2 Hhጉ%Ɩ*ql2(!Hqთ.f8Ν6Y-f)A ( "Z?E`_xYG>^PaHu ʩs{/{vbb$CP#GK"F~2c;Ϸl|1 380'Úzg]p.ɓ߸wZuӂS_M3L/|!Ԧc\äm?zvweJC o]^B=}Ȼqgvk?X#="t:X| ΠuurpѠf*Uh_ŔD.izXEp[Ch Fq`R@e0Qu4lf6 %I 8DRW+&L멧ae6gAHi'2 qJ0p/g0TV/2L1}Q J5F0O!)RY-t9=uK"}F6UIqN0lNF_~=ߏ|Ll/9\zDd~¯ okZ _Fc$e\i6zx?myK1eVeοX A{,Kʠ]7?ޘ޴鄅z+1A;zH*UaQZ/<%̶Qn[#c3̒$3ۓ;w7'۩#drZJ .;9=ML_A\DI\m<Ǖ7ELמONct:<<<A 2[,oYzΟin$E6h>+Wp8` 0 喖DI҃ϱqªo#Ql`-O2WVmc=bgo厕=Ȯ|C^_4wL@uX swzmV52CG>ZfR;zS޼vFP1Nm=;ǛeYaԥ[:Ξ9zY?O=+aVY PO}-A$I/,"l"_#{{G`ټeVm??rg/oQϽ7 zS'W-_rN ѻo S$Շ<7{c]ݻ4O۰ K{0<?@Ov{/VdJnװ* N[RV54חr+7lYwb^2{Vҙ}Np}f1cs}_:=m&Y?ysbr<x1zxc}vTŊgg5jw nw8K!ȿFdizAzvn;p'~Қ!ˁG9gKꏙg1zl[{nk9ls}3m့ĥeyU\_`<\2f2Cg_SfJ@O ("8U^˗,8}M]A' [<j4Isȿ ܘyvA{=j!:&Ny7`?c=|p4,]7MQf جu8}Z H q\X-bw8EyZwuaRRRPY <a.?wGnn.fY' pXJ*D ;nh]om0ކ+珔Lh]7yW>CZnO:|^c^1\[۹Gn;=|#Kvwlϥe21-@A:YykܴYێI0//׷b!!GZl2a6K2xz,VT~W$ $0H#4Z%.!mFPZvܫ<Z iђZ3zyJzAAO`Ʋlll|RrrHHɲ{yws9p0ZAAc˂,\ J:(I\ϚhAWY! wzGafA;q$I2AAA'V&?8z@  ȓ7  BYAA酲  Ӌ`CA{lbRw g<3e)Mۅ,/~8) . R tEj NۄD EVAyNG.PDAN>G*DY?`*" FhZT) ?$Hn)a9bA%wwtMS zv4 /ڕ2ZAoAm$VsD,|yC`VDAK<#"&R/R]' ArU7_Apv+\wڰ}z|q#A AIQ0Pq.7w]U A\BR޾{-ۗ/]Ó:܌]flvN֊"\yC]տlvdaC,qNYb=DwA;" _Id4{ k RQZ'Iq*eJbiWxAdI~5a9:nB{m>f=;[鮗'-mLoTzEV='qo=;7m fekuj!O]BuwޡUZգteotlROO@͍3W Axp^˂'ogh 4't(Lv='{_<5+?o^5bUn}!mk3~wo~܈SG`\h֝:6+ï+6,~cm ^^z%0($7nR媃񑤿꼒,]'~$p 0XE8 ȓ )N^Ra%eWYfn&Ul0`GτߵV1/$ 7\F/][[Exysz|~␗>Gtnv;O#㫷F| c˂03ެEv홟M݆O#p2%̈$b2[_TVfeLe& 0< ~VA>qvKig.q2 1cGwOS $I#;{*ƨ>g(I\K^4ZSwJ+W>왻gۗ"H$fDw=?Zbj])cH`CAAqfAN|ň1/dvXn`@Wf-7a|1+ȜkCY > _YuٸqP9g!j~zyNҲA]b=XwE4&}Ǝ& [AL /:?G=c=q϶I,'Ry]o@asܠEN,õS0*@&5X!;uӧ>='} pµQHhOS[ {lYqj*W.*(_zm5+ NvUYAF2J^ƊZL׸eyӱu?dw:vng9b0¿Ygasz7+';?/EZ'j>HޯՓ6WsG햰NUᯰ}Y`#DT]<~>UbkD3]=Cm9b'/Z?껵;3=3аh/0_h?P@ UT! z ̷oz ܾBL\||4sxKF!xާZd 6//+ʓMͶAM21Y9j-<VWTDlOa1YA#F>lXϦ6r@ZWe#=t@!F~y"/k~v )%+5Gӭ^.o A)"9DѨwڱoϦ^ٷl|?۷w1Y;j0y]:5Zt>^ H0 vk[2 ȃNc铿0W] #0w`bM1_kQvAidYzs'I*).dח3/Xp؈fV$)I|@.TSQ{"}0,ڲЪ:=@ƛ9sޝ8eAO.NY!n>&9/Z\B"=\0W͊K8![XИ4]PG+rR'#fϜw^&\e. [ԩ^ZV2ɉwSz*V6Lf)56>S>TPe1Ac#G֬بL![=+Hz gNX1l]7gP9@аDě\y¥K/_U-e^xMN@8k/]q1콛/^A&Fh ߾vr I F<1,  =[eYu+BVQΥsw0Y,o׮:oE =ӵG@`z/ƶK%>!iݫzld;$9j/x!v$akt{c,{',H̲cofV 7C%ڍsO'EJQ YߠP/˕7p={ 8VF?}HnJfٟEpV[t"p y"8CĨIoװڜ$ nj8 ;_ʏk`T:`s5v6aPDy=(b*'rzdTwnRZCX-V9ΚR/4 w} o*zN-,%n\%N(SZ4i{}{v|su&1L(<FQU'  а#RNt0 /SϾ)Nau(ERT?8Zr٭BN\3۠]3?uiyW?~g8!0u'AAQ ^^%EN=B̎fԘڶԶ}v:uصcn4M]r94"\~3)p(}^|6;7/!PT~k!{1 cxhX,Ø:񙸰};5I>|(EA[my/(#cZuдz鵃g:}!;<guN=}lM=z肅:}ם*=oAAG͂80x$Ua-66l$//oղEK]toK$WyGsigddS шva!Ar qm_ |òU+Ve8is ۺ~+[g-Ӡ0|C}?Yu3^)W+m7mOq'o>أg=yb_|e[ꣾ|sp_~Ͳӻfs(t  fz16oߩKΝ9i4z%&n޸z ғȾ}+beIϞ,>YRNNBb{0lN-C$(ڐ0 IV_TQl6OCHZV!Ai}ҽ;`|+RYw 648%KaVve4QazeΒw7*`MFy*ř7 GxxfNe qLtU6bgAhX-N]Dq ,7 8$^lt!IR׹A0 :N׿) c_phpHdFApoH:>BR͂nx<8 >a4 n OɲeEyއʂeǕ,Ёa7 Ou2ܡ'%d߁M>YREj z"KS㘬&DY~}|, ˹t:Е<Qju%%ŨjpYr=6]gfa k/hD WEE'Bt1YTo9`x^e/~su%KS/lRIVூ("l[$v˹vw" ~rT'8u`ڭ$I ؂k6v]cԑU9@U7sryԮAA' h _Qc#"q[7FlwnZf[﷋T{)>u<`udJ14*!6*[SXbH:R"n2% =WbRt>q!R10YUBt|QY1_\gJ"i*0 T!st`jB|2(c1իWЗ L I-q5}Rb֩Ӱ^, ȥwJ!!>L~ # 2?Zów#D`@`PYڹ!G͂('TRv*WMZz vOUƖ:~ O]IR~mmO7b}ex{yfܓLyޜ둟~6_qΒOZתS#+.FUs*r[v+~Тq"(V{ͅR>yށ zJIn¨icJW;m_=yi^F`P|]r")v6I_{ł*GYCPDe'cYFQ3`pYLQO`CFo]ԉc`>xaGEibh>s&Nn=KNyqДE{tY]:MﺻCOݥMlUUVg_yݷ$A*I?QlY9é_H1&;Nz)#;{ `ʁ 1Jx{MD;E,<>2r[Ι#p)1^ Ht׮g,ɭtb[UFex|LAA ^PQHn=_xU$.!aǽ /&G=Bfܝ<Fk ^Qc4j 55~Ž7 hLd@d~lhJLZzƵPj-d9J O8EE HZ [$7AO"Kf)`HH8|~HYrXV4iٱk쮹e1:!$myd C~g?|.LPS+  ȓq#v1eϟ>}frr:pcG̦2] s#P$/$ɕQ|, O?!ßAyJO,  \  eAAAʂ  O/AA^( " ,8d8H?˂5hޱs/>ZPp7O+$rO )7 ׻o;A?OdAfWwA0܁qfAV,] D0DZ4nױN-,ȼkLbb, "o>vA^Kê3( "~!wDACq43_=wmԔ 4c[$6sD؜y86 Uke3_^@yd Z좫%Y˛93Ža]}pw)6sC+/2AAVy) (lVjX~+Dz$AoU .Y=Y^F@ͽwgk( _zO> \"LF;rz/NW a-G=}` Bh՞ؾ';4P9 ֗:EQclGO?w8ũH0/}k';6wd2g-W}c徃n3(@[IJN>~sr" <,cxaQQ)~ޝ)ңիMD̲:#`ISwMiTkbY3^0Umz_ިYdL|)JNEn>hj l;zlܸ^s1ưVX%ao,|ķ 4JiϨ/>PFƽeCTo<6}`UV:|:ufرb$zӁ(ڰ?-VwAAH! *ݻ]ݿܷg6b}| 4Ri)!fKtj\/60^ͮc6h(;b7|Ս\lV/Ȳ2*uhT)`Gfͺ5"i>;JRo*jo)I~_r3ik/Y0T8>l7Ϙ}jފ3>^NȚ3qi}!xX; % $4;wL?w(Ay, QQڴ1ioqsIg'/S)EbfZ1RGtrjaœ{4jG>- ^ft 4ͫv<oN~K8EA%%f Pǰn>m ?MMOvН|6.3 ?.>N` EmӵMzOn*Xc lrhs|W[Wv YG|7jflڔSt|s޵S@ƉLmCtp2魶)Du:0+ezp9.ȣ5'vjokԂ992 a3o6 %)9l@xkְa,m?&==0I{ѲupO  ȿţ>_e-۴ZW' 1bFһ`V#+ fi'S4mTɓFcy),qN~o/y\I_X ͛Sp)9CS'O:in:ju ~cv_Yr> gM'o8Y(B} =^eWo)4Yw\Xw̫V|zy+^Q_VƂןnfd/.^֏^UGytg/Nv]_Q (OLݷ&S4]R\5zx5XWx}1q5ï 贳ǨDuK:tY`6yG={1ENjYNVwkW,3@`ZQG8,Y!lY:OMɜGGckHa1te=Fo`H|$F PDANyVK^9wa8eQC._`lNj+J9z dBYA;=$E;{fUSYiIqYIIqQaIQ.ʎ9 ^1ע^:Tw  G,˲ `q sZvU "O,T/ CD1BYA;=9bAA eAAAʂ< ((J5< . PDE%Ays}6 Q}Г"]~,2 ]}oP~ g)AaBV _ eAS` q]xLŤU*WMyOdYLTrU+ņiN}+TGT:m+>::̃u^ n8o6Y ovp-G'Ko-Ze}DMWL%#Tj`زPm;;qHU&1wշ߭5uyc |yӂݼq];KA5EpO PD9 VzcBCj, \8 I29wdip>u뮏G4Xo@Ca8і(B-˱2%,^)-rmE ɂ,ϕlmAEdz]e6YP%%\J~H7$i-jsJ4Fxcыڀmb:,tO^\d9yeINҮ`RZZh"b*hm2l$h,7@_pjƝ>YiS\vޙ f|ic8# &/U/~%uy^Db{oҴk X irWA/"] #(VOp'i-(1gJ}xهgo)6;jmFev{Z0 pMy1}:yiFo{솹ø%svs`KFA hP!s x <Fk(074k0iz7Jo4h6)4jұsע۷ yaњ,(wDDdX֑f+2$Pq!~^Zŏ&uwʓؤBC5G}:]Z0jkjxCW!9}ŅmkS#C MxeWau,g㎃&T!'oiA֣>_eٖm5jt 5i:gLofuRӼ}|{ nS6sr\`t:,ciId6(u )!N Ã`cx0SoON HƠs=!Zyiw/iFN;ϊ[rV2IF $&isq)Eȇ׬ұqj AAA{ Y1#5Fj!5߽}Xe3,Z͔9t\#΃RlNoƓ谄 ֜ns~{9QUL;>勧zjU|a՞s'ViKzU>(AAǓ̂>I;$zǐYY>hӤzeOS"3L3 I"%裟yzתR^YsgN1^M  OHHRw)oit΄5Xg§kd[/NZx-G, UAA26i޲ev죨߼?Ċ{/z' |xdnNYq=FQxANSI1Օݸeb&*>1Gdrޛ6ո4&Wp 9o]+%xiHk 7 =SA;=j$˻ca$75 %dge=028G8( p (8N3AEYj~yE7zLj4hYD1B xQQNR$z YzI"\As  УfAH`|ͧaf-P4]a)C ܪ>o^+IWr͡]ѫGɂ[HxvvKz\ <C` ;A G+I<|"+D{S$GK 8*?a,E%HQcߧ^$~'nbpHUX OIZ4YJrb b̂>ՓMEUiԱqZdљ+vQWGU궈,Evg%nU>$+yX%fhԲ]HZ.0g ֤E*Uc{VܤYzjR 7$5?@)MlbY7Lv_JMiC\aV,̎q ڠ,z# f6&AX?m|Zz|LߢCC> ?0,)jΕ4EVX2<.LLj۽:"ꂪO(>өAݏp%Z^""mWLZ BT$HEMZo^8zF(I걚!ILamG%z:8=n\]ϩ_\Ga ]C$؎O?]$SOlj>e##7o?jE^֪F8(+I6("DGA^&ò Q/1FXZ6^^4OD? sqKLOzô:8 "p]pTce#"od$|}£C}ja FX`1I'q'Y taA+*<Ӵv粳άX-sӆU3O730ټکC5Au۵f7ۘ{UٿL)Me2ﲌNG`D'GZyJ 7 ON8U?_+ޔk%q5T߰6b}Bc=)3LkHDMK6`GW:~sDKbqUCNjkկ/(%֨RRJӛBk׋ԟ:ݺWJQd@H|wэS6ldm#7rq˕;}-g BtwT%Z^ZV.S3$KG:vXP" O! R11>yf!)Ahnܲ-Am DF>j`Bo喃p,~jS͌!&?" WnT=KWH:[2/ڂ(b {vvy2Y}4cEQz)Xk>-iYfjsꖣǘgӷ TD͡=n7W}ȡ&}:󦽃#6Uz%Du\ɠUiU;t)&٨z`X%ޥw1=|䯇VIדd900BLg}ѻ_l?3{zyVQSYՠ,(ncޞ6ktc-uϜ)ȼgZlޒۂj"wRt;.9.ɚ;fNc$poS%e*x Jr`˕~{ Q#Iԅ5ydrUkÝ[89m & N[ߗQ,Qj-fٳ~MDi DݺpŲewε?Y<(T}u˿\|ZjٺQ(:qsL^c+Y%-v\`ѲCrc>t+~Is-[~UL(6 | 7/EZ+@,+ywɾNvn]Kf;fF#I gCF%=t)j^:r' %)">r|@juZTȪQ#Z/_ cc hu Ogm8ˇV#p"p^wSG1:bTo}dWfN6Mӡ `gdMi 4*$Ug3F~Vq.N!#i8#Ҍs7ă؆4ڎz%N|uͦ{=915'c:蝉Y( 1jǏne%pLZ oOΤ:' 0|9Dt4MYA8FمP$ FPT3?q0 Aſޝ*\G/*5ul1h.\ů_Ꝩ'ATԡ4yg5}~GT}[h˧N | &}δ/<_\DE<%v;+Kpy2)YY|&߃̔i|Nh~3bhgR}2vH|Bo%8F#^ 0{1`MEfa$U$;w Zl Kn^0ɔ@ݹ}G k6ɯӐ3I _:aہ_x נҺL>g G3ZrqC;v,1WWEzGYA b-'~|u[NX3K˨I]&3;e6R`oZ+6y兹ru0O4}|6(ѯ2f09Χ 3FO>pWWO=`'a|]LwryiSHr_7yɯΫo7iiGH``9֦PO!Z駻bKY(wP*6o;~>q^hܻG{&68,mK732 Mou\_ݑK3|7_0IΊqKTm%r`Kh-cji>hl`nœyNA,8%p qie%RmoUMEZ]0iV)Xbm攮=pn۪q &'NOc%Za}LDFFHNSnqA 0BρZ=' l-gl7&mS@J*}Tt"r>  VX/l մ2]ڭݡrͪJ`j~P#_p^8^ToJYaaB nЫNh5Ig/}~arb1N Y`NjuOU|pOמ dsMנAdaf]fL,txjC/r,l= EӦsGrj[ &걒K@Pa\ 쟇lyW3o$D juRxyӪLh鎝8IN_6;\Ty3ݿSm(\P׻[ 8EԵO]I™j\mr'?OhU ]v4\.*ܞYEjѯ u-Wf٥[̣ŲA-Fpj(Q\3̟ UCdب]tmW}蘤LYwd<2!bVFGnilI8(r\-ڷ`YHkTʕlV/Zlbd>t ߹o(+lxՌ2orxG:{ ; OwD}|}ܿ{"&,5ls^O(689=Qz|}!QrգM_)`]9~͚nV7КG/Իg-Վ=FFZq-Z7k:Wq H^֣u&Wn;q>_mkP(DEHYo4tk{D5?J.[Ox;܅,+"_<{=:=۵huecGuWtDAI;6[m_1eİM۴uUKh\+EW ÂkxRe?3#DGy=y,JӧO͌7{v-:V9xYv(Od|BDj/s %zay]:4kߪtи rΉ6Ps8 fR+7:Bn,9}QvjVLQQD[f,"+rɡ轃2ΜU˳;6t^V׽#SrtߣmSܹq- l nܻuS;44wE)ikN8rpf>}˩R9=:l?c%K_8K>dx.5n{7ݸT9fXzwǥ0]Ӄ1wwAY""+3NS+FMW._r1a~AN=YHnbӇа6FOr"#M W)=XiyV)"1"6QOź;$! M[e8S߃_%$Qk(y)a&2,!AtEx'+RC9J8킃P,_1 W[_lxQ&~T",kXg6Q CWU9*0Z9Y^0x !򷾈ɫ +(NC$ɦqw5wX=$dNv# y@,>xZ%-9՛5T>Ĵ:îyj(Lᘒ:zǡNS=\yj %c< ۇˬ~z#$+:uhX%F8 =p09VRߋ8VKA98 ͂[ᬭ2)G@bamc}4뀈SJk2\( MBâϽ= e`XhH8[ \躞9L%($\tm>pi4):YV5_A9΂ꃕ~q .w,WhQn}ICӢ׮]vJA~Q}ng[ҹK]1p!Ov #VbwNtCIo qP \F(} E"CE*-v?2c^,6 U#^k)),%AI<VG⠺=a8OA88" W=0([TS N P: q+:W%\:oE.0:8ҩ7!RըJDESJ9nM. " ȟ]@anAYA;{Gɂ wBC  ԑY^TR=l;Q{I9]όSpF9 зWhH \ M׭S E=BX< vnYmJ2{?_ Q%Maz-po/5&` -C,H +bEgr^hSACp&t6-k$!+,$5O 'Q[57dJ0H{08t5K”j Y NF_'=uwDcbQ)8,!yЉd{8pFGAWEӰݵ|ńخڝmڴIMMxŋ}Q˾ U[nSM֠vr^F1Fr3o^+o|IȍsjMߟJTɜͮҧWOI 0_**eqwVՈ߼;W+ʉvѯD?zo??9%w~~ac}g݄mq{#RmG9Say;w?%`#pb>N.((( |ʕ*(+zOw0*j藬M*q@]pvx>JX. ;S/+@+)76e7Vj+Fc_\=^\Wԕi/NH83y=^'(>ܯ6Fu;W6՟iWgLެ];Tk_lڗHpJbYv'fJ#.^z(C99k\~wE;m*yEȂoٳ&ǖ-8p`01"::V[Fg}n)Y[4m|y?;G ԳW>}~EDE+ovm簘,$wSQMY6VRocP;s HN1ݷ&5wa/22%X9キ_3bPA^?H0^iH[IuuUkFR 5pb\kM$ޞ9~QE #$|x~ '.Gʹ>/5z݊{9"~ 'Ձ%;[=?gBw\ҤzնJՙG) lA`Ns C+) c2$Y#/XW_{#l zs~ʍv|(ţi}:C] OﻫE<ǖ4p/ϵTD3X\ M=?)ݻ]1&F۠"qL( "O9Xò?>.m۶& PeQ^'N֕W/ɉWlXjL&JXJR=V38RUVe+LKTr*zPIC@|jŘư@մyn RMYzDTt}T ϶ f[_hWXv,)15V" FR_%)ƋvD&*rjU+W7FW]19"u'RjI &6МLp#"Xr*c`GTbR*WN 4J+٭Q~IA[P"*"0J/r8o'P%)O+s&žUx-w+ozlbhr/_R.\"ZB'^ T VR#:0 S0պzjS< 7\jO)j}S@5B@Vp LiԏH Xm (ݎw+ Im" t1C!.1<_M$5\}]IsÖyCyAb4,qO&GPG&Xo%T])qY/{9O\Msplc^)o xD, j^V.cE"z 'ȟ`TC#,nD]=xLWYU#W(EQ,Qp8[q P̅dųf^|ݦgqZZnú2~%ZM[jo\w{t8`W*W`\ऺQ>뽽5Sd mZ _jH\[{+g,Z˷FhMݶnO?vw^lӍ'VMA^WSHjm=,863YgNj6nnH~{ՆuOW͟@Elb;g^؛{ i1ڼ߭^v]hBi}o X^;wHـANJJu09]yUTRk\<VW53!]; \$F&^+A$*=y1&)~a0 1-h\Y2[Yt|B ^`R T05JG-uXPjUxRPgL#+P :0FVP4Tjj _ ]犠 yjZA_%Y[W_otC7mnθxem}M2p oҷJ_=[x|' 匬㵥wѳl7>G<Fʝ³FUPWSxa6ez¼nyf F)Y2}sg܍HX~9\Haiգ}ȥ_.ܹ;7YNIZ-^^UsXQ@)[͢R亡Vb9mY ֬=x)'VS [7x;ڱ,PW~\g32`ʛ,*-#UeLvV4`[BIT(Qr*Jrxt N% 0hs W5=WS`~ތ`PSI;@з'`Oc& T`ߞݕWw0 ]nU櫫gYm<[;thi2VRC99V’s,v{s-^%wT|fD[z[ R i'k'&Ylmxt҃UCʇU\&X콇ދ7aJp|zBq^.\ ; ( YHH`I ;)J[(V(IB!!!nw9].Hװ;;>;M'|RE^ =޺\C(.g-BC-Z#H[z잵͝%&] c,h)Zg<7z3?3c֋H{3V6\{n8F$bm^q󏮙?wt§bhZ}ʧ+P̹ M4ʕ+222¶mֺU+W+WoPUl$YxYOd+6Vԃ$28e.45S?8ui#$jbEe *J EhQž 9|Oƍ B{A {C{wo]:G #.ػߊ=O WҟyJ|e&dHUOwջ O1l1c>8n+1BXףu,9pais{ $HV"Vnmui/4m/X5dAHґ]*@ )C$# q!]րDSEa0te4tۑ4*O"cajNHT"6j)BΈD@@ =@~zx TFpݱl Hܸa_~ N)sQc=wCm白qFIt ]?~\RRҐ11O~&\Zu*P Pi :gE̳sg%!ZWCxDȬO^k񽿎yBt-]%%UZr4+ Q}ߞ ŴjՔO_Iw4~QOSNQB 4jҤGeavΩcu֭Yf7ltգO ^lL<59)ejw}dG*RQA~' qQo\ ۹qTMOI~ />yeHK4'œė/'?{5+E`(67#>Nl>I( Y?I P{ Fޏ3[zѓgaH}V~VD*z"":(zWP=^sk_)JE,p 9VUv(x9*ejGqК+c1Rl /oJeGITvqVYBo5IhU,~y W4$XŃ,rVS+eYet /rBq 0ea+)FYUPiPN^{ qAOϯG`rt/imBq-8m 4)XǼ2N($A)/ۛDρ2U 9M}]\0@HZ8TԚ,JN,4g^X:t +]IYOm~6y:>v0{}xcztU,*c+IT٨v]8kA@*^\F 0Tzr !?!҈WWTtUo>'5o* o>@ ]@  @ !@@-@ jA@ .P B wZ@ ˻ׂ(rAA@ δ }<ϛEZUZz {zYfe7S@ w1ϗ)[s* Y4Z-˰N{XqWC պ?cծ`ڷ浫QbСSWV(tIshU4?mZLfF&h/{HDSI̱'IPXƢ$-Pՠ/+@4 <41!~uQ/22^>~xunѪm&%Iჲ$6/dd/2@y626VNIgŗ) nUZ;w?*A;rhN$PL B7qf8@MY-(~"yB''uvv&m.K 6~,qGvz.7n_Nei? #ZeQxSs/r?\񯐃,Z׊UC@֥T p@ Y\ ^>gO%I 1*j5AFW) IQq1ѹ١UQ٦AVDwi(-jմ^v\H&helsGcf3-V_ޤwX.~`3Sd~dbqEl,6aM"q\28PXT}zyI 7''??82MVg͆6V#G^L&8ELl8֞RdVR*"?J*&=l(KVơ)/b9L$Q, ]vkk5T*:|(0mhK!~AC %ab}n"YCvXv^ԍV7oHrՈv ٵfÈڽݟ(gƜ~ybEX=̳[>\Ѣc[hz>ziO-mb$1] X YtZD;v/jv=3Z֭c4̱h=m럍hm?T@5b;7>]oқMlڹqQn\̲ր-\z7o+rXEƁtn( v{ ;ܝ+w"/nT薳l e O޽v/ƥݍC9~ŵȟG0]׭` էsx%ֽS{V٥N3c/>pMCZ) wEvM?CF.]8{ڃ?ܫ٥Vv4ictۣ+?2צɥi?e{Ϳ;V3u\@ VMTe1_v[u,>;:o4h~UڍW4"x7=ѓi n!V=m!cn{G-6\&w5&-6;pn׀6"7R7b>+XֹhgyC6)\僆,Ѻ9c5~F Yw\A69lZw㺛 % ?9bȡ C}Swm-j37ɥzͦTXs]%k@ a9Y@?Yiܬe}?2 Y_ph=zh҅޻_̞h~E+RZ{JʜUwֶEu62ۧc/}:w˴uoKV5;Wl8or8qÍ֞tjͦ3⎴i覇G\~.}v~!|XGn~]YB8[y4ۯ*Zp}͖0"s#S4yKQc6*~/˷6t3+:joEIg_9XHnN~OݡnC2z>}S&BP9pP̠/$RꖞgǶԮ@7/]8:,Ƞ?+o%Y)&}I Z <Ƣ.S "qgEQy>V~˷"/\X)xxFuf߸r"B[/ܸƽ3#;yixֳp\E]h^*Sk2Ro?1gnLtYGm}$.WF gXBDaNIͷdn)H`4"N,2 SYPE'Xc~ ^][rE(0AZPIP!_/yKBCѶ޵2^ *ԛk3>ա*WZA es/W}+ZlP> ~ŵȟׂ8A$=yֽg?.^;~]Κ~Ǖ R7YNC#'ziТhYclB]"9 8dCtErxT&JJoc #kոAfJ\WmWwYmz]eX;?yu[7mܤKS48˸ J 8D$DBE{Pyxc58EyL.ܯG#ӉɩJځI5~Ǭ.ѱ˨+sLc%iIGҁcxl:iT^vH>eҵoie{I5r !!Hܻ AGUp7 Sѹg£( yӥxFC5, xy羧£_;٤tv&rՏDdz|v[?q5zɣJR=0ba7NoPy ƌOkNd䍛'AsެwnD|kF6Q!8upbvg@ i.X(JXbϞS#;h2y5 (Iڞ: ݉=0999k\ӹx; vqNG=vbFUd6E->}ٌnƭkaDZ P'=}c&N%IY:ܝԨ36ŮY%wh $ \cZTx-Ѻhބd5XBh. 6ApդqИڬVgRiDXPD.: YAgtsU)Kp*b.;0#N{(d\HsAFN.,6tʚM(msz8,&i*3 g73s qqdEVa=N:8d>j$c V%mB{#(@ 3`;FdC hAX)Qz OooNKMy bH#njowjuq׿>GUc=]K|#J7dI4TA$C.@ ;ӂP IR83 (I*<χT){EJo1{}seA{כ6jE3@jO[ѩϺ9Nʤm'P B ]jAJMfG'JCAdI A@PF A@ZME$Zg?P B kAgZ@ '86@ _ Ԃ@ ]@  @ !@@-@ jA?,IMJ~gB(Ikr [@:;dꥭ۹+ o)yG2!L:pʤ&{5;)4_v, t¤>*d!6vtQj^d9RO6ghƲZF'L:Xd4L9%8^QݎL)(K{/%t Kcl #^r0@/*NࣽÕwt))$-~nD1d7*xVX_Z0Ȋc}ߎJo"r\u\vD XQo50Ács`0R G=qo:y_GR ~Vq6ʎ2rHo212"}5u^S15 b%E)L6KS,jS[Nշn-۲̘5~4sJ&Y`#+Ȕrl&4:Q2oe\V b884)&PM_NRf^ofX*!lPgnO8b@6y֛+v$8JJ&P73ozL,0V_Y,;n8J?LVpjXj?m2/Ԃ;PEeڎsi>xӜ! qMTj:*^}Q!گR*UCi1/TdJUCV)"0DDU(;Y䬒:08jU*{%IRZi'+-~A59(#e}H_)U5B2BCC+W,!Egrp ^lʥm6ƩD`*իV PI NAիs$!!5*ucXѺU Q e ZRuԬZgBW--(6DWħJrvtŪCzx ƵB K,VAP)RHE'h9 I\kT-邂p* z >t!tM,MPFO5βgЪ5tY;*Q1?Y(ZA!4feEm@L*V.J=C˗taT[z2*7pCT*ARDPeU*)ZZr>%V\ǕysGipTZS#6(Yf%_;x_RQm: KW s1ِ}M%K%*PʯrRjIҡ5+{+cޥV=DɍJQܓʔ DI\]*C)d5Q"AUpw+C1œ|*3}Fcj+U4]3dҥ5mאhAcyOW7WH!w`b}x>3ϫ|_DZ $R68~R҃I o9tjߑ'OԽh.6n;!ݧl#>yF{WYkv?pѣ{>mia^'I0#->^u>|jr߹uz9lVsgl w<~ʍNZ; !8gߵl9|`c/F jm#~cj}:t`U[;'Pgcŷ~;}P#S/;svO=1fs6";p֮+B=H鋇?}pyVX.\n~e3}ʥ6:}O:pl[o)ƙyPf 0ncy; V;eVF"2Γ.[MkVsz ݴSd}/8ex~}}O6\޷uo\;;}lׂB*ӕ;?{gӜFu-;&%2VnS7k-xM|=LV`,SnYy4pO{m.y浫9IZ6G)gO1띻-9wm Rg7NEf>|l՛v߳iVu"ZWw‘}[4)#@*,9p9vU}E,9U9Cg>#lLg9~Ğ}}zz%G>+\g'G?p!w%IXM5O&L?y)3J* !Ȼ%0,$n`D T]c#n?PrEZt&޶?7Yz|j4r0?P.]Zf-X{hK[Ƿ|ݽjb^󫌞VAN;t%ĞX]hvnR٫rMwJ٢ڌ3cojצiǫ1Ec1m\d߈3 5#4ozu yi:g3Be­lh}.35Av-8i؜-*"d1)Š;"XMEH`?Y_▱cpV)#s"Fvt3򻻭QAkL ?]8~쎅[4Gm+T7\k8})Gܯu Pd rF4>jDfoϧI ή>+udfm:M\`2Y-ĵ7yz:{0`3e5惯ҨqWi .4,,ls;hYNmVMZW:AXL1+뇴l֩k]U4r%)/l H4{@ĉycc"{.4W3s ?,f$Y_ӱYfcUy)]h޻\m1kZ5m޺aENX2SmzM'NY YN0il8w_$V cάiUSpDߝ@(:`Ȱ=OzvxOS7hԄe@V9Y8bE.=yz=A([w* +<<:33&gLأ6b(c[vE^F'OWw_TO$qѯ {oE'>O02N{n݊z(Jjwqt PvN(넸VcQ *_\P%>uo(&4"%Ԙo8zNB[)[i5dfbOzZD>PEXrPpV>~qa;KxMɹ^L>}ļ-{!+k vT߯qm:X߿䃨VM͆@X"h:(ǭfK^k\)D D" A,p"`2"Ɏg=IZ ghMP`z9νj-k}51Gګ` R-ӪryfyfUvl`IЛ8'(a8 x^PP9o-ڭhA5-;tu{O+ȶIӂl hPf.H9°>.[a2(Fʆ{v.1wWMO2Q@ de#g5("-2fKRdɗNN1 @#@}%Vݖo;yU,Q ͅ=I&9NASNS-,4Ovq+߻|Pi'F)ǐ& "="[@1˕&j&]ӻڱg/((Ҿ*+~uSKڸ RD>/#\}GҊ'k s~ie{s)[f34&bη-/L% s{reE]_NȰK&5fLܞ[zcHJچ@gޱ!+$%W6M6_kwO__7 o!KBjsjߨvF5n?/ @J,&dZ9Ʋ604-}$X[A8vWX^/% yhxMDSi5Zf*Vơȃ)tyN>=ɱw?ݛg |(9O<4nzLܳ]s>QȺ}G?O8k]␃V&">ʹ/˫=y&69!qyը# MzCMgd㖴-}i%y;G֟>  N|++Z Spx$^C+GՍH?5eRKw$iңu?ٚ;)w:2{aY~Kw-o-JRl>f~a#(2=OC*/kZUV8!Ev~[~6' Fּu1לr?ڈf%9NDirg٪I5GwV@i (П̖@IRe{3ZEpO*W/ l754bFjlЇ}nUM]_( %hFe_BTiR=FlFlΆ/jN K ?yi؆1:T&2V=LNx~*c# F*PIK&_;r1'=z>6wI_NپOc8˃K#Y}SO:ٷfR3Nɾэ -#xfԭg\lLzzIeꋧ<עu ܦ@C8ndѕQ)cW]xaEqUOЪZN=SE$ ԿIq)׵g{˵WRqΔWQ}5mn׺yܕ;/*1{$djO[zY聳>ުU ; ߸}G((?p3t--75{67:k?4׾IyBTtORE%XG/>NMyqiE};IA!Xqw_PqGSػd+J"cLyDK$qFrF/߽=Ybo?HayeUK^/%̩G ya'_=Ӆa_r:I5ؘ7ϞOx-jsj­/&DG\~Ҩ J&nE>~xƋQ wbyq+2<1yLB]+N%ߍ~ŊZU1+4nر_G)-> _ط阻Dg=yⱍa)3fWP{ 0&'FD?1 |p=6-,0IEI{?";yrj{vF%*vOQ29>:'Ν(1=V qBd&[rݨDM)-JyZsI^njb||]{=Z u<̳1I)Ov.ˣ}u+{%YoL1|;#b`mϣnn߱ i\jAaw%$g E(%b|,@ #>K.M}xw]0P:wҒ) 97?;-<ȝTkf,L~t浇VBoHJKONII}~tǎy& Nn I?-S sQя^$})ȈO%<}|mGnNX~/Ӟ?}wʡu6DYdSqO>7fLv"d}tV<3@OP'7?l_Gc٪G8{JEcJ8mflSǎb70sׄg;2m N#Iot{̘5_)tsՆ]wZ7vҮ/Y&;I_?}.AsB EbvاM"'. k2sY3}FT@~EK*"9IV++H8EӔ2"J9e1U*¾z00$Y#8J{;B%QXƾ.4M6+9-VV~aк(*XV^G4Xef #QY k! *56V) )%V@֋A(\T[ʅexQBA*(@G>8~eX%V J _a3Y]63zӫ>sћMi*/žg.*\lm2blR V$Yl(MF3xa*#A zJ*-M@JI++BwoJ$ndo) T`/H% Heh,8W6t?y<"neKwA+\ x~ {ƻт \ c> "r]'< $ gtoݶ);Z'ZPy>ԉl[#܇aFT.\9W. k,•/m=)kfˆ3\u+\|ם=~x)SMZQT݁ 0.٤VV=殫zfK+ ]S(\em{ƒf%mCaǴ;Go۝ yZbO::2 鳻;Dv{eekY/<1֦Y96SAqw!}K?5^窂:ne<{ :iۜm_6keb!ȿ VEݶvG|.GXm?kʔ n۷{뚯?<|X /΀NYm[7SѪЪwG=n|PP07_i._FE3[$58$~)kwh|r>E"\ZeD1Շ-JG+*ʙ(*#0 ݺuC=~P!22驁z%PSeJ!kKa^acrJ6\5vec us3zU/ Ait{}ji:-#(MVͫ׬Zͪ= S4I"|c[׮fu9M9q +ZPOOK,,,\WϺ=vJV/lYS'N0.!6 _XxȆe>5~`+ȑKJU}uj{+$ uڇVezZ+:0&c^Jr,[r(+eޒX//koEq5nXbҝ{S[O={ݭNWo_  |VW{^b괥!,BT* !nɱw[|e27@9\8tNʜZx!Z1Vu2a9kҼEЪ_}PRo<4Zff?~HD Uj /88cսIrU46dP)W6!Bf+ԾwVn^E߾O?ߐVuȈ޵vu7t>ӓ>ܲGh+3ekB̅ԬMĴxd'h$޺qyƚLWkty'f>;TR$ A-/$seڭ޸Zru{ ~y _?G @ a[k0ou 1Zپ4MaJUwWcrE)D |x+A5 /3+N=9O *O?G+\:aF9T&%]Z8QN6k|HtRؑeyҍ,-[> /zտn@1T|ͨѬ >ze%j2e|iYFyqɼ&?;@ ?YiڼU.D a/JBQU:rͺEoBPKH++ 6CvԽ6xWZt XC 3,?bk]HZW:8IY~ OD SQO?9*rVő3b͉ZsT  AȟjA( ##:}8 )W,,D9'N.NZnնFvCE*[TbӸ>UwP~ +%|O, pyu>sgs\iZ# (!x *IFdj(HyʻBEZ'@52"yVkP)LN|-#%ݧ\2){!wl@ ;[kZ$P9(6rP&9h}~({\6Z@ '-pޡ[|x'"htB@ ȿw!@ D@  @ !@@-@ jA@ .P B wZ@  ԂȿYY5M&d0fA  =~tSs=Ql&cMuPlYe>Zg'w^DP ~ӓ tQ=ʢK$#5_E=0[ i"IZ''$IEӒj+A..}h!(V^|i#.<|Xk֌NNQb ] eI$akFOTuG^?@zEV4=lzy*RV%W϶ !xf-תkM(ZXh^ B {GZ?{h8|䈿`` 󉉉1cDFFo޶C Ëݼ1^}gϥg:dY/Զnc7rZг P'QXoYV]w7 ʌ̡*g-Yl}&qu;s4@ ُvA+uɣ{wG/2nltTHHVq[_,<lj3Ȳ(pd$sVI_#(2(٭Ym Z m U`eёBd#@ V J[h:w]~W/_xKaW.9zΝ[TPO @T1lR~;9Y׀8ZB=A.a9 TTV{ʹLo`k@BH(`(ֱ}ӧOWTi4M]\\Lsf_ cekuqL6hճG=e2oc7:hª=ԟ|n΅F6m574Ɲs,ޱz؃Giqkl'|}~FJohyKSL#0)>ejxAyXL%%NRUᢙ1Ǿ,V{'e\V?N飵Y{s@&mͻ1|ӧEWwȼsͶ_y֣ǂ:aaMr5Wqǣ>{̘LhuW$*Ê׬נNzDԽtP ԩZJMXX9VV Mf֫lj>U [وz [~ڵ+ZJCVԮWncyו T9vúk`VVRgYBWNzJoDueU.)>V U,E 5խ[n%]6VU^AAޥk4lR$$U,KI]v/uH;Yx: ˕@죻^# BjkW+Ƞ2xRL@.ժR^ ](e' -۴[[/of&eId3g+A )FFFT@aܥҥKx~~HE,&IʍGtIrɑKR nߐhwB!:L.܈|E"?6[/ 9 rUsb[G %V,XsR$ (}GO.W6P*nwPU/OŬ'9ǵ쨠OT;,>xok8 glRfJ 8{ @ BޥuOFKA߱kw5o(!x{hǮ)TN..$4rM o}SXL&Ӭq&,Z{kL~3g:u.nǀҒ[F ˓K-xvݢƍ0sjwDAXr,ޢiC&~ciƥ͇w,[0gZ~m kŰ֋ׯ<]ꀄe ;lX}!EsQ cE8+W`_ծiVlQs"sb$FTp=eE !4/0i+xs@$ |3MKFWBlЩUP>96}z%%)eIVmحntc'5 Rm }qT DlЛoP. ?u xYGtb\ܭW__P9G~zsnYk2Zx6>+\>[udEyK6wfO5b!JI{4fs6֮V~pʜr%[=8{K/ϛ:|cAeĢ'}hG~Z젆b1 `򄅫Gvj ֥m5]478՜هWE(IqncVEߊl\d໏Ny˺̭8{҈ݓ>t솄FG@-7cE FDTu-\G1KU@p$FRAO_7ڗ^/M5bR#zo@i'ð7Ȩ `2,Ìx\w~ǀb8v̹u.]zŊ{0::zƍٳQQ$sx3.Qz*o.D^0A5M5RmAH77ANRK]DtؒuBy([󑮙W_itO" qW@dFR$HMRQŊ&77T@9@Kq\]L-ZN>'`*'ީѡ; l]4tsEʮ7hFX}n:VoW1itd^nRex TߥN 4Ѥz5Q #TZ'D&?A~E}ȴB230[._C@g*ލ,!Ӭn:W#fZMHαEI I~a!2?g_dT$SXԨ֕9EF$! =G2"E:}}ʎN!N[h%5NDҺu=}Aft4*m\a]^%k=so6iPCmI/0pe y|,]07'7q._Rk^aB:2 >:D3٨oToI앐'\WoNSk}Fd׏2*o{^Kv7ZJvE(>ˎ~f~T/OK=k蕔IێW~XgęۏΎnSK+= yԅC\re9:x1zhd9NU:.w(B[5gKY+rG1aXvOQTxJ\3<ݸdy.b$T8 ω8VuvqS8xDe,I*WZ<"oF> I"P^4fR@5&q8夏Zo*oTľg[BۻĐ +`5 5*Z^׮ K'CL@žFȱ&+۷qIC6cQ5:J\4o˸lI{x`Q\<{!BG}i}~^iZ&99e?_b+_tӖmBRqqwr(v#U1qswi\t #H'WBjMW5l8م RiBužh]|=gC#h< _9wBP97Q,Pp[ A"[*:[s(*5HrV)8H=t un PB B 0,/Ix1ژ~ 0Ua ;y >Rujm|rzjv~s NU~I.Y>?5g_S>[3J߭xPjANhشƮ[;zz=m~Ēl}j2Z!돍ٽz{dž\Y pX~Skn3vض5pJ_mOvFKxv5eex\E>;ڤqi ܸAU,[KK+uNj4Q[Akjj͹e::9]eWe'jdѨjU 95{:UCBtSw'\擰1ZẄx+.1-83K,_b.I35{ywbjVX6/!(ے30iW_E8jT! aTbzkd*+p~bǍIObrYr 1<<<cI**7"陳sZ^\tˤB5D'2*]ds=Lz'լ_YةO$5,ܺĵfАW6sݞ"FX|H2%{֮Q"-(j9?D} nk=(ժn@ v >%|?4UENkP+/nq1?Ӹ"Ֆ(N>I*M(gm,oQZsRjpzlV0VQ$ȒD0$Qk(F^ ye,!2Sja9EHMa(ZXP[ Ԃ;l@ {kAւ`G$A1HP B @ '$l!ATx>#3;yjÔnI#:M9AFA~oJEMe @ 6pZ+>@ (j(TA}׫\Xܭs{Z ϪD"P+"RA] ! ,"B(ϩWLf(cJV\PFVM~jr;ZyZ=EvmarssSR^TVAPHoܴe D9Z>Кj:M8!nоWR̓4ZcK}3]m({=>ٰy o4 wЁ@ FAzP'61!ЎA8$q\ttt]EI*v@LT^NuӀOXN7J_)o7DW˜ f_lnGvŋ:)vam@L^9Aig '?E~U&/(y@Abʕ1zւ~+ 6+pFdn1e Ϭ\N\h +*H+d@IvAw vG"JRT@@ɸG$5WNkڭ'Yy6nusUɜLשYT)w:7%K$0٫TEXl1[Yy3\jjɻ~f8<~R>+]T`t)Z%L 7:; xwf|-*_y:Tߑ3>YrB4)G*iصN:/3Fs)[:? ;~/Ԩߐӗ 'ڎ_};aĠVC;o f'.%e5Y9l)[(=:ř^ezϾ6QsNk$U^:c̨~]Rk83k.Έɳ;^{~jf>5#%Z6 M[ofFˑQ~bc(|? AH]biK@oo\3ͬZe hiղt>qŤ2bJtŸY[6l\\~ӧ7z6sOtEG-sʾ͝غݫ׬]H֟.moiᚯ;,62YOxw刦`!dљ w'W]vEϋD0e.2€T$Y0>HfU"?~IQ QTQ"'hұcuM^f3pdD(Wx`Ü!3f¸>ߖkAnJ;4Y($ vC?=v؏m_YA2AYw.dl[fCL+ho Jt-ي\ϵK@rB5ų@@?aG'ć9Or1[׮qKM+dYUzNnvFZ|Q]Hϭd(v3>*)Gw "8vvM+eb[ne6uQqdebX83Z yi/=Yvʕ+kZhvrr_羃XJ,Ӡӛ:7VjK4s=DW9vzU7_tj/*ZZpBѪUg{dž僠OQ3pv+)Z0pZQ9f$h$7B~ ͼpf]?Qltty6}BͣRNoF9d./S}kyW$ hNE_RIQaǮ=оsޕs3 DS&% ZQSJQ`JPjF %F<@hJ:kSf<ԟp/.5˳A`%.SdyUB(!? {֔r Kuw|qBd9ՔPB^‹o&.u:uCՕ*Ԫ\~zYMRl ͤO}y'FӠq*^7Oym} ʅQ1ϊxGesыظ$VPv@1dK==b+/v]#8;(R|=93yrˇ|3i&yEƦOsƭF^O{"ŋb/zVBᶼ1fZIG ʵWb33IED'XقsYGyk棨[y-" L ň=}p;E~ރ/r޿|F|nF-.-3"FA@ 5e e܏؁ @B8S4 N1Kq ѫWrBzzү-HvчZ4,#FQ}Gq=BL|bRc٪Ecg[<+;|ְ:m΃{4eGcFҹ78&'?YS@ ?ûigoj[;?x!zV%.=Q.){);%Wѓ54hTǙQʻR>IgϻVɏ;U2S[E"zЍ$ѳTr'W>澱ŐJQ7W F䖛kMYIe[1~;!uT. ? @ _]hAP*e?R+*MqVK4R n+@ (jbF%IZm˷oߪmuk/闝k(*"x[}Y^Qח۔De/@9hEIDUD dِI8IIo" &}' 6\$4oǛ "ˢ֫b#]g(J4Ma]mfsGU41ѝ;UDcB}V@>߿PWkʡjNe[@Z^$T4+1| ƽb7eˢbB0x||Zo|Bp!sr7?n -le/yYf뀀<}lpZ4EdZ`ӖՉJ2_V""Zy"_ls|ïIշY&ú3;Zޖo$ݽxw7x-9ȫԕ^Hqlk6ýKQ?P`,3׉lId=7^\< ņC@UCć8N$q5Vvww4䠩hy[""0< %j{3PY#Sh 6U0@l+ߊXQ|Kʒ~(v+^?MoeTyWVDZXhDQVkwC"x༨d[%(F AX{8J۾S%D& ":f E"gVUXo 3ٮ%NJfxPoMI%"#C$G[X8\YHKdd^Rdh5h+r\7ط@ !8@ԮUɓ'$=SEjjy6'ֻŸ^q7Ń%jxeחAZՇbOl@2_D̩kOSkVw/D}|FrQ: Ƴ˷#maD{@I;u;OVo |ݨ{w @:%@~%!Gg73YmV 7-#{1>CvnWZv}Tl(87Y[Գo2GcOՆggr_8l{+k4?{Uגh3o|:H6M[n37bܹa!|.'463'wF&}N5]q/YxY%hR=X7Eraf +2k/vt/œ\M vYv`_WkS¿u ݈sZLeמ<76ښOl`iw|Vte(!jANg6:Az !ȣ;>U::bss3XU:\wtʲV=Ҳk_Yv>sUoW֌a3h>oŌ_תQmqug~Ý)ck [-jn֬ʫ1zֆolXvAG( d-ˆ_]ޫMV?_v^YdMkkQSҷxq*Q>0W2dCAc؟-Ν[Zgej/R^Otxٮ~vjOKT${Nq^Zu RϕZ] M9q:3ݢ/0[곶lٰ.*BV[OEihPvm=vtZLYes캕6jTISWxjsݪjҕ]W m\^8]Z07M|x`] ֯aڅ }iv^N}cˎ^ԑl۠V&->Mi ȿ!:_Qi $A䗐EA ضM뎁skwȖ=++sjqڻ )ʿӳ{p" (nx=|Q~W؛yg]vLn9C,WIbgP埬Fǰt;;+߼"cψ([}$q4dʰh[S;RrcFCGҩ>hZ_4XeaЬ{Tw ="nڃ;feH~:2ǖ}B&`o)tT!=ܫ__awj+x6[mޡIB{vR?Fxjį?RYFݺB ~~t)8~}nۮ'Q_a*F' U7@5{o-ؔo6d]9}ʽh2MtIt"P{016=,E8#2gaE߯}p~3,8m v@pr6۩u64rVrt X3_k,ˢد"$Qq#uPDPH\$ 2]?ۍlٽ cId޵lfwb.nX̖՛vaU#> %]1'EƏFÖf->RcseJ߬F(C*0Rpu!غkPx ɑC3WkRcUGPoGoFA*8Cݧ!\tm573˯pV?ۥSS-Jͫ`l5atF'kX%~a![#-Pb]U! BFXk?-9Q;sZ`19YPe ')4C'$B. ]?_$ɤrh;Z,yyzt߽A}[~ 19\d$CP*/_?4Wݻ\޻US+C#j fƻ9:uyc%Sb+{c@N{urY;/6$W}6ztߧǰW|բ b{g2_(SM6 z L1E"-S)#/Du[-Gn:hnc9-1(2EBV$yΰ.H˝,}/8s\HQ 3h@-rYEo\a!:ѻs]wrՈ1o(XXŒUqqa[ڳSVcCU9>6Ky˂޴ rmy[ޅ )_7\hg߁Ĥ䨨(I3شiSIV Q(_'8IӸG][}w)#p YT}b'p~n#{40%6mDTE޲LJtf>Q:MfLMT G {dǛ;Rn^r_3 S+^xq̗u~tMC]xQyTMn(_h۩~ LXz}/~әMUX-T5M(;ҕo|'T,*ưWo.!ȖnfP5ʆªS2͓2Qd4K1:cO1KIA>DPj v}6 MH)]f-Otvױ(˵Y4amzޘ\D 1ڏ&kn{|Eݍ/bV4P?홢$3UUeXzIRwll֭ C"Ж|b0 "ϡ_oA|O|W:thٲjeϑ]:w/DY>btfk^e]ۧ떺nھw/?l}c.=uI,HaqRT8t C9krOx0Ʃջqϼn >0ꪌʾ>bG+p;={mSQw)Z?o–K6lݻK1~h?5[2A+F|xg#5.<|͡Ϋtk$ wWxQpzlybC1.eb12ME|ɢ qKm,T%j3}Y1ƯL8^7E:GE±OQwrgVIB0;E[mge^!\5V;VTѿuq6u9l{%Dy>cn1zF>|aiD+iԎC(PEyHf6Q'PlBN5wP &*&au'HTQhr/Ij6>P!P*Tv9%Sp9ϭ4TLmL mGA_Pfk,f̂_J:밡8MQUFS 4NӺ6Ilfʜpi:YP-2Q|[[6nVз,) g4P]bX ss7]XU7/[?h5#\uͷ^7FOMBoz]rE|u<ìzO˷ ._,(qeQ!<{>ξ=/pPUtn wէ~٫_>1*~%${6́Ol~ku=;߽CΓ*۵ɴ0/M0EzƙQ6BNFeh`U\c$ڼeUEUjCKNj@" qצs{[_}Z }&>nX>=S|&3ptyu??{H IE4eZ~z۷.y7^}飥oogcXDW_ ɢ9nbL .8H$5(U244wɮ?TXɱ%Tɏɞ6z=u M;)RQIujƸ8,smx8iҒe$3y/ssy_?ngQļ9Y'iok7|K`Zg%j_/yekڠǟ|I?;_|}4ϐV2 wkqqe(3g'd3{rswB{5h1w"M_Zf3JH=)Çsx3֛o}qncSRҊKKN2 sAAڠj":mf׵ܥ_6_Ӹ_qM/QcmؿGzt0lԺJB]?G$XXUr$ٍ ӹiqq-Ԟز*U?kS’֙$c0ʲdeƍkL.K${{{KZΛ>xg^gDIo9IU] xA0.p3J2vv\QV{ AT M⊪saH'xo-&mPC4Itkxu ;^߰u-o/,jP~Ѩ\(jr#ND'@yxD(tFW^3=2sA\ODPؒ%d0OHJh8%-MҒ@ p{BXIǾrOp@UT]ɸE@,a23) >A) l+fD6HCRd1W 6aMu8N8#k˜زWٱk#;$a$ЪY#Z]61u3YKo]ާb/rK[{XpC/g)K>^]2-k\ZӢ(8M'lNӌ4q^`eAQִE˧{j=Ul4Ejlf-鸮֖-ZEEG$ʭ M% ȮCTZ88 ]>MH&)|"K&[FZ8IpWUktG4&)ȁ#f-L|.hA$au]tWZU/} l %{䉲,&}iSǮx0սeך"iX'M`W[{}8sךpYX?Xϸș 3TEU3{3ݫa虑4%|'&uUtںA]G +IF+B j8VYѼMQ $*!KCtT,q})7p#92Q bМtHhiT4zv Z)"8 ~[UKVRXpD~j?Aq0MF Q곢)-AZqO IcgF>]4ApAO= CwICdAK#gE?tXȂ\lT @ybL"p+ EOS?$1|?Ȃ\\TDqSN_ ?#tAtAtAtAt,뺪(4~npq8הINJM_M{8Ȣd2']"):I$ɒBIk:MSƕx4URTB)~?8\S?fAESgΙۯ_M6FFE/ym[,[? PS)lBAo#RRQX<ݲd18m߮\58P4v?K(Ɋscci85ET0mf_cħ;mHKJhzj8ՁuJ޾No$p]k=eLy9Xm59QlhGbhLeu 0aZv&hewv7['H#6kQyiNwBvkj; F฻dޭwq .M"#F\3^E?įUKB2?wm;wn۱77MM8RةK `vҩC6 8IuUؚvҩS:%8R}gBAi89>A:o<$4%L65'GgjO DTˎ$jSlzݖҡ]N3IV8T'lJ֝Q;;vhbĠ?IΝ;wܩIǒomD{\n=2"tETE4Z%~̑ܡC8eM 6KHnܦSNYҢILD4ٚjj" #Y_(J&MR}!î&H,s1cKo?ӏ7_%!8xbϗ+vReUI]4zjէ4%-nu˭r2<{śn5ݛ n{ݦUmG7\rveYJ6-$Bk;5`;Yt~)]~WaƍC0Pd>z nv>RYM$5̱B;:yUW3Mmcm~%K"D11e̝ {\5eukhTӟ==7fXJ=I-[6V۾}RW|9뤱5I~#뎝 C*;._li-d|՗[Mn}5cxm޴^4tY;ϸﻍ;sSS*Uٸ}o|vǽw>$MS?;cÉswSeJ8>'L]4{2^32DZf;o⛟|fDfִg:v>Ę^"om䐼o=T|$t@,=׳?Z+=j 1%隹 i~EwO{bFQ>xߓ ZZy؎W܁*gsQP^_@L:.zg.ߣ֧vİ /윷n_ޛިmzFȫ'J~摹=r4)y84w|uϢ[#+5,N3ﻼW}8H>y LJ֗}Fbx4I f^jG~|. 5ڌGxn$E>'beeEllGKDaWT"L\K;K(c$cS8LA&+% %z+kk9I碚:bӲf`&  BW5-pZox3[]ǽZMM)a'I@|gG0՗G_~]ښ'k="tt]e99ǟ{Q _pǖPX(} AQ*z8ch%"n78e2dH2'TrK_Y_v24Fq$ ]u]dYIc72ڸytXj+sa$ڦ 7<ڢ+e@tEMƻ@d ð8Az,'zޜ琍K Ԕ)&̜>fLwH"cA&-,. A;Y?t9 CM$Q ɲLunHߢM[5Ms{ceְgvS<.oAEUEjV}΃fTYJ\$+?ШkP GT"5J?aɶ֤wSjNS,K(z%Z12Lxc] 鈯g!`^9r%NQYgLQ^LNwnmJ+ ,HD]mi2)3gc򬹝w/**+?ѬC:uڱL0q6MD0&byk^1y1v1#:4Y, uV"ĄmޗjM[y;>L"}רUׁ34`-f3%)T,lŰ͋;y/*ŰKoA̺:Z)h1յiN#Y *|͚eNy}m_o9Ы dai966S˚NS^H`߿8{_}1A{8A۰wݢ~Q_(S'b~6 ptE[':&VӌkAda~5eY屃*Ĉ326M8Kq$;;Ϗ$qŹ}v \*?]ɭlq$0wdޒZBb屽{ךSh[3[\^)#FAPUՙӧpg1 e˖w?ayH]WT01 }zf 6 %Q}Ng"_,Uy4a(\堗 W7\WOee\tQTG~J8SrAGL&\D2 i$*c6Q8I⌙0Y PN2Nh&>(SB ;u[9CM16R m"Y72+XCSЖPSEGM+K[ QLhhP9Yg,&>* hEMa- %sjJ0,KDSub-v@TͥQ;i@&5^due41\q w( h&Rξ551:8Mӕ?|YYٳ;w\rdkNS̈W}h߅W٭s1E-.N?Yʓ?6;g;j'-|{;Cis~|ſtݎ}|phX}{`,0 u#D]~RAdlBA3K8&AV3kZ؇rAU? $cFKG B%rSSP27&MV klh}SYcthfd,Z i1ѨNKf+f(ИtQ-hUl/$)IR0D툢/VUU3W^+ 5Xpm m/ŜٟWaޣykrsfZ6NQLzx@l ϯ69_䵍ŶtGxM\Y ڰ{횥m^i.F j.(++3LsAS~u0B4JrUoKŖ7-8^pt]uQ'_VW)g|s9AҌqL»z1=/~mXs?_ԕK:IGκa=˪lp,(fy֬Y)))\sMLL$"2tLQgX5IDAT{p_Ѓwϻw-啯 ,Mk \]Y8gc&N4]GW_ )A91/?85f+IoՊ\2cG=ַ^{YHT]h~Ȃ礣84gΜ,AxC`Ll!:iunui6F`PŪ-~ck4q.4Bw~`Z4Q4 A[t]E9Ȓ>ٸ`5ALcp0ZQ3%P1$[v+uY˟ʂ(V{ MJ?!nشո]% ?UU.>b  pq Y`YYYYK| ȋZc]>?.q~N}Z045fy<43y>*AA 몪j<7ED Uz ,pA^U EES=iFrMK- o3J u8/M %Cohҳhu4/oW 30#Z%'&*8h'Gb=tf>ŤY1猃}3.|ְq<9ၧF4K hm=3fZ c Q${zfYKkSbY+(xZlT[$ &v` N~@I0t"AAF/ b#9hs@'y_DH?xVDi$ %-jģ*#41##yYK7zqUpE-?!H0q?QD.Tl-QFxQ9AF! !@0()\PI)g#{#eµ:D4 eU9>Ͱk{q+e[_|ϋY@,"*hgtdJըFr Յ"񨏅x U?=f$piωu;cj9}|׫((桕@=&)Gn|*3U`F rh ^OÀfE3y>mD P" R ּsr G902Û 11+*j-ͪ;wWQK>GOўh" OAM2ڏ>v71M_4$5.8t< xhLTˬ5ήu) zΖC<֢ă_|Y2708-e{Ҹ(i=r1/im%\^cUz3"=еjB&ꎍ9(vQWm؟sđ2~-"+ĖCA]% Yj/lh3aLsx/)׾~[inaؾwF8%)5d!]o-~=h_ՆZT,խ'Ξ?neN%ѮڸmGx{6-ݽS˼nvڤ^iѬk U:Zi;`V=RN9;gP~өz߽.&(T /ϝyܧ'>ػl (1*2AP]SeuE̬?tӕo-s-Di-kT_zk,,벬46wƝ{#[wq2٪Ss\ü垹3~Y>ݓJkW'3k |)UEq4Ԧ[|L2iM7e'/Vw>S奇}ɶy3z'K2ObPsVxj!5BI`aްE Mhަ\qROY?1u;ϸco* j3uz+ f?Fs ifo|.ں6I]i.^E UA =MnkYQ#d}ԶKFgYQ8ia/ߺhmy:.G~Gڱ͟;cash -&NLWOrܰ=7[X }8I(KxG̯k`x|#%)&c3~h1FE69g·P]{4e-;ͦPlyeb}w޶qἮ>kѼM\L0yz|-3\Ӏ%Q? $wY)߼GˑjJ=uQ -ңޝֺT#vvkuk UQqݾU$PxPmEl1oOĵp9Mc;um]6KW~e|7J2'6%R]SU :6RRkng[&G];Ia(ix<(-V3^8#V9aڈ-p{&1'zZǹL$ꠛ9U5~a';qh[ieW7X"Xlf(X-$f"MW#[*ϫ5[GTTq3*-#Oe0upjB6dhPfԠ|^vFmO)hNwAZJa=))A՜<Le$ߎ7nk46_X|'lXL +Qrj>j+:vu`lc*/,R\(Ćj d2cX(K HoO))J[K4jHhαr Aϴ;Nqf_Ґ6uV{}E5f6IulPޜmD*"X }y/- 2xi:K'̞ҹ+($ŚYk"H\yrwfVG$%k l4eVA貵uG}+޷[QM3xZQtkEҸRis>I9Zd45b SGǚwo2e窭IU=৚ dA.M8EyydLtJ E%UάViDQfKuy9zlݲǗo3ф(ʼ剙IM26>aktYZMn ]Ǭ& $a=huIO]3kSF GPsmLKuʮ4"IX΁ÇswzwZLȄqcv(JO=x`=|ؓBX>99Gvo^;'jBWeQFJ:AZZYЌq˓"+8}0xyf2V-JxRYuO K9XѕKڔpw.> GˡХn?Xޭ{RȞ5L]#͵ E t ẽ>O5NEJ9)h?z[1Fk}72a윜C|'U$4}i(:|`ܼmX\(}*/TQʫag7Ȃ\pR OZ[MwD^$9IFun+j*mΈw&(9-B,.wfMqe7?ݨD$ J潚ϧ7S1ha-<RP# (q ӳ_]1JA(4'_x^OjCqW]܋Bc+l-k}c(*YٛsXU_t堍qSh$5I'؄qD$xܗs #$ެsBo=xVSt9_}fs^MdEb 5u۲=[uaFO}gyy^+T) טXJYLkK"Fuoנ>_\ѢC.x)69-9<ۛ7HA$7M(ƤXQ7% YWFZ=>AEʧQD91tI<׼ma&iQ{G8 zlb.HE>3&T{(؃ޜP1F/J0EBr]*4HQ9BŤ&(x  dA.UE-b1oj!NOf 4y|ʝd;fMy)_6<)pb+قC'tXWtX) b1?_1H?|F0fdK\aLfKG4|XZhĠB!2NUxm_>J0c\pS Z[R8aGM1ix[R'ļ[c DŲەQO:qI{1YƾT" Wu3ex(@bTҠaF߽M3Vnc=~O\S*[phhĨS+5ʊ"Ř͌1ǚ(gFeM5k҈kw`ʭ3!֗;$qIH)(Hc.b&BP&,<=a-uMD!ˎn^[SO1 r\?įɑ,TR f~(p{D\SE <$^No/]Sp[\UqR(WtI,j.i4UxMtx-ZH& HP8WsT]Tؔ8GD:ؓK-qCHu5x ,%2"5-ڊ)+ё 1 WVG$"@b*$֕x*Um (ಷ"jFtER-IVa%¸h_yU'qU1)qNweyGKMJQk4UHMqb9i z|*Jx)ѱ1R]OE%'Fvs'k !":3(SLDL U[Y#3&ոINRB:eŕAGj݂手wKxco2nu25# rqQr::5gbBKaڋ4ZTGIrյ>1&SXjSQVEQƘ$d2Y#X[dU)pc5>9M=ZIbLL8;*z160M؆)/)q;⣅~ Q1{BF@A[m9RI IqijIY_o[b#%~I|>^uEe#Ғbq\6V1{\R|U2F 9>п&,̂qF1VS7Q‡:/Z(| MT=;$@EOVM39*nRgRP1Fypb1RZ(0ڨ5-a 5x֖Xt6T5i^'̨p+&183õ9:8JXPhb.'tf0P4=P4vf? UcOUաQ#553/95FYu zf7dQN?pޠ7GˢW.P=BEQ%x"!$ڬ.BsGC[M]S]W̩ȅTB@eU״3WL%:/;PPu]*jķZZv?>es FҪqpX]*W5v(#F~)8Q-+kG $cRױ0a>~Z ?т^hTf;"#jDcfTfh zy-43z)ԀxTNMhqA]T4хQ8eY4Y'/?[hl5 ##eOġ9C^a72KkO°N='qM]DֶKgڂČ8"u |98LFUQ>'! zlbphh4Oz50PG_d4A ؠ.eƃ3 P"ȹ3#̻Yq#tVn_ltzBATc/B?]fC$ۈ%JT<-B e|Bf蛤bf ej6VL`TQX7X:#V"z8??73M0(ccy?Xn#TG+ۻØ#½Q͟c|{:P7/ <_i@72qw:vhg߇5z1i3њ죋rS2]|ޓџ&Fq.qf\טF1gvK5jO[ģMl$fH"rw{c(}Aj{9EG+TaI$34nT bvViW wj,ZB zQ@Fk+qnQ`Df"{*kco:`ELh|OwAmv5PUc:c˝7Q"YKac.tzsK/m@8(b.NMs렑PH;Y|ik~xØ԰Nz+[cXUJ>#a &o֦#R8^H[D,=ES,1kA L|}sS5mm~b.(}\hZo.-tK7U֫ _*f#(rRÔgW#E:<؆jKct;;^b؀TM7v}7tɓ߼G $.x%g',Z16&b@ٖ~\N/͵-=IfKA []{ټx-͡w$r REG)-4=Wz.<*}j &6tR$;:dB:$* T((N]Sm{>_qm636+MLL&GNlكĴϖ6c [ĕu['W^̕QSQO5&( v7蘆&+7" j1FLݼnt@/E-$x j81̒M x nW)_9ZpIJyu$eWl1'vE7Kq3--&lfm~O]V@jN YI-چW;WX/5qn0ucXRج2Jz"xrH~R";5 7"~Əqw3QNׇcntQ LC4'8p\ь aS,yP̤n"?AjMuZ4I=(V+dvD҂(X9 U1- _pR"xIb Mì'})-$$c,7*IwAӃ{ts8#EESFӯeIe6b0EcYw}+T(oa%TNe0C:G@4U(\hh"Mڣ $auAjnMJð<=PX,|{B>.,JP.]2 fu+M䬪Y^(_)e6rq~)~שKK5~T#q)[&z y~, }֣!Uޯ#Ԓ 3Dsb:_ˮpSxkQNMMGɊFy|xxz+OC J8U"iLfZLl-UVk[=wZ6wT0D u8NcAk4HǢB)@g:X4k <:`:Z]fqFp\Bp巸fC2hδ'[#nu~K;#~O4/K+k2 o}SsJ O  $R Wb艑swGٶzhQ| #>Ђo} Π|Yz0D6W<]Z91!<~.f_7GNa'ǃ:F8>ʅ%Q:fjÿҧUC#wvڷzz׽)/j2jwd&mcmXߚ[Jw 3K튖noc!/6~ԓ!J:uBLK+K6VZ7Gnom=w7 r):ķ+lW̖¾Y5 ePLQv*Q^m'<;Wo߬i+;|fnV_smgzNL|j@5FeE,\6OJ-񠊥3i~}T&sDшJoݧF]MG$:׬Z&mDt&W$HQ}-垐^;ȠENL]w7r?r@MmJǻ|cٚ*ʑW%5jMVBl ,8Ai~ucHID's|%Vdr}l[S-wDMnr "[1=)o܏~Ɉ"ۋ'&͟(?Yӫ9<3ۑm䓅kq'mɷyTQ24N+=a{Hm{K7R+G޼d(ҡ-513e*5jsY!Vh33za z\%?˵6xf~g]38IcN9>;`Yԩr_=}KnTW0p{D\SEmQLMM/?NR}ʸ7].F~Pd= _84>w,OU;VwJc(q(JWu+9qt`f+nưKF2bv;b:A5rYp\NWDѸ /th5(9Wmw2 neqE1.nV_ӱ8^ *DiTBÍl *XE'Ţn X .j^I,Ҋ4]wFT}/XXY۴mWQQۑ5SO cΜUW4cW=EV8Qu ao@Uh*MZ$ w0[1NV;>LU1n#A N>0 \ -ZeKT2:WE[)@YQ>0KvǨUTIUg4OP5R]F%v:植Qٳ[{&ʡ   Ay8$=u':szL _˂ு,?,WecE.58Kc^]9iq\7,N18h!bfz'ѥ_Y7+дq=ppBR@b24 dA..! "hp)9:꧂eNXRA}~p8f3IPשx# Ȃ\\ZDЂ*Pa$p~8AkǮ=R3{@bDNtAtAtAtAtAtA"W,'q$.4Ȃ\|t]YYA.p'܃$I%2$ެ.Ȃ\4M "))a/H*kBdA.RDh ʅp^1  // ?׷`ÜIENDB`backintime-1.5.2/common/doc-dev/3_How_to_set_up_openssh_server_for_ssh_unit_tests.md000066400000000000000000000152331465446530500311710ustar00rootroot00000000000000# How to set up a local `openssh-server` to enable ssh unit tests # Motivation `ssh`-based unit tests are skipped in `make test` when no local ssh server is configured and running. In order to execute also run all ssh unit tests in the `common` folder via `make test` the `openssh` server must be **installed on your local machine** and a public/private key must be set up for password-less connections. This document describes the required steps. # How the unit tests access the ssh server `ssh`-based unit tests use the [`common/test/generic.py`](https://github.com/bit-team/backintime/blob/f801b14a98f9a442008a5f514eec98e1b2d7e29a/common/test/generic.py) to check and establish a ssh connection to the ssh server via this code: https://github.com/bit-team/backintime/blob/f801b14a98f9a442008a5f514eec98e1b2d7e29a/common/test/generic.py#L43-L72 The code implements the following logic: 1. Check if a `sshd` process is running on the local machine 2. Check if a public key file `~/.ssh/id_rsa.pub` exists for the local user 3. Check if the file `~/.ssh/authorized_keys` exists (contains all public keys that are authorized to log in to the local ssh server) 4. Check if `authorized_keys` contains the public key of the local user (file `id_rsa.pub`) 5. Check that the ssh port 22 at localhost is available (= ssh server running at the standard IP port) If all checks succeed the global variable `LOCAL_SSH` is set to `True` (and this variable us used then to skip ssh-based unit tests). # Installation This installation is based on Ubuntu 20.04. If you are using another distro please consult the documentation of your distro when following this installation guide for required changes. 1. Open a terminal 1. Install openssh-server ```commandline sudo apt update sudo apt install openssh-server ``` 1. Edit the config file to make these changes ```commandline sudo nano /etc/ssh/sshd_config ``` Disable root login by changing this property to: ``` PermitRootLogin no ``` 1. Restart the `sshd` ```commandline sudo systemctl restart sshd ``` 1. Authorize sshd logins with a public/private key pair Check if you already have a key pair: ``` ls -l ~/.ssh/id_rsa ``` If no `id_rsa` file exists create a new public/private key: ```commandline ssh-keygen -t rsa -b 4096 # saves in ~/.ssh/id_rsa and id_rsa.pub by default # Enter and a remember a passphrase to protect your private key! ``` Now copy the public key to the ssh server's `autorized_keys` file: ```commandline ssh-copy-id -i ~/.ssh/id_rsa.pub username@localhost ``` 1. Run the BiT unit tests to check if ssh tests do work now ```commandline cd common make test ``` You shouldn't see skipped ssh tests now (indicated with an "s" instead of a dot). # Optionally configure your local firewall to restrict ssh access **_WARNING_: Do (re)configure and enable your firewall only if you are sure you know exactly what you are doing. Otherwise you can lock your computer from any network and internet access and as worst-case even the IP-based communication to your daemon processes!** If you have installed a firewall like `ufw` you can check the current settings with ```commandline $ sudo ufw status verbose Status: inactive # Activate the firewall (if shown as "inactive") $ sudo ufw enable Firewall is active and enabled on system startup ``` To restrict logins on your `openssh` server to addresses from your local network you first have to find out your IP address and add the restricted IP address range to the allowed connections: ```commandline # show IP address of your computer ip addr show # Allow the IP range of your local network (of the correct network adapter) sudo ufw allow from 192.168.178.12/24 to any port 22 # Check the firewall status $ sudo ufw status verbose Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), deny (routed) New profiles: skip To Action From -- ------ ---- 22/tcp ALLOW IN Anywhere 22 ALLOW IN 192.168.178.0/24 22/tcp (v6) ALLOW IN Anywhere (v6) # Disallow ssh access to port 22 for everyone else now... # Show active firewall rules: $ sudo ufw status numbered Status: active To Action From -- ------ ---- [ 1] 22/tcp ALLOW IN Anywhere [ 2] 22 ALLOW IN 192.168.178.0/24 [ 3] 22/tcp (v6) ALLOW IN Anywhere (v6) # Delete the rule which allows Port 22 access for everyone "from anywhere" (here: Rule #1) sudo ufw delete 1 # Show remaining rules to determine IP6 rule to be deleted too $ sudo ufw status numbered Status: active To Action From -- ------ ---- [ 1] 22 ALLOW IN 192.168.178.0/24 [ 2] 22/tcp (v6) ALLOW IN Anywhere (v6) # Delete rule #2 which allows port 22 access (IPv6) from anywhere: sudo ufw delete 2 # Now check if the remaining rule(s) are plausible: $ sudo ufw status verbose Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), deny (routed) New profiles: skip To Action From -- ------ ---- 22 ALLOW IN 192.168.178.0/24 ``` Finally run the unit tests again to make sure the firewall is working correctly (= not blocking ssh traffic to localhost): ```commandline cd common make test ``` # FAQ ## How can I temporarily disable the ssh unit tests since they consume too much time Just kill the sshd process (works until you restart your computer): ```commandline # Find the process number of the sshd daemon $ ps aux | grep -i sshd root 202345 0.0 0.0 12184 7076 ? Ss 23:25 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups # Kill the daemon ;-) sudo kill 202345 ``` ## How can I permanently enable or disable the ssh server (sshd)? To disable the `sshd` even when you reboot use: ```commandline sudo systemctl disable sshd.service # Requires a restart or killing the sshd process to become effective ``` To enable the `sshd` when booting use: ``` # no typo, the service is named without a "d" !!! # See also: https://askubuntu.com/questions/978852/enabling-and-disabling-sshd-at-boot-via-systemd sudo systemctl enable ssh.service # Restart sshd now to avoid a reboot sudo systemctl restart sshd ``` ## How can I find out if my ssh server (sshd) is running? ```commandline sudo systemctl status sshd ``` backintime-1.5.2/common/doc-dev/4_Control_files_usage_(locks_flocks_logs_and_others).md000066400000000000000000001034041465446530500313160ustar00rootroot00000000000000# Usage of control files in _Back In Time_ (developer documentation) Table of contents: * [TLDR ;-)](#tldr--) * [_Back In Time_ commands that use lock files](#back-in-time-commands-that-use-lock-files) + [`backup`](#backup) + [`restore`](#restore) + [`shutdown`](#shutdown) * [List of known control files](#list-of-known-control-files) + [GUI (application) lock files (`app.lock.pid`)](#gui-application-lock-files-applockpid) + [Global flock file `/tmp/backintime.lock`](#global-flock-file-tmpbackintimelock) + [Backup lock files (`worker.lock.flock`)](#backup-lock-files-workerprofile-idlockflock) + [Backup progress file (`worker.progress`)](#backup-progress-file-workerpidprogress) + [`takesnapshot_.log`](#takesnapshot_profile-idlog) + [`restore_.log`](#restore_profile-idlog) + [Raise file (`app.lock.raise`)](#raise-file-applockraise) + [`save_to_continue` flag file in new snapshots](#save_to_continue-flag-file-in-new-snapshots) + [Restore lock file (`restore.lock`)](#restore-lock-file-restoreprofile-idlock) * [See also](#see-also) + [_Back in Time_ FAQ](#back-in-time-faq) + [Linux advisory locks](#linux-advisory-locks) Notes: - The logic is based on the the source code of [_Back In Time_ v1.4.1](https://github.com/bit-team/backintime/releases/tag/v1.4.1) and all source code references point to v1.4.1. The code may look differently and even the logic may have been changed in later versions. - Tables in this markdown file are generated using https://www.tablesgenerator.com/markdown_tables with `Line breaks as "br"` enabled. - Whenever `` is mentioned it means the profile number of the configuration. **For the profile ID 1 no number is used but an empty string** (why this confusing exception is made is unclear). ## TLDR ;-) _Back In Time_ uses control files to prevent that multiple instances work in parallel and conflicts may occur (eg. taking a snapshot for the same profile in two different processes). The most important control file is `worker.lock` which is used to avoid starting the same backup twice in parallel. The `` is empty for the default profile (1). It also uses another exclusively locked file named `worker.lock.flock` to serialize the access of checking for another running backup of _Back In Time_. _Back In Time_ does only start a new backup job (for the same profile) if the control file does not exist. Lock files are stored by default in the folder `~/.local/share/backintime` and contain the process id (also known as PID - see `man ps`) and process name of the running backup process. The PID is used [to check if the process that created the lock file is still running](https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/applicationinstance.py#L78-L79) and delete or overwrite the lock file with a new instance. ## _Back In Time_ commands that use lock files ### `backup` Takes a snapshot after checking that no other snapshot or restore is running at the same time: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/snapshots.py#L713-L729 If a snapshot or restore is running a warning (not an error!) is issued and **no** new snapshot is taken! Another control file named `worker.lock.flock` is used to serialize access to the `worker*.lock` file **before** a new one is created (via a “flock” = blocking advisory lock on the file). https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/applicationinstance.py#L47-L48 This table shows the control file focused execution sequence of the [`snapshots.py#backup()` function](https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/snapshots.py#L680): | Step | worker.lock.flock | worker.lock | restore.lock.flock | restore.lock | /tmp/backintime.lock | Other actions | Relevant code snippets | |------|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | | Control file to serialize access to the `worker*.lock` file (via a “flock” = blocking advisory lock on the file) | Control file to indicate a running backup | Control file to serialize access to the `restore*.lock` file (via a “flock” = blocking advisory lock on the file) | Control file to indicate a running restore | Global control file to prevent running backups or restores on multiple snapshots from different profiles or users at the same time (only if `global.use_flock` option is `True` in config) | Executed logic not related to lock files | Taken from the source code of BiT v1.4.1 | | 1 | Create file and set flock | | | | | | instance = ApplicationInstance(self.config.takeSnapshotInstanceFile(), False, flock = True) | | 2 | | | | | | | restore_instance = ApplicationInstance(self.config.restoreInstanceFile(), False) | | 3 | | Check if exists:
→ Yes: Exit without taking a snapshot | | | | | instance.check() == not True? | | 4 | | | | Check if exists:
-> Yes: Exit without taking a snapshot | | | restore_instance.check() == not True? | | 5 | | Create file | | | | | instance.startApplication() | | 6 | Release flock and delete file | | | | | | self.flockUnlock() # within startApplication() | | 7 | | | | | Create file and set flock | | self.flockExclusive() # global flock to block backups from other profiles or users (and run them serialized) | | 8 | | | | | | Take the snapshot using rsync | self.takeSnapshot(sid, now, include_folders) | | 9 | | | | | | Perform user-callback calls | In case of errors call eg.
self.config.PLUGIN_MANAGER.error(5, msg) # no snapshot and errors
self.config.PLUGIN_MANAGER.error(6, sid.displayID) # snapshot with errors
If a new snapshot was taken (complete or incomplete due to errors):
self.config.PLUGIN_MANAGER.newSnapshot(sid, sid.path()) #new snapshot (if taken)
Finally:
self.config.PLUGIN_MANAGER.processEnd() | | 10 | | Delete file | | | | | instance.exitApplication() | | 11 | | | | | Release flock and delete file | | self.flockRelease() | ### `restore` Before restoring one or more files from a snapshot _Back In Time_ checks if a restore is already running (using the restore lock file `restore.lock`) and exits with a warning (**not an error!**). This table shows the control file focused execution sequence of the [`snapshots.py#restore()` function](https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/snapshots.py#L416: | Step | worker.lock.flock | worker.lock | restore.lock.flock | restore.lock | /tmp/backintime.lock | Other actions | Relevant code snippets | |------|------------------------------------------------------------------------------------------------------------------|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|-------------------------------------------------------------------------------------------------------| | | Control file to serialize access to the `worker*.lock` file (via a “flock” = blocking advisory lock on the file) | Control file to indicate a running backup | Control file to serialize access to the `restore*.lock` file (via a “flock” = blocking advisory lock on the file) | Control file to indicate a running restore | Global control file to prevent running backups or restores on multiple snapshots from different profiles or users at the same time (only if `global.use_flock` option is `True` in config) | Executed logic not related to lock files | Taken from the source code of BiT v1.4.1 | | 1 | | | Create file and set flock | | | | instance = ApplicationInstance(pidFile=self.config.restoreInstanceFile(), autoExit=False, flock=True) | | 2 | | | | Check if exists:
→ Yes: Exit without restoring the snapshot | | | instance.check() == not True? | | 3 | | | | Create file | | | instance.startApplication() | | 4 | | | Release flock and delete file | | | | self.flockUnlock() # within startApplication() | | 5 | | | | | | Restore the snapshot using rsync | proc = tools.Execute(cmd […] | | 6 | | | | Delete file | | | instance.exitApplication() | ### `shutdown` This command shuts down the computer after the current snapshot has finished. It polls the worker lock file to recognize running backups. It is implemented in the function `backintime.py#shutdown()`: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/backintime.py#L805-L819 This table shows the control file focused execution sequence of the function: | Step | worker.lock.flock | worker.lock | restore.lock.flock | restore.lock | /tmp/backintime.lock | Other actions | Relevant code snippets | |------|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------| | | Control file to serialize access to the `worker*.lock` file (via a “flock” = blocking advisory lock on the file) | Control file to indicate a running backup | Control file to serialize access to the `restore*.lock` file (via a “flock” = blocking advisory lock on the file) | Control file to indicate a running restore | Global control file to prevent running backups or restores on multiple snapshots from different profiles or users at the same time (only if `global.use_flock` option is `True` in config) | Executed logic not related to lock files | Taken from the source code of BiT v1.4.1 | | 1 | | | | | | Prepare lock file checking (without using a flock file) | instance = ApplicationInstance(cfg.takeSnapshotInstanceFile() | | 2 | | Check if exists:
→ No: Exit (no active snapshot) | | | | | if not instance.busy(): logger.info('There is no active snapshot for profile %s. Skip shutdown.' | | 3 | | Check if exists:
→ Yes: Wait 5 seconds and check again | | | | | while instance.busy(): logger.debug('Snapshot is still active. Wait for shutdown.') sleep(5) | | 4 | | | | | | shutdown computer | sd.shutdown() | ## List of known control files ### GUI (application) lock files (`app.lock.pid`) An **application lock file** named `app.lock.pid` is used for the _Back In Time_ application (GUI) to avoid starting more than one instance of the application (GUI) for the same user. The name and path of the application lock file is defined in two locations (which should be refactored into one single place). Path and base file name in `config.py#appInstanceFile()`: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/config.py#L1369-L1370 The file extension `.pid` is added in `guiapplicationinstance.py#__init__()`: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/guiapplicationinstance.py#L35 ### Global flock file `/tmp/backintime.lock` You can prevent taking multiple snapshots from different profiles or users to be run at the same time. BiT has a global configuration option for that named `global.use_flock` (see `man backintime-config`) which can also be set in the GUI in the `Options` tab of the `Manage profiles` dialog. It is named `Run only one snapshot at a time`. Other snapshots will be blocked until the current snapshot is done. This is a global option. So it will affect all profiles **for this user**. But you need to activate this for every other user too if you want enable this option for all users. Technically a global flock file ("flock") `/tmp/backintime.lock` is created (with an advisory lock to serialize access): https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/config.py#L1356-L1358 ### Backup lock files (`worker.lock.flock`) The name and path of the worker process lock file for running snapshots is defined in `config.py#takeSnapshotInstanceFile()`: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/config.py#L1388-L1391 Only for the default profile ID (1) no number is used resulting in `worker.lock` (why this confusing exception is made is unclear): https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/config.py#L1372-L1377 ### Backup progress file (`worker.progress`) _Back In Time_ starts `rsync` as separate process. To read the progress, errors and results of `rsync` a `worker.progress' file is used (written by `rsync` and read + filtered by _Back In Time_): https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/snapshots.py#L858 ### `takesnapshot_.log` TODO ### `restore_.log` TODO ### Raise file (`app.lock.raise`) TODO (what is the purpose of this?) Defined: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/guiapplicationinstance.py#L32-L33 Called via timer: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/qt/app.py#L389-L393 RaiseCMD passed in the main() entry point: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/qt/app.py#L1979 ### `save_to_continue` flag file in new snapshots This flag file is set for new snapshots initially. https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/snapshots.py#L2749-L2763 In `snapshots.py#takeSnapshot()` it is then decided if the existing snapshot can be used to "continue" taking a snapshot or to delete the contained files and restart taking the snapshot: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/snapshots.py#L1140-L1166 TODO How exactly does this work? Could we use this to implement a "retry" feature for failed snapshots? ### Restore lock file (`restore.lock`) The name and path of the restore process lock file `restore.lock` for running restores is defined in `config.py#restoreInstanceFile()`: https://github.com/bit-team/backintime/blob/25c2115b42904ec4a4aee5ba1d73bd97cb5d8b31/common/config.py#L1444-L1447 The flock file to serialize write access to the lock file (via a blocking advisory lock on the file) is different from the backup flock file: `restore.lock.flock` ## See also ### _Back in Time_ FAQ The [FAQ](https://github.com/bit-team/backintime/blob/dev/FAQ.md) gives answers for some problems caused by lock files. ### Linux advisory locks See `man 2 fcntl` and https://linuxhandbook.com/file-locking/ > 1. Advisory Locking > > The advisory locking system will not force the forces and will only work if both processes are participating in locking. > For example, process A acquires the lock and starts given tasks with a file. > And if process B starts without acquiring a lock, it can interrupt the ongoing task with process A. > So the condition for making an advisory lock is to ensure each process participates in locking. > the flock command which allows users to handle tasks related to advisory locking backintime-1.5.2/common/doc-dev/BiT_release_process.md000066400000000000000000000240641465446530500227160ustar00rootroot00000000000000# How to prepare and publish a new BIT release ## Overview A release is prepared like a feature by using a "feature" branch and sending a pull request asking for a review. - Source branch: `dev` - Target branch for the pull request: `dev` ## Preconditions for a new release - Developers agreed on the new version number. - Most-recent translations were merged into `dev` branch. See the [localization documentation](2_localization.md). - Full CI build pipeline matrix is activate (see [#1529](https://github.com/bit-team/backintime/issues/1529)). This is related to the Python versions and also to the Ubuntu Distro versions. - `dev` version was tested (CLI in `common` and GUI in `qt`) and testers/developers agreed on "readiness to be released". ## TLDR ;-) - Create a new branch in your clone for the new release candidate. - Update `VERSION` file. - Update `CHANGES` file. - Execute the script `./updateversion.sh` to update the version numbers (based on `VERSION` file) in several files. - Update the "as at" date in the man page files `backintime.1` and `backintime-askpass.1`. - Autogenerate and update the man page file `backintime-config.1` by executing the script `common/create-manapge-backintime-config.py`. - Update `README.md` file. - Run `codespell` to check for common spelling errors. - Commit the changes. - Open a new pull request (PR) for review by other developers. When the PR is merged: - Create a new tar archive (eg. `backintime-1.4.0.tar.gz`) with `./make-tarball.sh`. - Create a new release in Github (attaching above tar archive). - Update `VERSION` and `CHANGES` for the `dev` branch. ## Step by step - Announce code freeze on `dev` branch to all active developers via email. - Check that Travis CI did successfully build the latest `dev` branch commit: https://app.travis-ci.com/github/bit-team/backintime - Pull latest `dev` branch changes into your BiT repo clone's `dev` branch: ``` git switch dev git pull upstream dev ``` - Create a release candidate branch in your clone using the new version number: ``` git checkout -b rc/v1.4.0 ``` - Enable the full build matrix in Travis CI (Python version * arch[icture]) by commenting the excluded architectures: ``` jobs: # exclude: # - python: "3.9" # - python: "3.10" ``` - Build the still unchanged release candidate and execute the unit tests: ``` cd common ./configure make make test cd ../qt ./configure make ``` - **Recommended:** Use a linter like [`pylint`](https://pypi.org/project/pylint/) to identify code errors that are not obvious but may be found only (too late) at run-time, eg. object name typos (see e.g. [#1553](https://github.com/bit-team/backintime/issues/1553)). *Note:* Since v1.4.x there is a unit test `test_lint.py` which performs a minimal check for severe problems via `make test`. - Update the `CHANGES` text file in the project's root folder: - Check `git log` to find and add forgotten but relevant entries for `CHANGES`, eg. using the tag of the previous release: `git log v1.4.0..HEAD` - Rename the top-most line with the collected `dev` changes from eg. `Version 1.3.4-dev (development of upcoming release)` into `Version 1.4.0 (2023-09-14)` using the new version number and release date. - Update `VERSION` text file in the project's root folder: Set the new version number **without** the release date (eg. `1.4.0`) - Execute the script `./updateversion.sh` in the project's root folder to automatically update the version number in multiple files using the version number from the `VERSION` file (so you do not forget to update one file ;-). - BiT CLI config in `common/version.py` - man pages in `common/man/C/backintime*.1` and `qt/man/C/backintime*.1` - Check that the version numbers have been update by opening some of the above files. - Update the "as at" date in the man page files (in `common/man/C/backintime*.1` and `qt/man/C/backintime*.1`) manually by changing the month and year in the first line that looks like this: ``` .TH backintime-config 1 "Aug 2023" "version 1.4.0" "USER COMMANDS" ``` - Update the `AUTHORS` file in the project's root folder - Should be done during development normally - Ask contributors for explicit permission to publish their name and email address! - Review and update the `README.md` in your release candidate branch - Update the **Known Problems and Workarounds** section: - Move fixed major known problems from the "Known Problems and Workarounds" section (which describes the latest release) into the "Problems in Versions older than the latest stable release" to stay visible for users of older versions. - Remove old known problems if you are sure old BiT versions with this issue are unlikely to be used "in the wild" anymore. - Update table of contents (TOC) for the changed parts. You can eg. use https://github.com/derlin/bitdowntoc to generate a TOC and copy the changed parts into the `README.md`. - Build the prepared release candidate and execute the unit tests: ``` cd common ./configure make make test cd ../qt ./configure make ``` - Execute [`codespell`](https://pypi.org/project/codespell) in the repositories root folder to check for common spelling errors. - Do a manual smoke and UAT ("user acceptance test") of the GUI. - If you find bugs: - Open an issue. - Decide if you want to fix this in the release candidate. - If you fix it in the release candidate: Update the CHANGES file (add the issue number + description). - If you don't fix it (eg. too risky) and it is a HIGH bug: - Add the bug to the [Known Problems and Workarounds](https://github.com/bit-team/backintime#known-problems-and-workarounds) section of `README.md` (of the release candidate branch) and describe a workaround (if any). - Commit and push, if no "show-stopping" bug exists. Note: To push your release candidate branch into a new remote branch use: ``` git push --set-upstream origin # eg. rc/v1.4.0 ``` - Open a new pull request for your pushed release candidate branch: - Add all developers as reviewers. - Mention bugs (and status) discovered during preparation of the release candidate in the description. - Fix review findings and push the changes again to update the pull request. - Finally check the Travis CI status of the pull request (everything must be green). - Once all the PR reviewer approved the PR do a squash-merge (= all changes are "squashed" into one commit) into the `dev` branch using a commit message like `Release candidate for v1.4.1 (Oct. 1, 2023)` - Wait for the final Travis CI build on the `dev` branch and check if everything is OK to proceed with the release. - Create the tarball archive files to be attached as "binaries" to the release: - Update the `dev` branch ``` git switch dev git pull upstream dev ``` - Create a new tar archive (eg. `backintime-1.4.0.tar.gz`) with `./make-tarball.sh`: The script will actually `git clone` the current branch into a new folder `../backintime-$VERSION` and then make an tar archive file using that new folder. Cloning into a new folder ensures that there are no left-over files inside the tar archive. - Test this tarball. Install it. - Create a new release in Github (`Releases` button under `code`): - Tag in `dev` branch with version number, eg.: `v1.4.0` - Release title eg.: Back In Time 1.4.0 (Sept. 14, 2023) - Description: `# Changelog` + the relevant part of the CHANGES file - Check `Set as the latest release` - Attach binaries: Upload the generated tar archive (eg. `backintime-1.4.0.tar.gz`). In releases < 1.4.0 also the public key of Germar was attached (`*.asc` file) since it can be used to validate signed debian packages files (`backintime*.deb`). Since we neither have the private key of Germar nor do publish any `deb` packages via Ubuntu PPA anymore this is not required or helpful anymore. - Click on the "Publish release" button - Start a new dev version by preparing a new PR ``` git switch dev git pull upstream git checkout -b PR/v1.4.1-dev # use a new minor version number ``` - Increment the version number for the new dev version: - Update the `VERSION` text file in the project's root folder: Set the new version number by incrementing the last number and appending `-dev` (eg. `1.4.1-dev`) - Update the `CHANGES` text file in the project's root folder: Add a new top-most line with the new version number, eg.: `Version 1.4.1-dev (development of upcoming release)` - Execute the script `./updateversion.sh` in the project's root folder to automatically update the version number in files - Edit `.travis.yml` to reduce the build matrix again (to save "build credits") Eg. re-enable the exclusion list: ``` jobs: exclude: - python: "3.9" - python: "3.10" ``` - Check the "Known Problems and Workarounds" section of the `README.md` and make sure it is up-to-date - Commit and push the changes and create a new pull request Commit and PR message, eg.: `Start of new dev version v1.4.1-dev` - Optional: Request PR approval - Squash-merge the PR into the `dev` branch - Send an email to all developers - to announce "end of code freeze" - send a link to the github release - inform about unexpected (open) problems (if any) - (Out of scope here): Update the Github milestones and the assigned issues ## Other noteworthy things ### "Read the docs" code documentation The "Read the docs" site is automatically updated with every commit on the `dev` branch. See [Issue #1533](https://github.com/bit-team/backintime/pull/1533#issuecomment-1720897669) and the [_backintime-dev_ project](https://readthedocs.org/projects/backintime-dev) at Read the docs. ### Building `deb` package files We do no longer maintain and publish `deb` package files. To build your own `deb` file see: https://github.com/bit-team/backintime/blob/dev/CONTRIBUTING.md#build-own-deb-file November 2023 backintime-1.5.2/common/doc-dev/Makefile000066400000000000000000000152601465446530500201160ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/BackInTime.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/BackInTime.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/BackInTime" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/BackInTime" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." htmlOpen: html xdg-open $(BUILDDIR)/html/index.html backintime-1.5.2/common/doc-dev/README.md000066400000000000000000000010251465446530500177270ustar00rootroot00000000000000# Documentation for Maintainers and Developers - [Using Sphinx to write and build documentation](1_doc_maintenance_howto.md) - [Localization and translation using Weblate](2_localization.md) - [How to setup openssh for unit tests](3_How_to_set_up_openssh_server_for_ssh_unit_tests.md) - [Usage of control files (locks, flocks, logs and others)](4_Control_files_usage_(locks_flocks_logs_and_others).md) - [How to prepare and publish a new BiT release](BiT_release_process.md) Dec 2023 backintime-1.5.2/common/doc-dev/_static/000077500000000000000000000000001465446530500201005ustar00rootroot00000000000000backintime-1.5.2/common/doc-dev/_static/.dummy000066400000000000000000000000001465446530500212220ustar00rootroot00000000000000backintime-1.5.2/common/doc-dev/_templates/000077500000000000000000000000001465446530500206075ustar00rootroot00000000000000backintime-1.5.2/common/doc-dev/_templates/.dummy000066400000000000000000000000001465446530500217310ustar00rootroot00000000000000backintime-1.5.2/common/doc-dev/applicationinstance.rst000066400000000000000000000002241465446530500232320ustar00rootroot00000000000000applicationinstance module ========================== .. automodule:: applicationinstance :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/askpass.rst000066400000000000000000000001601465446530500206460ustar00rootroot00000000000000askpass module ============== .. automodule:: askpass :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/backintime.rst000066400000000000000000000001711465446530500213110ustar00rootroot00000000000000backintime module ================= .. automodule:: backintime :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/bcolors.rst000066400000000000000000000001601465446530500206440ustar00rootroot00000000000000bcolors module ============== .. automodule:: bcolors :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/cli.rst000066400000000000000000000001441465446530500177520ustar00rootroot00000000000000cli module ========== .. automodule:: cli :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/conf.py000066400000000000000000000102561465446530500177550ustar00rootroot00000000000000#!/usr/bin/env python3 import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath(os.path.join(os.pardir))) sys.path.insert(0, os.path.abspath(os.path.join(os.pardir, "plugins"))) # Import to solve race conditions between config an mount. import config # -- General configuration ------------------------------------------------ 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. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'BackInTime' copyright = '2016, Germar Reitze' author = 'Germar Reitze' # Don't edit this variable. It is updated automatically by "updateversion.sh". import backintime version = backintime.__version__ # The full version, including alpha/beta/rc tags. 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 name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' 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/', 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' 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'] # 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, %H:%M (%Z)' # Output file base name for HTML help builder. htmlhelp_basename = 'BackInTimeDevDoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'BackInTime.tex', 'Back In Time Development Documentation', 'Germar Reitze', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'backintime', 'Back In Time Development Documentation', ['Germar Reitze'], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'BackInTime', 'Back In Time Development Documentation', 'Germar Reitze', 'BackInTime', 'One line description of project.', 'Miscellaneous'), ] backintime-1.5.2/common/doc-dev/config.rst000066400000000000000000000001551465446530500204520ustar00rootroot00000000000000config module ============= .. automodule:: config :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/configfile.rst000066400000000000000000000001711465446530500213100ustar00rootroot00000000000000configfile module ================= .. automodule:: configfile :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/diagnostics.rst000066400000000000000000000001741465446530500215150ustar00rootroot00000000000000diagnostics module ================== .. automodule:: diagnostics :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/encfstools.rst000066400000000000000000000001711465446530500213620ustar00rootroot00000000000000encfstools module ================= .. automodule:: encfstools :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/exceptions.rst000066400000000000000000000001711465446530500213640ustar00rootroot00000000000000exceptions module ================= .. automodule:: exceptions :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/flock.rst000066400000000000000000000001521465446530500203000ustar00rootroot00000000000000flock module ============ .. automodule:: flock :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/guiapplicationinstance.rst000066400000000000000000000002351465446530500237410ustar00rootroot00000000000000guiapplicationinstance module ============================= .. automodule:: guiapplicationinstance :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/index.rst000066400000000000000000000007351465446530500203200ustar00rootroot00000000000000.. BackInTime documentation master file, created by sphinx-quickstart on Sat Jan 9 00:04:35 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to Back In Time's documentation ======================================= Contents: .. toctree:: :maxdepth: 2 modules.rst plugins/modules.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` backintime-1.5.2/common/doc-dev/logger.rst000066400000000000000000000001551465446530500204640ustar00rootroot00000000000000logger module ============= .. automodule:: logger :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/modules.rst000066400000000000000000000005531465446530500206570ustar00rootroot00000000000000common ====== .. toctree:: :maxdepth: 4 applicationinstance askpass backintime bcolors cli config configfile diagnostics encfstools exceptions flock guiapplicationinstance logger mount password password_ipc pluginmanager progress schedule snapshotlog snapshots sshMaxArg sshtools tools backintime-1.5.2/common/doc-dev/mount.rst000066400000000000000000000001521465446530500203440ustar00rootroot00000000000000mount module ============ .. automodule:: mount :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/password.rst000066400000000000000000000001631465446530500210460ustar00rootroot00000000000000password module =============== .. automodule:: password :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/password_ipc.rst000066400000000000000000000001771465446530500217060ustar00rootroot00000000000000password_ipc module =================== .. automodule:: password_ipc :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/pluginmanager.rst000066400000000000000000000002021465446530500220270ustar00rootroot00000000000000pluginmanager module ==================== .. automodule:: pluginmanager :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/plugins/000077500000000000000000000000001465446530500201335ustar00rootroot00000000000000backintime-1.5.2/common/doc-dev/plugins/modules.rst000066400000000000000000000001051465446530500223310ustar00rootroot00000000000000plugins ======= .. toctree:: :maxdepth: 4 usercallbackplugin backintime-1.5.2/common/doc-dev/plugins/usercallbackplugin.rst000066400000000000000000000002211465446530500245320ustar00rootroot00000000000000usercallbackplugin module ========================= .. automodule:: usercallbackplugin :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/progress.rst000066400000000000000000000001631465446530500210500ustar00rootroot00000000000000progress module =============== .. automodule:: progress :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/schedule.rst000066400000000000000000000001631465446530500210000ustar00rootroot00000000000000schedule module =============== .. automodule:: schedule :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/snapshotlog.rst000066400000000000000000000001741465446530500215470ustar00rootroot00000000000000snapshotlog module ================== .. automodule:: snapshotlog :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/snapshots.rst000066400000000000000000000001661465446530500212310ustar00rootroot00000000000000snapshots module ================ .. automodule:: snapshots :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/sshMaxArg.rst000066400000000000000000000001661465446530500211040ustar00rootroot00000000000000sshMaxArg module ================ .. automodule:: sshMaxArg :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/sshtools.rst000066400000000000000000000001631465446530500210620ustar00rootroot00000000000000sshtools module =============== .. automodule:: sshtools :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/doc-dev/tools.rst000066400000000000000000000001521465446530500203420ustar00rootroot00000000000000tools module ============ .. automodule:: tools :members: :undoc-members: :show-inheritance: backintime-1.5.2/common/encfstools.py000066400000000000000000000700741465446530500176720ustar00rootroot00000000000000# Copyright (C) 2012-2022 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 grp import subprocess import re import shutil import tempfile from datetime import datetime from packaging.version import Version import config import password import password_ipc import tools import sshtools import logger from mount import MountControl from exceptions import MountException, EncodeValueError class EncFS_mount(MountControl): """ Mount encrypted paths with encfs. """ def __init__(self, *args, **kwargs): # init MountControl super(EncFS_mount, self).__init__(*args, **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) self.setattrKwargs('reverse', False, **kwargs) self.setattrKwargs('config_path', None, **kwargs) self.setattrKwargs('password', None, store = False, **kwargs) self.setattrKwargs('hash_id_1', None, **kwargs) self.setattrKwargs('hash_id_2', None, **kwargs) self.setDefaultArgs() self.mountproc = 'encfs' self.log_command = '%s: %s' % (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) logger.debug('Provide password through temp FIFO', self) thread = password_ipc.TempPasswordThread(self.password) env = self.env() env['ASKPASS_TEMP'] = thread.temp_file with thread.starter(): 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) output = proc.communicate()[0] self.backupConfig() if proc.returncode: raise MountException( '{}:\n\n{}'.format( _("Can't mount '{command}'") .format(command=' '.join(encfs)), output)) def preMountCheck(self, first_run = False): """ check what ever conditions must be given for the mount """ self.checkFuse() if first_run: self.checkVersion() return True def env(self): """ return environment with encfs configfile """ env = os.environ.copy() cfg = self.configFile() if os.path.isfile(cfg): env['ENCFS6_CONFIG'] = cfg return env def configFile(self): """ return encfs config file """ f = '.encfs6.xml' if self.config_path is None: cfg = os.path.join(self.path, f) else: cfg = os.path.join(self.config_path, f) return cfg def isConfigured(self): """ check if encfs config file exist. If not and if we are in settingsdialog ask for password confirmation. _mount will then create a new config """ cfg = self.configFile() if os.path.isfile(cfg): logger.debug(f'Found encfs config in {cfg}', self) return True else: logger.debug(f'No encfs config in {cfg}', self) msg = _('Config for encrypted folder not found.') if not self.tmp_mount: raise MountException(msg) else: question = '{}\n{}'.format( msg, _('Create a new encrypted folder?') ) if not self.config.askQuestion(question): raise MountException(_('Cancel')) else: pw = password.Password(self.config) password_confirm = pw.passwordFromUser( self.parent, prompt=_('Please confirm the password.')) if self.password == password_confirm: return False else: raise MountException(_("Password doesn't match.")) def checkVersion(self): """ check encfs version. 1.7.2 had a bug with --reverse that will create corrupt files 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) output = proc.communicate()[0] m = re.search(r'(\d\.\d\.\d)', output) 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 """ cfg = self.configFile() if not os.path.isfile(cfg): logger.warning('No encfs config in %s. Skip backup of config file.' %cfg, 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) 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 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 = os.path.join(backup_folder, new_backup_file) logger.debug('Create backup of encfs config %s to %s' %(cfg, 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() self.mode = mode if self.mode is None: self.mode = self.config.snapshotsMode(self.profile_id) self.parent = parent 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')) super(EncFS_SSH, self).__init__(*self.args, **self.splitKwargs('encfs')) def mount(self, *args, **kwargs): """ call mount for sshfs, encfs --reverse and encfs register 'encfsctl encode' in config.ENCODE """ 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 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 likely 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) 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.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) 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) self.rev_root.mount(*args, **kwargs) 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 """ self.config.ENCODE.close() self.config.ENCODE = Bounce() logger.debug('Unmount encfs', self) super(EncFS_SSH, self).umount(*args, **kwargs) logger.debug('Unmount local filesystem root mount encfs --reverse', self) self.rev_root.umount(*args, **kwargs) logger.debug('Unmount sshfs', self) self.ssh.umount(*args, **kwargs) def preMountCheck(self, *args, **kwargs): """ 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 def splitKwargs(self, mode): """ split all given arguments for the desired mount class """ d = self.kwargs.copy() d['cfg'] = self.config d['profile_id'] = self.profile_id 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) 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) 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) 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): """ encode path with encfsctl. ENCFS_SSH will replace config.ENCODE with this """ def __init__(self, encfs): self.encfs = encfs self.password = self.encfs.password self.chroot = self.encfs.rev_root.currentMountpoint if not self.chroot[-1] == os.sep: self.chroot += os.sep self.remote_path = self.encfs.ssh.path if not self.remote_path[-1] == os.sep: self.remote_path += os.sep #precompile some regular expressions self.re_asterisk = re.compile(r'\*') self.re_separate_asterisk = re.compile(r'(.*?)(\*+)(.*)') def __del__(self): self.close() def startProcess(self): """ start 'encfsctl encode' process in pipe mode. """ thread = password_ipc.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) def path(self, path): """ write plain path to encfsctl stdin and read encrypted path from stdout """ if not 'p' in vars(self): self.startProcess() if not self.p.returncode is None: 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) raise EncodeValueError() return ret def exclude(self, path): """ encrypt paths for snapshots.takeSnapshot exclude list. After encoding the path a wildcard would not match anymore so all paths with wildcards are ignored. Only single and double asterisk that will match a full file or folder name will work. """ if tools.patternHasNotEncryptableWildcard(path): return None 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 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): """ encrypt paths for snapshots.takeSnapshot include list. """ return os.path.join(os.sep, self.path(path)) 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): """ stop encfsctl process """ if 'p' in vars(self) and self.p.returncode is None: 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): """ decode path with encfsctl. """ 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) elif self.mode == 'ssh_encfs': 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. # Warning (2023-11): Do not modify the source string. # See #1559 for details. takeSnapshot = _('Take snapshot') \ .replace('Schnappschuss', '(?:Schnappschuss|Snapshot)') #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=")(.*?)(")') # codespell-ignore #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="\.\./\.\./)(.*?)(")') #search for: [C] self.re_change = re.compile(r'(^\[C\] .{11} )(.*)') #search for: [I] Take snapshot (rsync: BACKINTIME: ) # [I] Take snapshot (rsync: deleting ) # [I] Take snapshot (rsync: rsync: readlink_stat("...mountpoint/") # [I] Take snapshot (rsync: rsync: send_files failed to open "...mountpoint/": Permission denied (13)) # [I] Take snapshot (rsync: file has vanished: "...mountpoint/") # [I] Take snapshot (rsync: ) pattern = [] pattern.append(r' BACKINTIME: .{11} ') pattern.append(r' deleting ') pattern.append(r' rsync: readlink_stat\(".*?mountpoint/') pattern.append(r' rsync: send_files failed to open ".*?mountpoint/') pattern.append(r' file has vanished: ".*?mountpoint/') pattern.append(r' ') self.re_info = re.compile(r'(^(?:\[I\] )?%s \(rsync:(?:%s))(.*?)(\).*|".*)' % (takeSnapshot, '|'.join(pattern))) #search for: [E] Error: rsync readlink_stat("...mountpoint/") # [E] Error: rsync: send_files failed to open "...mountpoint/": Permission denied (13) # [E] Error: rsync: recv_generator: failed to stat "/": File name too long (36) # [E] Error: rsync: recv_generator: mkdir "/": File name too long (36) 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" self.re_info_cp= re.compile(r'(^\[I\] .*? cp -aRl "%s/)(.*?)("\* "%s/)(.*?)(")' % (path, path)) #search for all chars except * self.re_all_except_asterisk = re.compile(r'[^\*]+') #search for: -> self.re_all_except_arrow = re.compile(r'(.*?)((?: [-=]> )+)(.*)') #skip: [I] Take snapshot (rsync: sending incremental file list) # [I] Take snapshot (rsync: building file list ... done) # [I] Take snapshot (rsync: sent 26569703 bytes received 239616 bytes 85244.26 bytes/sec) # [I] Take snapshot (rsync: total size is 9130263449 speedup is 340.56) # [I] Take snapshot (rsync: rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1070) [sender=3.0.9]) # [I] Take snapshot (rsync: rsync warning: some files vanished before they could be transferred (code 24) at main.c(1070) [sender=3.0.9]) pattern = [] pattern.append(r'sending incremental file list') pattern.append(r'building file list ... done') pattern.append(r'sent .*? received') pattern.append(r'total size is .*? speedup is') pattern.append(r'rsync error: some files/attrs were not transferred') pattern.append(r'rsync warning: some files vanished before they could be transferred') 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' def __del__(self): self.close() def startProcess(self): """ start 'encfsctl decode' process in pipe mode. """ thread = password_ipc.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) def path(self, 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) 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 """ output = [] for path in list_: output.append(self.path(path)) return output def log(self, line): """ decode paths in takesnapshot.log """ #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 m = self.re_change.match(line) if not m is None: return m.group(1) + self.pathWithArrow(m.group(2)) #[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 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 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): """ 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): """ return decoded path of a match object """ return self.path(m.group(0)) def pathWithArrow(self, path): """ 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) def remote(self, path): """ decode the path on remote host starting from backintime/host/user/... """ assert isinstance(path, bytes), 'path is not bytes type: %s' % 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): """ stop encfsctl process """ if 'p' in vars(self) and self.p.returncode is None: logger.debug('stop \'encfsctl decode\' process', self) self.p.communicate() backintime-1.5.2/common/exceptions.py000066400000000000000000000034261465446530500176710ustar00rootroot00000000000000# Copyright (C) 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. # # 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. 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): self.msg = msg def __str__(self): return self.msg class InvalidCmd(BackInTimeException): def __init__(self, msg): self.msg = msg def __str__(self): return self.msg class LimitExceeded(BackInTimeException): def __init__(self, msg): self.msg = msg def __str__(self): return self.msg class PermissionDeniedByPolicy(BackInTimeException): def __init__(self, msg): self.msg = msg def __str__(self): return self.msg backintime-1.5.2/common/flock.py000066400000000000000000000135471465446530500166130ustar00rootroot00000000000000# SPDX-FileCopyrightText: © 2024 Christian BUHTZ # # SPDX-License-Identifier: GPL-2.0 # # This file is part of the program "Back In time" which is released under GNU # General Public License v2 (GPLv2). # See file LICENSE 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 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 # Try user specific file lock # e.g. /run/user/ self._file_path = Path(os.environ['XDG_RUNTIME_DIR']) / filename if self._can_use_file(self._file_path): return # 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 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 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 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) backintime-1.5.2/common/guiapplicationinstance.py000066400000000000000000000051501465446530500222410ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 logger from applicationinstance import ApplicationInstance class GUIApplicationInstance(ApplicationInstance): """ class used to handle one application instance mechanism """ 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) #remove raiseFile is already exists if os.path.exists(self.raiseFile): os.remove(self.raiseFile) self.check(raiseCmd) self.startApplication() def check(self, raiseCmd): """ 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 try: with open(self.raiseFile, 'wt') as f: f.write(raiseCmd) except OSError as e: logger.error('Failed to write raise file %s: [%s] %s' %(e.filename, e.errno, e.strerror)) exit(0) #exit raise an exception so don't put it in a try/except block else: return ret def raiseCommand(self): """ check if the application must to be raised return None if no raise needed, or a string command to raise """ ret_val = None try: if os.path.isfile(self.raiseFile): with open(self.raiseFile, 'rt') as f: ret_val = f.read() os.remove(self.raiseFile) except: pass return ret_val backintime-1.5.2/common/languages.py000066400000000000000000002055101465446530500174540ustar00rootroot00000000000000# Generated at Tue Aug 6 10:08:08 2024 with help of package "babel" and "polib". # https://babel.pocoo.org # https://github.com/python-babel/babel names = { 'ar': { '_native': 'العربية', 'ar': 'العربية', 'bg': 'арабски', 'bs': 'arapski', 'ca': 'àrab', 'cs': 'arabština', 'da': 'arabisk', 'de': 'Arabisch', 'el': 'Αραβικά', 'en': 'Arabic', 'eo': 'araba', 'es': 'árabe', 'et': 'araabia', 'eu': 'arabiera', 'fa': 'عربی', 'fi': 'arabia', 'fo': 'arabiskt', 'fr': 'arabe', 'gl': 'árabe', 'he': 'ערבית', 'hr': 'arapski', 'hu': 'arab', 'id': 'Arab', 'is': 'arabíska', 'it': 'arabo', 'ja': 'アラビア語', 'ko': '아랍어', 'lt': 'arabų', 'nb': 'arabisk', 'nl': 'Arabisch', 'nn': 'arabisk', 'pl': 'arabski', 'pt': 'árabe', 'pt_BR': 'árabe', 'ro': 'arabă', 'ru': 'арабский', 'sk': 'arabčina', 'sl': 'arabščina', 'sr': 'арапски', 'sv': 'arabiska', 'th': 'อาหรับ', 'tr': 'Arapça', 'uk': 'арабська', 'vi': 'Tiếng Ả Rập', 'zh_CN': '阿拉伯语', 'zh_TW': '阿拉伯文'}, 'bg': { '_native': 'български', 'ar': 'البلغارية', 'bg': 'български', 'bs': 'bugarski', 'ca': 'búlgar', 'cs': 'bulharština', 'da': 'bulgarsk', 'de': 'Bulgarisch', 'el': 'Βουλγαρικά', 'en': 'Bulgarian', 'eo': 'bulgara', 'es': 'búlgaro', 'et': 'bulgaaria', 'eu': 'bulgariera', 'fa': 'بلغاری', 'fi': 'bulgaria', 'fo': 'bulgarskt', 'fr': 'bulgare', 'gl': 'búlgaro', 'he': 'בולגרית', 'hr': 'bugarski', 'hu': 'bolgár', 'id': 'Bulgaria', 'is': 'búlgarska', 'it': 'bulgaro', 'ja': 'ブルガリア語', 'ko': '불가리아어', 'lt': 'bulgarų', 'nb': 'bulgarsk', 'nl': 'Bulgaars', 'nn': 'bulgarsk', 'pl': 'bułgarski', 'pt': 'búlgaro', 'pt_BR': 'búlgaro', 'ro': 'bulgară', 'ru': 'болгарский', 'sk': 'bulharčina', 'sl': 'bolgarščina', 'sr': 'бугарски', 'sv': 'bulgariska', 'th': 'บัลแกเรีย', 'tr': 'Bulgarca', 'uk': 'болгарська', 'vi': 'Tiếng Bulgaria', 'zh_CN': '保加利亚语', 'zh_TW': '保加利亞文'}, 'bs': { '_native': 'bosanski', 'ar': 'البوسنية', 'bg': 'босненски', 'bs': 'bosanski', 'ca': 'bosnià', 'cs': 'bosenština', 'da': 'bosnisk', 'de': 'Bosnisch', 'el': 'Βοσνιακά', 'en': 'Bosnian', 'eo': 'bosnia', 'es': 'bosnio', 'et': 'bosnia', 'eu': 'bosniera', 'fa': 'بوسنیایی', 'fi': 'bosnia', 'fo': 'bosniskt', 'fr': 'bosniaque', 'gl': 'bosníaco', 'he': 'בוסנית', 'hr': 'bosanski', 'hu': 'bosnyák', 'id': 'Bosnia', 'is': 'bosníska', 'it': 'bosniaco', 'ja': 'ボスニア語', 'ko': '보스니아어', 'lt': 'bosnių', 'nb': 'bosnisk', 'nl': 'Bosnisch', 'nn': 'bosnisk', 'pl': 'bośniacki', 'pt': 'bósnio', 'pt_BR': 'bósnio', 'ro': 'bosniacă', 'ru': 'боснийский', 'sk': 'bosniačtina', 'sl': 'bosanščina', 'sr': 'босански', 'sv': 'bosniska', 'th': 'บอสเนีย', 'tr': 'Boşnakça', 'uk': 'боснійська', 'vi': 'Tiếng Bosnia', 'zh_CN': '波斯尼亚语', 'zh_TW': '波士尼亞文'}, 'ca': { '_native': 'català', 'ar': 'الكتالانية', 'bg': 'каталонски', 'bs': 'katalonski', 'ca': 'català', 'cs': 'katalánština', 'da': 'catalansk', 'de': 'Katalanisch', 'el': 'Καταλανικά', 'en': 'Catalan', 'eo': 'kataluna', 'es': 'catalán', 'et': 'katalaani', 'eu': 'katalana', 'fa': 'کاتالان', 'fi': 'katalaani', 'fo': 'katalani', 'fr': 'catalan', 'gl': 'catalán', 'he': 'קטלאנית', 'hr': 'katalonski', 'hu': 'katalán', 'id': 'Katalan', 'is': 'katalónska', 'it': 'catalano', 'ja': 'カタロニア語', 'ko': '카탈로니아어', 'lt': 'katalonų', 'nb': 'katalansk', 'nl': 'Catalaans', 'nn': 'katalansk', 'pl': 'kataloński', 'pt': 'catalão', 'pt_BR': 'catalão', 'ro': 'catalană', 'ru': 'каталанский', 'sk': 'katalánčina', 'sl': 'katalonščina', 'sr': 'каталонски', 'sv': 'katalanska', 'th': 'คาตาลัน', 'tr': 'Katalanca', 'uk': 'каталонська', 'vi': 'Tiếng Catalan', 'zh_CN': '加泰罗尼亚语', 'zh_TW': '加泰蘭文'}, 'cs': { '_native': 'čeština', 'ar': 'التشيكية', 'bg': 'чешки', 'bs': 'češki', 'ca': 'txec', 'cs': 'čeština', 'da': 'tjekkisk', 'de': 'Tschechisch', 'el': 'Τσεχικά', 'en': 'Czech', 'eo': 'ĉeĥa', 'es': 'checo', 'et': 'tšehhi', 'eu': 'txekiera', 'fa': 'چکی', 'fi': 'tšekki', 'fo': 'kekkiskt', 'fr': 'tchèque', 'gl': 'checo', 'he': 'צ׳כית', 'hr': 'češki', 'hu': 'cseh', 'id': 'Cheska', 'is': 'tékkneska', 'it': 'ceco', 'ja': 'チェコ語', 'ko': '체코어', 'lt': 'čekų', 'nb': 'tsjekkisk', 'nl': 'Tsjechisch', 'nn': 'tsjekkisk', 'pl': 'czeski', 'pt': 'tcheco', 'pt_BR': 'tcheco', 'ro': 'cehă', 'ru': 'чешский', 'sk': 'čeština', 'sl': 'češčina', 'sr': 'чешки', 'sv': 'tjeckiska', 'th': 'เช็ก', 'tr': 'Çekçe', 'uk': 'чеська', 'vi': 'Tiếng Séc', 'zh_CN': '捷克语', 'zh_TW': '捷克文'}, 'da': { '_native': 'dansk', 'ar': 'الدانمركية', 'bg': 'датски', 'bs': 'danski', 'ca': 'danès', 'cs': 'dánština', 'da': 'dansk', 'de': 'Dänisch', 'el': 'Δανικά', 'en': 'Danish', 'eo': 'dana', 'es': 'danés', 'et': 'taani', 'eu': 'daniera', 'fa': 'دانمارکی', 'fi': 'tanska', 'fo': 'danskt', 'fr': 'danois', 'gl': 'dinamarqués', 'he': 'דנית', 'hr': 'danski', 'hu': 'dán', 'id': 'Dansk', 'is': 'danska', 'it': 'danese', 'ja': 'デンマーク語', 'ko': '덴마크어', 'lt': 'danų', 'nb': 'dansk', 'nl': 'Deens', 'nn': 'dansk', 'pl': 'duński', 'pt': 'dinamarquês', 'pt_BR': 'dinamarquês', 'ro': 'daneză', 'ru': 'датский', 'sk': 'dánčina', 'sl': 'danščina', 'sr': 'дански', 'sv': 'danska', 'th': 'เดนมาร์ก', 'tr': 'Danca', 'uk': 'данська', 'vi': 'Tiếng Đan Mạch', 'zh_CN': '丹麦语', 'zh_TW': '丹麥文'}, 'de': { '_native': 'Deutsch', 'ar': 'الألمانية', 'bg': 'немски', 'bs': 'njemački', 'ca': 'alemany', 'cs': 'němčina', 'da': 'tysk', 'de': 'Deutsch', 'el': 'Γερμανικά', 'en': 'German', 'eo': 'germana', 'es': 'alemán', 'et': 'saksa', 'eu': 'alemana', 'fa': 'آلمانی', 'fi': 'saksa', 'fo': 'týskt', 'fr': 'allemand', 'gl': 'alemán', 'he': 'גרמנית', 'hr': 'njemački', 'hu': 'német', 'id': 'Jerman', 'is': 'þýska', 'it': 'tedesco', 'ja': 'ドイツ語', 'ko': '독일어', 'lt': 'vokiečių', 'nb': 'tysk', 'nl': 'Duits', 'nn': 'tysk', 'pl': 'niemiecki', 'pt': 'alemão', 'pt_BR': 'alemão', 'ro': 'germană', 'ru': 'немецкий', 'sk': 'nemčina', 'sl': 'nemščina', 'sr': 'немачки', 'sv': 'tyska', 'th': 'เยอรมัน', 'tr': 'Almanca', 'uk': 'німецька', 'vi': 'Tiếng Đức', 'zh_CN': '德语', 'zh_TW': '德文'}, 'el': { '_native': 'Ελληνικά', 'ar': 'اليونانية', 'bg': 'гръцки', 'bs': 'grčki', 'ca': 'grec', 'cs': 'řečtina', 'da': 'græsk', 'de': 'Griechisch', 'el': 'Ελληνικά', 'en': 'Greek', 'eo': 'greka', 'es': 'griego', 'et': 'kreeka', 'eu': 'greziera', 'fa': 'یونانی', 'fi': 'kreikka', 'fo': 'grikskt', 'fr': 'grec', 'gl': 'grego', 'he': 'יוונית', 'hr': 'grčki', 'hu': 'görög', 'id': 'Yunani', 'is': 'gríska', 'it': 'greco', 'ja': 'ギリシャ語', 'ko': '그리스어', 'lt': 'graikų', 'nb': 'gresk', 'nl': 'Grieks', 'nn': 'gresk', 'pl': 'grecki', 'pt': 'grego', 'pt_BR': 'grego', 'ro': 'greacă', 'ru': 'греческий', 'sk': 'gréčtina', 'sl': 'grščina', 'sr': 'грчки', 'sv': 'grekiska', 'th': 'กรีก', 'tr': 'Yunanca', 'uk': 'грецька', 'vi': 'Tiếng Hy Lạp', 'zh_CN': '希腊语', 'zh_TW': '希臘文'}, 'en': { '_native': 'English', 'ar': 'الإنجليزية', 'bg': 'английски', 'bs': 'engleski', 'ca': 'anglès', 'cs': 'angličtina', 'da': 'engelsk', 'de': 'Englisch', 'el': 'Αγγλικά', 'en': 'English', 'eo': 'angla', 'es': 'inglés', 'et': 'inglise', 'eu': 'ingelesa', 'fa': 'انگلیسی', 'fi': 'englanti', 'fo': 'enskt', 'fr': 'anglais', 'gl': 'inglés', 'he': 'אנגלית', 'hr': 'engleski', 'hu': 'angol', 'id': 'Inggris', 'is': 'enska', 'it': 'inglese', 'ja': '英語', 'ko': '영어', 'lt': 'anglų', 'nb': 'engelsk', 'nl': 'Engels', 'nn': 'engelsk', 'pl': 'angielski', 'pt': 'inglês', 'pt_BR': 'inglês', 'ro': 'engleză', 'ru': 'английский', 'sk': 'angličtina', 'sl': 'angleščina', 'sr': 'енглески', 'sv': 'engelska', 'th': 'อังกฤษ', 'tr': 'İngilizce', 'uk': 'англійська', 'vi': 'Tiếng Anh', 'zh_CN': '英语', 'zh_TW': '英文'}, 'eo': { '_native': 'esperanto', 'ar': 'الإسبرانتو', 'bg': 'есперанто', 'bs': 'esperanto', 'ca': 'esperanto', 'cs': 'esperanto', 'da': 'esperanto', 'de': 'Esperanto', 'el': 'Εσπεράντο', 'en': 'Esperanto', 'eo': 'esperanto', 'es': 'esperanto', 'et': 'esperanto', 'eu': 'esperantoa', 'fa': 'اسپرانتو', 'fi': 'esperanto', 'fo': 'esperanto', 'fr': 'espéranto', 'gl': 'esperanto', 'he': 'אספרנטו', 'hr': 'esperanto', 'hu': 'eszperantó', 'id': 'Esperanto', 'is': 'esperantó', 'it': 'esperanto', 'ja': 'エスペラント語', 'ko': '에스페란토어', 'lt': 'esperanto', 'nb': 'esperanto', 'nl': 'Esperanto', 'nn': 'esperanto', 'pl': 'esperanto', 'pt': 'esperanto', 'pt_BR': 'esperanto', 'ro': 'esperanto', 'ru': 'эсперанто', 'sk': 'esperanto', 'sl': 'esperanto', 'sr': 'есперанто', 'sv': 'esperanto', 'th': 'เอสเปรันโต', 'tr': 'Esperanto', 'uk': 'есперанто', 'vi': 'Tiếng Quốc Tế Ngữ', 'zh_CN': '世界语', 'zh_TW': '世界文'}, 'es': { '_native': 'español', 'ar': 'الإسبانية', 'bg': 'испански', 'bs': 'španski', 'ca': 'espanyol', 'cs': 'španělština', 'da': 'spansk', 'de': 'Spanisch', 'el': 'Ισπανικά', 'en': 'Spanish', 'eo': 'hispana', 'es': 'español', 'et': 'hispaania', 'eu': 'espainiera', 'fa': 'اسپانیایی', 'fi': 'espanja', 'fo': 'spanskt', 'fr': 'espagnol', 'gl': 'español', 'he': 'ספרדית', 'hr': 'španjolski', 'hu': 'spanyol', 'id': 'Spanyol', 'is': 'spænska', 'it': 'spagnolo', 'ja': 'スペイン語', 'ko': '스페인어', 'lt': 'ispanų', 'nb': 'spansk', 'nl': 'Spaans', 'nn': 'spansk', 'pl': 'hiszpański', 'pt': 'espanhol', 'pt_BR': 'espanhol', 'ro': 'spaniolă', 'ru': 'испанский', 'sk': 'španielčina', 'sl': 'španščina', 'sr': 'шпански', 'sv': 'spanska', 'th': 'สเปน', 'tr': 'İspanyolca', 'uk': 'іспанська', 'vi': 'Tiếng Tây Ban Nha', 'zh_CN': '西班牙语', 'zh_TW': '西班牙文'}, 'et': { '_native': 'eesti', 'ar': 'الإستونية', 'bg': 'естонски', 'bs': 'estonski', 'ca': 'estonià', 'cs': 'estonština', 'da': 'estisk', 'de': 'Estnisch', 'el': 'Εσθονικά', 'en': 'Estonian', 'eo': 'estona', 'es': 'estonio', 'et': 'eesti', 'eu': 'estoniera', 'fa': 'استونیایی', 'fi': 'viro', 'fo': 'estiskt', 'fr': 'estonien', 'gl': 'estoniano', 'he': 'אסטונית', 'hr': 'estonski', 'hu': 'észt', 'id': 'Esti', 'is': 'eistneska', 'it': 'estone', 'ja': 'エストニア語', 'ko': '에스토니아어', 'lt': 'estų', 'nb': 'estisk', 'nl': 'Estisch', 'nn': 'estisk', 'pl': 'estoński', 'pt': 'estoniano', 'pt_BR': 'estoniano', 'ro': 'estonă', 'ru': 'эстонский', 'sk': 'estónčina', 'sl': 'estonščina', 'sr': 'естонски', 'sv': 'estniska', 'th': 'เอสโตเนีย', 'tr': 'Estonca', 'uk': 'естонська', 'vi': 'Tiếng Estonia', 'zh_CN': '爱沙尼亚语', 'zh_TW': '愛沙尼亞文'}, 'eu': { '_native': 'euskara', 'ar': 'الباسكية', 'bg': 'баски', 'bs': 'baskijski', 'ca': 'basc', 'cs': 'baskičtina', 'da': 'baskisk', 'de': 'Baskisch', 'el': 'Βασκικά', 'en': 'Basque', 'eo': 'eŭska', 'es': 'euskera', 'et': 'baski', 'eu': 'euskara', 'fa': 'باسکی', 'fi': 'baski', 'fo': 'baskiskt', 'fr': 'basque', 'gl': 'éuscaro', 'he': 'בסקית', 'hr': 'baskijski', 'hu': 'baszk', 'id': 'Basque', 'is': 'baskneska', 'it': 'basco', 'ja': 'バスク語', 'ko': '바스크어', 'lt': 'baskų', 'nb': 'baskisk', 'nl': 'Baskisch', 'nn': 'baskisk', 'pl': 'baskijski', 'pt': 'basco', 'pt_BR': 'basco', 'ro': 'bască', 'ru': 'баскский', 'sk': 'baskičtina', 'sl': 'baskovščina', 'sr': 'баскијски', 'sv': 'baskiska', 'th': 'บาสก์', 'tr': 'Baskça', 'uk': 'баскська', 'vi': 'Tiếng Basque', 'zh_CN': '巴斯克语', 'zh_TW': '巴斯克文'}, 'fa': { '_native': 'فارسی', 'ar': 'الفارسية', 'bg': 'персийски', 'bs': 'perzijski', 'ca': 'persa', 'cs': 'perština', 'da': 'persisk', 'de': 'Persisch', 'el': 'Περσικά', 'en': 'Persian', 'eo': 'persa', 'es': 'persa', 'et': 'pärsia', 'eu': 'persiera', 'fa': 'فارسی', 'fi': 'persia', 'fo': 'persiskt', 'fr': 'persan', 'gl': 'persa', 'he': 'פרסית', 'hr': 'perzijski', 'hu': 'perzsa', 'id': 'Persia', 'is': 'persneska', 'it': 'persiano', 'ja': 'ペルシア語', 'ko': '페르시아어', 'lt': 'persų', 'nb': 'persisk', 'nl': 'Perzisch', 'nn': 'persisk', 'pl': 'perski', 'pt': 'persa', 'pt_BR': 'persa', 'ro': 'persană', 'ru': 'персидский', 'sk': 'perzština', 'sl': 'perzijščina', 'sr': 'персијски', 'sv': 'persiska', 'th': 'เปอร์เซีย', 'tr': 'Farsça', 'uk': 'перська', 'vi': 'Tiếng Ba Tư', 'zh_CN': '波斯语', 'zh_TW': '波斯文'}, 'fi': { '_native': 'suomi', 'ar': 'الفنلندية', 'bg': 'фински', 'bs': 'finski', 'ca': 'finès', 'cs': 'finština', 'da': 'finsk', 'de': 'Finnisch', 'el': 'Φινλανδικά', 'en': 'Finnish', 'eo': 'finna', 'es': 'finés', 'et': 'soome', 'eu': 'finlandiera', 'fa': 'فنلاندی', 'fi': 'suomi', 'fo': 'finskt', 'fr': 'finnois', 'gl': 'finés', 'he': 'פינית', 'hr': 'finski', 'hu': 'finn', 'id': 'Suomi', 'is': 'finnska', 'it': 'finlandese', 'ja': 'フィンランド語', 'ko': '핀란드어', 'lt': 'suomių', 'nb': 'finsk', 'nl': 'Fins', 'nn': 'finsk', 'pl': 'fiński', 'pt': 'finlandês', 'pt_BR': 'finlandês', 'ro': 'finlandeză', 'ru': 'финский', 'sk': 'fínčina', 'sl': 'finščina', 'sr': 'фински', 'sv': 'finska', 'th': 'ฟินแลนด์', 'tr': 'Fince', 'uk': 'фінська', 'vi': 'Tiếng Phần Lan', 'zh_CN': '芬兰语', 'zh_TW': '芬蘭文'}, 'fo': { '_native': 'føroyskt', 'ar': 'الفاروية', 'bg': 'фарьорски', 'bs': 'farski', 'ca': 'feroès', 'cs': 'faerština', 'da': 'færøsk', 'de': 'Färöisch', 'el': 'Φεροϊκά', 'en': 'Faroese', 'eo': 'feroa', 'es': 'feroés', 'et': 'fääri', 'eu': 'faroera', 'fa': 'فارویی', 'fi': 'fääri', 'fo': 'føroyskt', 'fr': 'féroïen', 'gl': 'feroés', 'he': 'פארואזית', 'hr': 'ferojski', 'hu': 'feröeri', 'id': 'Faroe', 'is': 'færeyska', 'it': 'faroese', 'ja': 'フェロー語', 'ko': '페로어', 'lt': 'farerų', 'nb': 'færøysk', 'nl': 'Faeröers', 'nn': 'færøysk', 'pl': 'farerski', 'pt': 'feroês', 'pt_BR': 'feroês', 'ro': 'feroeză', 'ru': 'фарерский', 'sk': 'faerčina', 'sl': 'ferščina', 'sr': 'фарски', 'sv': 'färöiska', 'th': 'แฟโร', 'tr': 'Faroe dili', 'uk': 'фарерська', 'vi': 'Tiếng Faroe', 'zh_CN': '法罗语', 'zh_TW': '法羅文'}, 'fr': { '_native': 'français', 'ar': 'الفرنسية', 'bg': 'френски', 'bs': 'francuski', 'ca': 'francès', 'cs': 'francouzština', 'da': 'fransk', 'de': 'Französisch', 'el': 'Γαλλικά', 'en': 'French', 'eo': 'franca', 'es': 'francés', 'et': 'prantsuse', 'eu': 'frantsesa', 'fa': 'فرانسوی', 'fi': 'ranska', 'fo': 'franskt', 'fr': 'français', 'gl': 'francés', 'he': 'צרפתית', 'hr': 'francuski', 'hu': 'francia', 'id': 'Prancis', 'is': 'franska', 'it': 'francese', 'ja': 'フランス語', 'ko': '프랑스어', 'lt': 'prancūzų', 'nb': 'fransk', 'nl': 'Frans', 'nn': 'fransk', 'pl': 'francuski', 'pt': 'francês', 'pt_BR': 'francês', 'ro': 'franceză', 'ru': 'французский', 'sk': 'francúzština', 'sl': 'francoščina', 'sr': 'француски', 'sv': 'franska', 'th': 'ฝรั่งเศส', 'tr': 'Fransızca', 'uk': 'французька', 'vi': 'Tiếng Pháp', 'zh_CN': '法语', 'zh_TW': '法文'}, 'gl': { '_native': 'galego', 'ar': 'الجاليكية', 'bg': 'галисийски', 'bs': 'galicijski', 'ca': 'gallec', 'cs': 'galicijština', 'da': 'galicisk', 'de': 'Galicisch', 'el': 'Γαλικιανά', 'en': 'Galician', 'eo': 'galega', 'es': 'gallego', 'et': 'galeegi', 'eu': 'galiziera', 'fa': 'گالیسیایی', 'fi': 'galicia', 'fo': 'galisiskt', 'fr': 'galicien', 'gl': 'galego', 'he': 'גליציאנית', 'hr': 'galicijski', 'hu': 'gallego', 'id': 'Galisia', 'is': 'galisíska', 'it': 'galiziano', 'ja': 'ガリシア語', 'ko': '갈리시아어', 'lt': 'galisų', 'nb': 'galisisk', 'nl': 'Galicisch', 'nn': 'galisisk', 'pl': 'galicyjski', 'pt': 'galego', 'pt_BR': 'galego', 'ro': 'galiciană', 'ru': 'галисийский', 'sk': 'galícijčina', 'sl': 'galicijščina', 'sr': 'галицијски', 'sv': 'galiciska', 'th': 'กาลิเซีย', 'tr': 'Galiçyaca', 'uk': 'галісійська', 'vi': 'Tiếng Galician', 'zh_CN': '加利西亚语', 'zh_TW': '加利西亞文'}, 'he': { '_native': 'עברית', 'ar': 'العبرية', 'bg': 'иврит', 'bs': 'hebrejski', 'ca': 'hebreu', 'cs': 'hebrejština', 'da': 'hebraisk', 'de': 'Hebräisch', 'el': 'Εβραϊκά', 'en': 'Hebrew', 'eo': 'hebrea', 'es': 'hebreo', 'et': 'heebrea', 'eu': 'hebreera', 'fa': 'عبری', 'fi': 'heprea', 'fo': 'hebraiskt', 'fr': 'hébreu', 'gl': 'hebreo', 'he': 'עברית', 'hr': 'hebrejski', 'hu': 'héber', 'id': 'Ibrani', 'is': 'hebreska', 'it': 'ebraico', 'ja': 'ヘブライ語', 'ko': '히브리어', 'lt': 'hebrajų', 'nb': 'hebraisk', 'nl': 'Hebreeuws', 'nn': 'hebraisk', 'pl': 'hebrajski', 'pt': 'hebraico', 'pt_BR': 'hebraico', 'ro': 'ebraică', 'ru': 'иврит', 'sk': 'hebrejčina', 'sl': 'hebrejščina', 'sr': 'хебрејски', 'sv': 'hebreiska', 'th': 'ฮิบรู', 'tr': 'İbranice', 'uk': 'іврит', 'vi': 'Tiếng Do Thái', 'zh_CN': '希伯来语', 'zh_TW': '希伯來文'}, 'hr': { '_native': 'hrvatski', 'ar': 'الكرواتية', 'bg': 'хърватски', 'bs': 'hrvatski', 'ca': 'croat', 'cs': 'chorvatština', 'da': 'kroatisk', 'de': 'Kroatisch', 'el': 'Κροατικά', 'en': 'Croatian', 'eo': 'kroata', 'es': 'croata', 'et': 'horvaadi', 'eu': 'kroaziera', 'fa': 'کروات', 'fi': 'kroatia', 'fo': 'kroatiskt', 'fr': 'croate', 'gl': 'croata', 'he': 'קרואטית', 'hr': 'hrvatski', 'hu': 'horvát', 'id': 'Kroasia', 'is': 'króatíska', 'it': 'croato', 'ja': 'クロアチア語', 'ko': '크로아티아어', 'lt': 'kroatų', 'nb': 'kroatisk', 'nl': 'Kroatisch', 'nn': 'kroatisk', 'pl': 'chorwacki', 'pt': 'croata', 'pt_BR': 'croata', 'ro': 'croată', 'ru': 'хорватский', 'sk': 'chorvátčina', 'sl': 'hrvaščina', 'sr': 'хрватски', 'sv': 'kroatiska', 'th': 'โครเอเชีย', 'tr': 'Hırvatça', 'uk': 'хорватська', 'vi': 'Tiếng Croatia', 'zh_CN': '克罗地亚语', 'zh_TW': '克羅埃西亞文'}, 'hu': { '_native': 'magyar', 'ar': 'الهنغارية', 'bg': 'унгарски', 'bs': 'mađarski', 'ca': 'hongarès', 'cs': 'maďarština', 'da': 'ungarsk', 'de': 'Ungarisch', 'el': 'Ουγγρικά', 'en': 'Hungarian', 'eo': 'hungara', 'es': 'húngaro', 'et': 'ungari', 'eu': 'hungariera', 'fa': 'مجاری', 'fi': 'unkari', 'fo': 'ungarskt', 'fr': 'hongrois', 'gl': 'húngaro', 'he': 'הונגרית', 'hr': 'mađarski', 'hu': 'magyar', 'id': 'Hungaria', 'is': 'ungverska', 'it': 'ungherese', 'ja': 'ハンガリー語', 'ko': '헝가리어', 'lt': 'vengrų', 'nb': 'ungarsk', 'nl': 'Hongaars', 'nn': 'ungarsk', 'pl': 'węgierski', 'pt': 'húngaro', 'pt_BR': 'húngaro', 'ro': 'maghiară', 'ru': 'венгерский', 'sk': 'maďarčina', 'sl': 'madžarščina', 'sr': 'мађарски', 'sv': 'ungerska', 'th': 'ฮังการี', 'tr': 'Macarca', 'uk': 'угорська', 'vi': 'Tiếng Hungary', 'zh_CN': '匈牙利语', 'zh_TW': '匈牙利文'}, 'id': { '_native': 'Indonesia', 'ar': 'الإندونيسية', 'bg': 'индонезийски', 'bs': 'indonezijski', 'ca': 'indonesi', 'cs': 'indonéština', 'da': 'indonesisk', 'de': 'Indonesisch', 'el': 'Ινδονησιακά', 'en': 'Indonesian', 'eo': 'indonezia', 'es': 'indonesio', 'et': 'indoneesia', 'eu': 'indonesiera', 'fa': 'اندونزیایی', 'fi': 'indonesia', 'fo': 'indonesiskt', 'fr': 'indonésien', 'gl': 'indonesio', 'he': 'אינדונזית', 'hr': 'indonezijski', 'hu': 'indonéz', 'id': 'Indonesia', 'is': 'indónesíska', 'it': 'indonesiano', 'ja': 'インドネシア語', 'ko': '인도네시아어', 'lt': 'indoneziečių', 'nb': 'indonesisk', 'nl': 'Indonesisch', 'nn': 'indonesisk', 'pl': 'indonezyjski', 'pt': 'indonésio', 'pt_BR': 'indonésio', 'ro': 'indoneziană', 'ru': 'индонезийский', 'sk': 'indonézština', 'sl': 'indonezijščina', 'sr': 'индонежански', 'sv': 'indonesiska', 'th': 'อินโดนีเซีย', 'tr': 'Endonezce', 'uk': 'індонезійська', 'vi': 'Tiếng Indonesia', 'zh_CN': '印度尼西亚语', 'zh_TW': '印尼文'}, 'is': { '_native': 'íslenska', 'ar': 'الأيسلندية', 'bg': 'исландски', 'bs': 'islandski', 'ca': 'islandès', 'cs': 'islandština', 'da': 'islandsk', 'de': 'Isländisch', 'el': 'Ισλανδικά', 'en': 'Icelandic', 'eo': 'islanda', 'es': 'islandés', 'et': 'islandi', 'eu': 'islandiera', 'fa': 'ایسلندی', 'fi': 'islanti', 'fo': 'íslendskt', 'fr': 'islandais', 'gl': 'islandés', 'he': 'איסלנדית', 'hr': 'islandski', 'hu': 'izlandi', 'id': 'Islandia', 'is': 'íslenska', 'it': 'islandese', 'ja': 'アイスランド語', 'ko': '아이슬란드어', 'lt': 'islandų', 'nb': 'islandsk', 'nl': 'IJslands', 'nn': 'islandsk', 'pl': 'islandzki', 'pt': 'islandês', 'pt_BR': 'islandês', 'ro': 'islandeză', 'ru': 'исландский', 'sk': 'islandčina', 'sl': 'islandščina', 'sr': 'исландски', 'sv': 'isländska', 'th': 'ไอซ์แลนด์', 'tr': 'İzlandaca', 'uk': 'ісландська', 'vi': 'Tiếng Iceland', 'zh_CN': '冰岛语', 'zh_TW': '冰島文'}, 'it': { '_native': 'italiano', 'ar': 'الإيطالية', 'bg': 'италиански', 'bs': 'italijanski', 'ca': 'italià', 'cs': 'italština', 'da': 'italiensk', 'de': 'Italienisch', 'el': 'Ιταλικά', 'en': 'Italian', 'eo': 'itala', 'es': 'italiano', 'et': 'itaalia', 'eu': 'italiera', 'fa': 'ایتالیایی', 'fi': 'italia', 'fo': 'italskt', 'fr': 'italien', 'gl': 'italiano', 'he': 'איטלקית', 'hr': 'talijanski', 'hu': 'olasz', 'id': 'Italia', 'is': 'ítalska', 'it': 'italiano', 'ja': 'イタリア語', 'ko': '이탈리아어', 'lt': 'italų', 'nb': 'italiensk', 'nl': 'Italiaans', 'nn': 'italiensk', 'pl': 'włoski', 'pt': 'italiano', 'pt_BR': 'italiano', 'ro': 'italiană', 'ru': 'итальянский', 'sk': 'taliančina', 'sl': 'italijanščina', 'sr': 'италијански', 'sv': 'italienska', 'th': 'อิตาลี', 'tr': 'İtalyanca', 'uk': 'італійська', 'vi': 'Tiếng Italy', 'zh_CN': '意大利语', 'zh_TW': '義大利文'}, 'ja': { '_native': '日本語', 'ar': 'اليابانية', 'bg': 'японски', 'bs': 'japanski', 'ca': 'japonès', 'cs': 'japonština', 'da': 'japansk', 'de': 'Japanisch', 'el': 'Ιαπωνικά', 'en': 'Japanese', 'eo': 'japana', 'es': 'japonés', 'et': 'jaapani', 'eu': 'japoniera', 'fa': 'ژاپنی', 'fi': 'japani', 'fo': 'japanskt', 'fr': 'japonais', 'gl': 'xaponés', 'he': 'יפנית', 'hr': 'japanski', 'hu': 'japán', 'id': 'Jepang', 'is': 'japanska', 'it': 'giapponese', 'ja': '日本語', 'ko': '일본어', 'lt': 'japonų', 'nb': 'japansk', 'nl': 'Japans', 'nn': 'japansk', 'pl': 'japoński', 'pt': 'japonês', 'pt_BR': 'japonês', 'ro': 'japoneză', 'ru': 'японский', 'sk': 'japončina', 'sl': 'japonščina', 'sr': 'јапански', 'sv': 'japanska', 'th': 'ญี่ปุ่น', 'tr': 'Japonca', 'uk': 'японська', 'vi': 'Tiếng Nhật', 'zh_CN': '日语', 'zh_TW': '日文'}, 'ko': { '_native': '한국어', 'ar': 'الكورية', 'bg': 'корейски', 'bs': 'korejski', 'ca': 'coreà', 'cs': 'korejština', 'da': 'koreansk', 'de': 'Koreanisch', 'el': 'Κορεατικά', 'en': 'Korean', 'eo': 'korea', 'es': 'coreano', 'et': 'korea', 'eu': 'koreera', 'fa': 'کره\u200cای', 'fi': 'korea', 'fo': 'koreanskt', 'fr': 'coréen', 'gl': 'coreano', 'he': 'קוריאנית', 'hr': 'korejski', 'hu': 'koreai', 'id': 'Korea', 'is': 'kóreska', 'it': 'coreano', 'ja': '韓国語', 'ko': '한국어', 'lt': 'korėjiečių', 'nb': 'koreansk', 'nl': 'Koreaans', 'nn': 'koreansk', 'pl': 'koreański', 'pt': 'coreano', 'pt_BR': 'coreano', 'ro': 'coreeană', 'ru': 'корейский', 'sk': 'kórejčina', 'sl': 'korejščina', 'sr': 'корејски', 'sv': 'koreanska', 'th': 'เกาหลี', 'tr': 'Korece', 'uk': 'корейська', 'vi': 'Tiếng Hàn', 'zh_CN': '韩语', 'zh_TW': '韓文'}, 'lt': { '_native': 'lietuvių', 'ar': 'الليتوانية', 'bg': 'литовски', 'bs': 'litvanski', 'ca': 'lituà', 'cs': 'litevština', 'da': 'litauisk', 'de': 'Litauisch', 'el': 'Λιθουανικά', 'en': 'Lithuanian', 'eo': 'litova', 'es': 'lituano', 'et': 'leedu', 'eu': 'lituaniera', 'fa': 'لیتوانیایی', 'fi': 'liettua', 'fo': 'litaviskt', 'fr': 'lituanien', 'gl': 'lituano', 'he': 'ליטאית', 'hr': 'litavski', 'hu': 'litván', 'id': 'Lituavi', 'is': 'litháíska', 'it': 'lituano', 'ja': 'リトアニア語', 'ko': '리투아니아어', 'lt': 'lietuvių', 'nb': 'litauisk', 'nl': 'Litouws', 'nn': 'litauisk', 'pl': 'litewski', 'pt': 'lituano', 'pt_BR': 'lituano', 'ro': 'lituaniană', 'ru': 'литовский', 'sk': 'litovčina', 'sl': 'litovščina', 'sr': 'литвански', 'sv': 'litauiska', 'th': 'ลิทัวเนีย', 'tr': 'Litvanca', 'uk': 'литовська', 'vi': 'Tiếng Litva', 'zh_CN': '立陶宛语', 'zh_TW': '立陶宛文'}, 'nb': { '_native': 'norsk bokmål', 'ar': 'النرويجية بوكمال', 'bg': 'норвежки (букмол)', 'bs': 'norveški (Bokmal)', 'ca': 'noruec bokmål', 'cs': 'norština (bokmål)', 'da': 'bokmål', 'de': 'Norwegisch (Bokmål)', 'el': 'Νορβηγικά Μποκμάλ', 'en': 'Norwegian Bokmål', 'eo': 'dannorvega', 'es': 'noruego bokmal', 'et': 'norra bokmål', 'eu': 'bokmål (norvegiera)', 'fa': 'نروژی بوک\u200cمُل', 'fi': 'norjan bokmål', 'fo': 'norskt bókmál', 'fr': 'norvégien bokmål', 'gl': 'noruegués bokmål', 'he': 'נורווגית ספרותית', 'hr': 'norveški bokmål', 'hu': 'norvég (bokmål)', 'id': 'Bokmål Norwegia', 'is': 'norskt bókmál', 'it': 'norvegese bokmål', 'ja': 'ノルウェー語(ブークモール)', 'ko': '노르웨이어(보크말)', 'lt': 'norvegų bukmolas', 'nb': 'norsk bokmål', 'nl': 'Noors - Bokmål', 'nn': 'norsk bokmål', 'pl': 'norweski (bokmål)', 'pt': 'bokmål norueguês', 'pt_BR': 'bokmål norueguês', 'ro': 'norvegiană bokmål', 'ru': 'норвежский букмол', 'sk': 'nórčina (bokmal)', 'sl': 'knjižna norveščina', 'sr': 'норвешки букмол', 'sv': 'norskt bokmål', 'th': 'นอร์เวย์บุคมอล', 'tr': 'Norveççe Bokmål', 'uk': 'норвезька (букмол)', 'vi': 'Tiếng Na Uy (Bokmål)', 'zh_CN': '书面挪威语', 'zh_TW': '巴克摩挪威文'}, 'nl': { '_native': 'Nederlands', 'ar': 'الهولندية', 'bg': 'нидерландски', 'bs': 'holandski', 'ca': 'neerlandès', 'cs': 'nizozemština', 'da': 'nederlandsk', 'de': 'Niederländisch', 'el': 'Ολλανδικά', 'en': 'Dutch', 'eo': 'nederlanda', 'es': 'neerlandés', 'et': 'hollandi', 'eu': 'nederlandera', 'fa': 'هلندی', 'fi': 'hollanti', 'fo': 'hálendskt', 'fr': 'néerlandais', 'gl': 'neerlandés', 'he': 'הולנדית', 'hr': 'nizozemski', 'hu': 'holland', 'id': 'Belanda', 'is': 'hollenska', 'it': 'olandese', 'ja': 'オランダ語', 'ko': '네덜란드어', 'lt': 'olandų', 'nb': 'nederlandsk', 'nl': 'Nederlands', 'nn': 'nederlandsk', 'pl': 'niderlandzki', 'pt': 'holandês', 'pt_BR': 'holandês', 'ro': 'neerlandeză', 'ru': 'нидерландский', 'sk': 'holandčina', 'sl': 'nizozemščina', 'sr': 'холандски', 'sv': 'nederländska', 'th': 'ดัตช์', 'tr': 'Felemenkçe', 'uk': 'нідерландська', 'vi': 'Tiếng Hà Lan', 'zh_CN': '荷兰语', 'zh_TW': '荷蘭文'}, 'nn': { '_native': 'norsk nynorsk', 'ar': 'النرويجية نينورسك', 'bg': 'норвежки (нюношк)', 'bs': 'norveški (Nynorsk)', 'ca': 'noruec nynorsk', 'cs': 'norština (nynorsk)', 'da': 'nynorsk', 'de': 'Norwegisch (Nynorsk)', 'el': 'Νορβηγικά Νινόρσκ', 'en': 'Norwegian Nynorsk', 'eo': 'novnorvega', 'es': 'noruego nynorsk', 'et': 'uusnorra', 'eu': 'nynorsk (norvegiera)', 'fa': 'نروژی نی\u200cنُشک', 'fi': 'norjan nynorsk', 'fo': 'nýnorskt', 'fr': 'norvégien nynorsk', 'gl': 'noruegués nynorsk', 'he': 'נורווגית חדשה', 'hr': 'norveški nynorsk', 'hu': 'norvég (nynorsk)', 'id': 'Nynorsk Norwegia', 'is': 'nýnorska', 'it': 'norvegese nynorsk', 'ja': 'ノルウェー語(ニーノシュク)', 'ko': '노르웨이어(니노르스크)', 'lt': 'naujoji norvegų', 'nb': 'norsk nynorsk', 'nl': 'Noors - Nynorsk', 'nn': 'norsk nynorsk', 'pl': 'norweski (nynorsk)', 'pt': 'nynorsk norueguês', 'pt_BR': 'nynorsk norueguês', 'ro': 'norvegiană nynorsk', 'ru': 'нюнорск', 'sk': 'nórčina (nynorsk)', 'sl': 'novonorveščina', 'sr': 'норвешки нинорск', 'sv': 'nynorska', 'th': 'นอร์เวย์นีนอสก์', 'tr': 'Norveççe Nynorsk', 'uk': 'норвезька (нюношк)', 'vi': 'Tiếng Na Uy (Nynorsk)', 'zh_CN': '挪威尼诺斯克语', 'zh_TW': '耐諾斯克挪威文'}, 'pl': { '_native': 'polski', 'ar': 'البولندية', 'bg': 'полски', 'bs': 'poljski', 'ca': 'polonès', 'cs': 'polština', 'da': 'polsk', 'de': 'Polnisch', 'el': 'Πολωνικά', 'en': 'Polish', 'eo': 'pola', 'es': 'polaco', 'et': 'poola', 'eu': 'poloniera', 'fa': 'لهستانی', 'fi': 'puola', 'fo': 'pólskt', 'fr': 'polonais', 'gl': 'polaco', 'he': 'פולנית', 'hr': 'poljski', 'hu': 'lengyel', 'id': 'Polski', 'is': 'pólska', 'it': 'polacco', 'ja': 'ポーランド語', 'ko': '폴란드어', 'lt': 'lenkų', 'nb': 'polsk', 'nl': 'Pools', 'nn': 'polsk', 'pl': 'polski', 'pt': 'polonês', 'pt_BR': 'polonês', 'ro': 'poloneză', 'ru': 'польский', 'sk': 'poľština', 'sl': 'poljščina', 'sr': 'пољски', 'sv': 'polska', 'th': 'โปแลนด์', 'tr': 'Lehçe', 'uk': 'польська', 'vi': 'Tiếng Ba Lan', 'zh_CN': '波兰语', 'zh_TW': '波蘭文'}, 'pt': { '_native': 'português', 'ar': 'البرتغالية', 'bg': 'португалски', 'bs': 'portugalski', 'ca': 'portuguès', 'cs': 'portugalština', 'da': 'portugisisk', 'de': 'Portugiesisch', 'el': 'Πορτογαλικά', 'en': 'Portuguese', 'eo': 'portugala', 'es': 'portugués', 'et': 'portugali', 'eu': 'portugesa', 'fa': 'پرتغالی', 'fi': 'portugali', 'fo': 'portugiskiskt', 'fr': 'portugais', 'gl': 'portugués', 'he': 'פורטוגזית', 'hr': 'portugalski', 'hu': 'portugál', 'id': 'Portugis', 'is': 'portúgalska', 'it': 'portoghese', 'ja': 'ポルトガル語', 'ko': '포르투갈어', 'lt': 'portugalų', 'nb': 'portugisisk', 'nl': 'Portugees', 'nn': 'portugisisk', 'pl': 'portugalski', 'pt': 'português', 'pt_BR': 'português', 'ro': 'portugheză', 'ru': 'португальский', 'sk': 'portugalčina', 'sl': 'portugalščina', 'sr': 'португалски', 'sv': 'portugisiska', 'th': 'โปรตุเกส', 'tr': 'Portekizce', 'uk': 'португальська', 'vi': 'Tiếng Bồ Đào Nha', 'zh_CN': '葡萄牙语', 'zh_TW': '葡萄牙文'}, 'pt_BR': { '_native': 'português (Brasil)', 'ar': 'البرتغالية (البرازيل)', 'bg': 'португалски (Бразилия)', 'bs': 'portugalski (Brazil)', 'ca': 'portuguès (Brasil)', 'cs': 'portugalština (Brazílie)', 'da': 'portugisisk (Brasilien)', 'de': 'Portugiesisch (Brasilien)', 'el': 'Πορτογαλικά (Βραζιλία)', 'en': 'Portuguese (Brazil)', 'eo': 'portugala (Brazilo)', 'es': 'portugués (Brasil)', 'et': 'portugali (Brasiilia)', 'eu': 'portugesa (Brasil)', 'fa': 'پرتغالی (برزیل)', 'fi': 'portugali (Brasilia)', 'fo': 'portugiskiskt (Brasil)', 'fr': 'portugais (Brésil)', 'gl': 'portugués (O Brasil)', 'he': 'פורטוגזית (ברזיל)', 'hr': 'portugalski (Brazil)', 'hu': 'portugál (Brazília)', 'id': 'Portugis (Brasil)', 'is': 'portúgalska (Brasilía)', 'it': 'portoghese (Brasile)', 'ja': 'ポルトガル語 (ブラジル)', 'ko': '포르투갈어 (브라질)', 'lt': 'portugalų (Brazilija)', 'nb': 'portugisisk (Brasil)', 'nl': 'Portugees (Brazilië)', 'nn': 'portugisisk (Brasil)', 'pl': 'portugalski (Brazylia)', 'pt': 'português (Brasil)', 'pt_BR': 'português (Brasil)', 'ro': 'portugheză (Brazilia)', 'ru': 'португальский (Бразилия)', 'sk': 'portugalčina (Brazília)', 'sl': 'portugalščina (Brazilija)', 'sr': 'португалски (Бразил)', 'sv': 'portugisiska (Brasilien)', 'th': 'โปรตุเกส (บราซิล)', 'tr': 'Portekizce (Brezilya)', 'uk': 'португальська (Бразилія)', 'vi': 'Tiếng Bồ Đào Nha (Brazil)', 'zh_CN': '葡萄牙语 (巴西)', 'zh_TW': '葡萄牙文 (巴西)'}, 'ro': { '_native': 'română', 'ar': 'الرومانية', 'bg': 'румънски', 'bs': 'rumunski', 'ca': 'romanès', 'cs': 'rumunština', 'da': 'rumænsk', 'de': 'Rumänisch', 'el': 'Ρουμανικά', 'en': 'Romanian', 'eo': 'rumana', 'es': 'rumano', 'et': 'rumeenia', 'eu': 'errumaniera', 'fa': 'رومانیایی', 'fi': 'romania', 'fo': 'rumenskt', 'fr': 'roumain', 'gl': 'romanés', 'he': 'רומנית', 'hr': 'rumunjski', 'hu': 'román', 'id': 'Rumania', 'is': 'rúmenska', 'it': 'rumeno', 'ja': 'ルーマニア語', 'ko': '루마니아어', 'lt': 'rumunų', 'nb': 'rumensk', 'nl': 'Roemeens', 'nn': 'rumensk', 'pl': 'rumuński', 'pt': 'romeno', 'pt_BR': 'romeno', 'ro': 'română', 'ru': 'румынский', 'sk': 'rumunčina', 'sl': 'romunščina', 'sr': 'румунски', 'sv': 'rumänska', 'th': 'โรมาเนีย', 'tr': 'Rumence', 'uk': 'румунська', 'vi': 'Tiếng Romania', 'zh_CN': '罗马尼亚语', 'zh_TW': '羅馬尼亞文'}, 'ru': { '_native': 'русский', 'ar': 'الروسية', 'bg': 'руски', 'bs': 'ruski', 'ca': 'rus', 'cs': 'ruština', 'da': 'russisk', 'de': 'Russisch', 'el': 'Ρωσικά', 'en': 'Russian', 'eo': 'rusa', 'es': 'ruso', 'et': 'vene', 'eu': 'errusiera', 'fa': 'روسی', 'fi': 'venäjä', 'fo': 'russiskt', 'fr': 'russe', 'gl': 'ruso', 'he': 'רוסית', 'hr': 'ruski', 'hu': 'orosz', 'id': 'Rusia', 'is': 'rússneska', 'it': 'russo', 'ja': 'ロシア語', 'ko': '러시아어', 'lt': 'rusų', 'nb': 'russisk', 'nl': 'Russisch', 'nn': 'russisk', 'pl': 'rosyjski', 'pt': 'russo', 'pt_BR': 'russo', 'ro': 'rusă', 'ru': 'русский', 'sk': 'ruština', 'sl': 'ruščina', 'sr': 'руски', 'sv': 'ryska', 'th': 'รัสเซีย', 'tr': 'Rusça', 'uk': 'російська', 'vi': 'Tiếng Nga', 'zh_CN': '俄语', 'zh_TW': '俄文'}, 'sk': { '_native': 'slovenčina', 'ar': 'السلوفاكية', 'bg': 'словашки', 'bs': 'slovački', 'ca': 'eslovac', 'cs': 'slovenština', 'da': 'slovakisk', 'de': 'Slowakisch', 'el': 'Σλοβακικά', 'en': 'Slovak', 'eo': 'slovaka', 'es': 'eslovaco', 'et': 'slovaki', 'eu': 'eslovakiera', 'fa': 'اسلواکی', 'fi': 'slovakki', 'fo': 'slovakiskt', 'fr': 'slovaque', 'gl': 'eslovaco', 'he': 'סלובקית', 'hr': 'slovački', 'hu': 'szlovák', 'id': 'Slovak', 'is': 'slóvakíska', 'it': 'slovacco', 'ja': 'スロバキア語', 'ko': '슬로바키아어', 'lt': 'slovakų', 'nb': 'slovakisk', 'nl': 'Slowaaks', 'nn': 'slovakisk', 'pl': 'słowacki', 'pt': 'eslovaco', 'pt_BR': 'eslovaco', 'ro': 'slovacă', 'ru': 'словацкий', 'sk': 'slovenčina', 'sl': 'slovaščina', 'sr': 'словачки', 'sv': 'slovakiska', 'th': 'สโลวัก', 'tr': 'Slovakça', 'uk': 'словацька', 'vi': 'Tiếng Slovak', 'zh_CN': '斯洛伐克语', 'zh_TW': '斯洛伐克文'}, 'sl': { '_native': 'slovenščina', 'ar': 'السلوفانية', 'bg': 'словенски', 'bs': 'slovenski', 'ca': 'eslovè', 'cs': 'slovinština', 'da': 'slovensk', 'de': 'Slowenisch', 'el': 'Σλοβενικά', 'en': 'Slovenian', 'eo': 'slovena', 'es': 'esloveno', 'et': 'sloveeni', 'eu': 'esloveniera', 'fa': 'اسلوونیایی', 'fi': 'sloveeni', 'fo': 'slovenskt', 'fr': 'slovène', 'gl': 'esloveno', 'he': 'סלובנית', 'hr': 'slovenski', 'hu': 'szlovén', 'id': 'Sloven', 'is': 'slóvenska', 'it': 'sloveno', 'ja': 'スロベニア語', 'ko': '슬로베니아어', 'lt': 'slovėnų', 'nb': 'slovensk', 'nl': 'Sloveens', 'nn': 'slovensk', 'pl': 'słoweński', 'pt': 'esloveno', 'pt_BR': 'esloveno', 'ro': 'slovenă', 'ru': 'словенский', 'sk': 'slovinčina', 'sl': 'slovenščina', 'sr': 'словеначки', 'sv': 'slovenska', 'th': 'สโลวีเนีย', 'tr': 'Slovence', 'uk': 'словенська', 'vi': 'Tiếng Slovenia', 'zh_CN': '斯洛文尼亚语', 'zh_TW': '斯洛維尼亞文'}, 'sr': { '_native': 'српски', 'ar': 'الصربية', 'bg': 'сръбски', 'bs': 'srpski', 'ca': 'serbi', 'cs': 'srbština', 'da': 'serbisk', 'de': 'Serbisch', 'el': 'Σερβικά', 'en': 'Serbian', 'eo': 'serba', 'es': 'serbio', 'et': 'serbia', 'eu': 'serbiera', 'fa': 'صربی', 'fi': 'serbia', 'fo': 'serbiskt', 'fr': 'serbe', 'gl': 'serbio', 'he': 'סרבית', 'hr': 'srpski', 'hu': 'szerb', 'id': 'Serbia', 'is': 'serbneska', 'it': 'serbo', 'ja': 'セルビア語', 'ko': '세르비아어', 'lt': 'serbų', 'nb': 'serbisk', 'nl': 'Servisch', 'nn': 'serbisk', 'pl': 'serbski', 'pt': 'sérvio', 'pt_BR': 'sérvio', 'ro': 'sârbă', 'ru': 'сербский', 'sk': 'srbčina', 'sl': 'srbščina', 'sr': 'српски', 'sv': 'serbiska', 'th': 'เซอร์เบีย', 'tr': 'Sırpça', 'uk': 'сербська', 'vi': 'Tiếng Serbia', 'zh_CN': '塞尔维亚语', 'zh_TW': '塞爾維亞文'}, 'sv': { '_native': 'svenska', 'ar': 'السويدية', 'bg': 'шведски', 'bs': 'švedski', 'ca': 'suec', 'cs': 'švédština', 'da': 'svensk', 'de': 'Schwedisch', 'el': 'Σουηδικά', 'en': 'Swedish', 'eo': 'sveda', 'es': 'sueco', 'et': 'rootsi', 'eu': 'suediera', 'fa': 'سوئدی', 'fi': 'ruotsi', 'fo': 'svenskt', 'fr': 'suédois', 'gl': 'sueco', 'he': 'שוודית', 'hr': 'švedski', 'hu': 'svéd', 'id': 'Swedia', 'is': 'sænska', 'it': 'svedese', 'ja': 'スウェーデン語', 'ko': '스웨덴어', 'lt': 'švedų', 'nb': 'svensk', 'nl': 'Zweeds', 'nn': 'svensk', 'pl': 'szwedzki', 'pt': 'sueco', 'pt_BR': 'sueco', 'ro': 'suedeză', 'ru': 'шведский', 'sk': 'švédčina', 'sl': 'švedščina', 'sr': 'шведски', 'sv': 'svenska', 'th': 'สวีเดน', 'tr': 'İsveççe', 'uk': 'шведська', 'vi': 'Tiếng Thụy Điển', 'zh_CN': '瑞典语', 'zh_TW': '瑞典文'}, 'th': { '_native': 'ไทย', 'ar': 'التايلاندية', 'bg': 'тайски', 'bs': 'tajlandski', 'ca': 'tai', 'cs': 'thajština', 'da': 'thai', 'de': 'Thailändisch', 'el': 'Ταϊλανδικά', 'en': 'Thai', 'eo': 'taja', 'es': 'tailandés', 'et': 'tai', 'eu': 'thailandiera', 'fa': 'تایلندی', 'fi': 'thai', 'fo': 'tailendskt', 'fr': 'thaï', 'gl': 'tailandés', 'he': 'תאית', 'hr': 'tajlandski', 'hu': 'thai', 'id': 'Thai', 'is': 'taílenska', 'it': 'thailandese', 'ja': 'タイ語', 'ko': '태국어', 'lt': 'tajų', 'nb': 'thai', 'nl': 'Thai', 'nn': 'thai', 'pl': 'tajski', 'pt': 'tailandês', 'pt_BR': 'tailandês', 'ro': 'thailandeză', 'ru': 'тайский', 'sk': 'thajčina', 'sl': 'tajščina', 'sr': 'тајски', 'sv': 'thailändska', 'th': 'ไทย', 'tr': 'Tayca', 'uk': 'тайська', 'vi': 'Tiếng Thái', 'zh_CN': '泰语', 'zh_TW': '泰文'}, 'tr': { '_native': 'Türkçe', 'ar': 'التركية', 'bg': 'турски', 'bs': 'turski', 'ca': 'turc', 'cs': 'turečtina', 'da': 'tyrkisk', 'de': 'Türkisch', 'el': 'Τουρκικά', 'en': 'Turkish', 'eo': 'turka', 'es': 'turco', 'et': 'türgi', 'eu': 'turkiera', 'fa': 'ترکی استانبولی', 'fi': 'turkki', 'fo': 'turkiskt', 'fr': 'turc', 'gl': 'turco', 'he': 'טורקית', 'hr': 'turski', 'hu': 'török', 'id': 'Turki', 'is': 'tyrkneska', 'it': 'turco', 'ja': 'トルコ語', 'ko': '터키어', 'lt': 'turkų', 'nb': 'tyrkisk', 'nl': 'Turks', 'nn': 'tyrkisk', 'pl': 'turecki', 'pt': 'turco', 'pt_BR': 'turco', 'ro': 'turcă', 'ru': 'турецкий', 'sk': 'turečtina', 'sl': 'turščina', 'sr': 'турски', 'sv': 'turkiska', 'th': 'ตุรกี', 'tr': 'Türkçe', 'uk': 'турецька', 'vi': 'Tiếng Thổ Nhĩ Kỳ', 'zh_CN': '土耳其语', 'zh_TW': '土耳其文'}, 'uk': { '_native': 'українська', 'ar': 'الأوكرانية', 'bg': 'украински', 'bs': 'ukrajinski', 'ca': 'ucraïnès', 'cs': 'ukrajinština', 'da': 'ukrainsk', 'de': 'Ukrainisch', 'el': 'Ουκρανικά', 'en': 'Ukrainian', 'eo': 'ukraina', 'es': 'ucraniano', 'et': 'ukraina', 'eu': 'ukrainera', 'fa': 'اوکراینی', 'fi': 'ukraina', 'fo': 'ukrainskt', 'fr': 'ukrainien', 'gl': 'ucraíno', 'he': 'אוקראינית', 'hr': 'ukrajinski', 'hu': 'ukrán', 'id': 'Ukraina', 'is': 'úkraínska', 'it': 'ucraino', 'ja': 'ウクライナ語', 'ko': '우크라이나어', 'lt': 'ukrainiečių', 'nb': 'ukrainsk', 'nl': 'Oekraïens', 'nn': 'ukrainsk', 'pl': 'ukraiński', 'pt': 'ucraniano', 'pt_BR': 'ucraniano', 'ro': 'ucraineană', 'ru': 'украинский', 'sk': 'ukrajinčina', 'sl': 'ukrajinščina', 'sr': 'украјински', 'sv': 'ukrainska', 'th': 'ยูเครน', 'tr': 'Ukraynaca', 'uk': 'українська', 'vi': 'Tiếng Ukraina', 'zh_CN': '乌克兰语', 'zh_TW': '烏克蘭文'}, 'vi': { '_native': 'Tiếng Việt', 'ar': 'الفيتنامية', 'bg': 'виетнамски', 'bs': 'vijetnamski', 'ca': 'vietnamita', 'cs': 'vietnamština', 'da': 'vietnamesisk', 'de': 'Vietnamesisch', 'el': 'Βιετναμικά', 'en': 'Vietnamese', 'eo': 'vjetnama', 'es': 'vietnamita', 'et': 'vietnami', 'eu': 'vietnamera', 'fa': 'ویتنامی', 'fi': 'vietnam', 'fo': 'vjetnamesiskt', 'fr': 'vietnamien', 'gl': 'vietnamita', 'he': 'וייטנאמית', 'hr': 'vijetnamski', 'hu': 'vietnámi', 'id': 'Vietnam', 'is': 'víetnamska', 'it': 'vietnamita', 'ja': 'ベトナム語', 'ko': '베트남어', 'lt': 'vietnamiečių', 'nb': 'vietnamesisk', 'nl': 'Vietnamees', 'nn': 'vietnamesisk', 'pl': 'wietnamski', 'pt': 'vietnamita', 'pt_BR': 'vietnamita', 'ro': 'vietnameză', 'ru': 'вьетнамский', 'sk': 'vietnamčina', 'sl': 'vietnamščina', 'sr': 'вијетнамски', 'sv': 'vietnamesiska', 'th': 'เวียดนาม', 'tr': 'Vietnamca', 'uk': 'вʼєтнамська', 'vi': 'Tiếng Việt', 'zh_CN': '越南语', 'zh_TW': '越南文'}, 'zh_CN': { '_native': '中文 (简体, 中国)', 'ar': 'الصينية (المبسطة, الصين)', 'bg': 'китайски (опростена, Китай)', 'bs': 'kineski (pojednostavljeno, Kina)', 'ca': 'xinès (simplificat, Xina)', 'cs': 'čínština (zjednodušené, Čína)', 'da': 'kinesisk (forenklet, Kina)', 'de': 'Chinesisch (Vereinfacht, China)', 'el': 'Κινεζικά (Απλοποιημένο, Κίνα)', 'en': 'Chinese (Simplified, China)', 'eo': 'ĉina (simpligita, Ĉinujo)', 'es': 'chino (simplificado, China)', 'et': 'hiina (lihtsustatud, Hiina)', 'eu': 'txinera (sinplifikatua, Txina)', 'fa': 'چینی (ساده\u200cشده, چین)', 'fi': 'kiina (yksinkertaistettu, Kiina)', 'fo': 'kinesiskt (einkult, Kina)', 'fr': 'chinois (simplifié, Chine)', 'gl': 'chinés (simplificado, A China)', 'he': 'סינית (פשוט, סין)', 'hr': 'kineski (pojednostavljeno pismo, Kina)', 'hu': 'kínai (Egyszerűsített, Kína)', 'id': 'Tionghoa (Sederhana, Tiongkok)', 'is': 'kínverska (einfaldað, Kína)', 'it': 'cinese (semplificato, Cina)', 'ja': '中国語 (簡体字, 中国)', 'ko': '중국어 (간체, 중국)', 'lt': 'kinų (supaprastinti, Kinija)', 'nb': 'kinesisk (forenklet, Kina)', 'nl': 'Chinees (vereenvoudigd, China)', 'nn': 'kinesisk (forenkla, Kina)', 'pl': 'chiński (uproszczone, Chiny)', 'pt': 'chinês (simplificado, China)', 'pt_BR': 'chinês (simplificado, China)', 'ro': 'chineză (simplificată, China)', 'ru': 'китайский (упрощенная, Китай)', 'sk': 'čínština (zjednodušené, Čína)', 'sl': 'kitajščina (poenostavljena pisava, Kitajska)', 'sr': 'кинески (поједностављено кинеско писмо, Кина)', 'sv': 'kinesiska (förenklad, Kina)', 'th': 'จีน (ตัวย่อ, จีน)', 'tr': 'Çince (Basitleştirilmiş, Çin)', 'uk': 'китайська (спрощена, Китай)', 'vi': 'Tiếng Trung (Giản thể, Trung Quốc)', 'zh_CN': '中文 (简体, 中国)', 'zh_TW': '中文 (簡體, 中國)'}, 'zh_TW': { '_native': '中文 (繁體, 台灣)', 'ar': 'الصينية (التقليدية, تايوان)', 'bg': 'китайски (традиционна, Тайван)', 'bs': 'kineski (tradicionalno, Tajvan)', 'ca': 'xinès (tradicional, Taiwan)', 'cs': 'čínština (tradiční, Tchaj-wan)', 'da': 'kinesisk (traditionelt, Taiwan)', 'de': 'Chinesisch (Traditionell, Taiwan)', 'el': 'Κινεζικά (Παραδοσιακό, Ταϊβάν)', 'en': 'Chinese (Traditional, Taiwan)', 'eo': 'ĉina (tradicia, Tajvano)', 'es': 'chino (tradicional, Taiwán)', 'et': 'hiina (traditsiooniline, Taiwan)', 'eu': 'txinera (tradizionala, Taiwan)', 'fa': 'چینی (سنتی, تایوان)', 'fi': 'kiina (perinteinen, Taiwan)', 'fo': 'kinesiskt (vanligt, Taivan)', 'fr': 'chinois (traditionnel, Taïwan)', 'gl': 'chinés (tradicional, Taiwán)', 'he': 'סינית (מסורתי, טייוואן)', 'hr': 'kineski (tradicionalno pismo, Tajvan)', 'hu': 'kínai (Hagyományos, Tajvan)', 'id': 'Tionghoa (Tradisional, Taiwan)', 'is': 'kínverska (hefðbundið, Taívan)', 'it': 'cinese (tradizionale, Taiwan)', 'ja': '中国語 (繁体字, 台湾)', 'ko': '중국어 (번체, 대만)', 'lt': 'kinų (tradiciniai, Taivanas)', 'nb': 'kinesisk (tradisjonell, Taiwan)', 'nl': 'Chinees (traditioneel, Taiwan)', 'nn': 'kinesisk (tradisjonell, Taiwan)', 'pl': 'chiński (tradycyjne, Tajwan)', 'pt': 'chinês (tradicional, Taiwan)', 'pt_BR': 'chinês (tradicional, Taiwan)', 'ro': 'chineză (tradițională, Taiwan)', 'ru': 'китайский (традиционная, Тайвань)', 'sk': 'čínština (tradičné, Taiwan)', 'sl': 'kitajščina (tradicionalna pisava, Tajvan)', 'sr': 'кинески (традиционално кинеско писмо, Тајван)', 'sv': 'kinesiska (traditionell, Taiwan)', 'th': 'จีน (ตัวเต็ม, ไต้หวัน)', 'tr': 'Çince (Geleneksel, Tayvan)', 'uk': 'китайська (традиційна, Тайвань)', 'vi': 'Tiếng Trung (Phồn thể, Đài Loan)', 'zh_CN': '中文 (繁体, 台湾)', 'zh_TW': '中文 (繁體, 台灣)'}} completeness = { 'ar': 100, 'bg': 78, 'bs': 25, 'ca': 88, 'cs': 77, 'da': 100, 'de': 100, 'el': 53, 'en': 100, 'eo': 100, 'es': 100, 'et': 53, 'eu': 97, 'fa': 81, 'fi': 86, 'fo': 30, 'fr': 100, 'gl': 100, 'he': 84, 'hr': 35, 'hu': 84, 'id': 100, 'is': 81, 'it': 95, 'ja': 83, 'ko': 88, 'lt': 55, 'nb': 82, 'nl': 100, 'nn': 76, 'pl': 100, 'pt': 100, 'pt_BR': 93, 'ro': 100, 'ru': 76, 'sk': 46, 'sl': 94, 'sr': 100, 'sv': 61, 'th': 63, 'tr': 99, 'uk': 100, 'vi': 82, 'zh_CN': 100, 'zh_TW': 99} backintime-1.5.2/common/logger.py000066400000000000000000000105101465446530500167570ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 syslog import os import sys import atexit import bcolors DEBUG = False # Set to "True" when passing "--debug" as cmd arg SYSLOG_IDENTIFIER = 'backintime' SYSLOG_MESSAGE_PREFIX = '' # Labels for the syslog levels _level_names = { syslog.LOG_INFO: 'INFO', syslog.LOG_ERR: 'ERROR', syslog.LOG_WARNING: 'WARNING', syslog.LOG_DEBUG: 'DEBUG' } def openlog(): """ Initialize the BiT logger system (which uses syslog) Esp. sets the app name as identifier for the log entries in the syslog. Attention: Call it in each sub process that uses logging. """ syslog.openlog(SYSLOG_IDENTIFIER) atexit.register(closelog) def changeProfile(profile_id, profile_name): global SYSLOG_MESSAGE_PREFIX SYSLOG_MESSAGE_PREFIX = f'{profile_name}({profile_id}) :: ' def closelog(): syslog.closelog() def _do_syslog(message: str, level: int) -> str: syslog.syslog(level, '{}{}: {}'.format( SYSLOG_MESSAGE_PREFIX, _level_names[level], message)) def error(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) _do_syslog(msg, syslog.LOG_ERR) def warning(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) _do_syslog(msg, syslog.LOG_WARNING) def info(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.stderr) _do_syslog(msg, syslog.LOG_INFO) def debug(msg, parent=None, traceDepth=0): if DEBUG: msg = '%s %s' % (_debugHeader(parent, traceDepth), msg) # Why does this code differ from eg. "error()" # (where the following lines are NOT part of the "if")? print('%sDEBUG%s: %s' % (bcolors.OKBLUE, bcolors.ENDC, msg), file=sys.stderr) _do_syslog(msg, syslog.LOG_DEBUG) def deprecated(parent=None): """Dev note (buhtz 2023-07-23): To my knowledge this function is called only one time in BIT. I assume it could be replace with python's own deprecation warning system. """ 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 msg = '%s/%s:%s %s%s called from ' %(fmodule, fname, line, fclass, func) msgCaller = '%s/%s:%s' %(fmoduleCaller, fnameCaller, lineCaller) print('%sDEPRECATED%s: %s%s%s%s' %(bcolors.WARNING, bcolors.ENDC, msg, bcolors.OKBLUE, msgCaller, bcolors.ENDC), file=sys.stderr) _do_syslog('DEPRECATED: %s%s' % (msg, msgCaller), syslog.LOG_WARNING) 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 fclass = '%s.' % parent.__class__.__name__ if parent else '' func = frame.f_code.co_name return '[%s/%s:%s %s%s]' % (fmodule, fname, line, fclass, func) backintime-1.5.2/common/man/000077500000000000000000000000001465446530500157045ustar00rootroot00000000000000backintime-1.5.2/common/man/C/000077500000000000000000000000001465446530500160665ustar00rootroot00000000000000backintime-1.5.2/common/man/C/backintime-askpass.1000066400000000000000000000017721465446530500217300ustar00rootroot00000000000000.TH backintime-askpass 1 "August 2024" "version 1.5.2" "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 .BR backintime (1), .BR backintime-qt (1), .BR backintime-config (1), .PP \fBBack In Time\fP project website: https://github.com/bit-team/backintime .PP \fBBack In Time\fP mailing list: https://mail.python.org/mailman3/lists/bit-dev.python.org .SH AUTHOR \fBBack In Time\fP Team backintime-1.5.2/common/man/C/backintime-config.1000066400000000000000000000451641465446530500215330ustar00rootroot00000000000000.TH backintime-config 1 "August 2024" "version 1.5.2" "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 usable 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 .IP "\fIglobal.hash_collision\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Internal value used to prevent hash collisions on mountpoints. Do not change this. .PP Default: 0 .RE .IP "\fIglobal.language\fR" 6 .RS Type: str Allowed Values: text .br 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. .PP Default: '' .RE .IP "\fIglobal.use_flock\fR" 6 .RS Type: bool Allowed Values: true|false .br Prevent multiple snapshots (from different profiles or users) to be run at the same time .PP Default: false .RE .IP "\fIprofile.name\fR" 6 .RS Type: str Allowed Values: text .br Name of this profile. .PP Default: Main profile .RE .IP "\fIprofile.schedule.custom_time\fR" 6 .RS Type: str Allowed Values: comma separated int (8,12,18,23) or */3 .br Custom hours for cronjob. Only valid for \fIprofile.schedule.mode\fR = 19 .PP Default: 8,12,18,23 .RE .IP "\fIprofile.schedule.day\fR" 6 .RS Type: int Allowed Values: 1-28 .br Which day of month the cronjob should run? Only valid for \fIprofile.schedule.mode\fR >= 40 .PP Default: 1 .RE .IP "\fIprofile.schedule.mode\fR" 6 .RS Type: int Allowed Values: 0|1|2|4|7|10|12|14|16|18|19|20|25|27|30|40|80 .br Which schedule used for crontab. The crontab entry will be generated with 'backintime check-config'. .br 0 = Disabled .br 1 = at every boot .br 2 = every 5 minute .br 4 = every 10 minute .br 7 = every 30 minute .br 10 = every hour .br 12 = every 2 hours .br 14 = every 4 hours .br 16 = every 6 hours .br 18 = every 12 hours .br 19 = custom defined hours .br 20 = every day .br 25 = daily anacron .br 27 = when drive get connected .br 30 = every week .br 40 = every month .br 80 = every year .PP Default: 0 .RE .IP "\fIprofile.schedule.repeatedly.period\fR" 6 .RS Type: int Allowed Values: 0-99999 .br How many units to wait between new snapshots with anacron? Only valid for \fIprofile.schedule.mode\fR = 25|27 .PP Default: 1 .RE .IP "\fIprofile.schedule.repeatedly.unit\fR" 6 .RS Type: int Allowed Values: 10|20|30|40 .br Units to wait between new snapshots with anacron. .br 10 = hours .br 20 = days .br 30 = weeks .br 40 = months .br Only valid for \fIprofile.schedule.mode\fR = 25|27 .PP Default: 20 .RE .IP "\fIprofile.schedule.time\fR" 6 .RS Type: int Allowed Values: 0-2400 .br 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) .PP Default: 0 .RE .IP "\fIprofile.schedule.weekday\fR" 6 .RS Type: int Allowed Values: 1 = monday \- 7 = sunday .br Which day of week the cronjob should run? Only valid for \fIprofile.schedule.mode\fR = 30 .PP Default: 7 .RE .IP "\fIprofile.snapshots.backup_on_restore.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br Rename existing files before restore into FILE.backup.YYYYMMDD .PP Default: true .RE .IP "\fIprofile.snapshots.bwlimit.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br Limit rsync bandwidth usage over network. Use this with mode SSH. For mode Local you should rather use ionice. .PP Default: false .RE .IP "\fIprofile.snapshots.bwlimit.value\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Bandwidth limit in KB/sec. .PP Default: 3000 .RE .IP "\fIprofile.snapshots.continue_on_errors\fR" 6 .RS Type: bool Allowed Values: true|false .br Continue on errors. This will keep incomplete snapshots rather than deleting and start over again. .PP Default: true .RE .IP "\fIprofile.snapshots.copy_links\fR" 6 .RS Type: bool Allowed Values: true|false .br When symlinks are encountered, the item that they point to (the reference) is copied, rather than the symlink. .PP Default: false .RE .IP "\fIprofile.snapshots.copy_unsafe_links\fR" 6 .RS Type: bool Allowed Values: true|false .br This tells rsync to copy the referent of symbolic links that point outside the copied tree. Absolute symlinks are also treated like ordinary files. .PP Default: false .RE .IP "\fIprofile.snapshots.cron.ionice\fR" 6 .RS Type: bool Allowed Values: true|false .br Run cronjobs with 'ionice \-c2 \-n7'. This will give BackInTime the lowest IO bandwidth priority to not interrupt any other working process. .PP Default: true .RE .IP "\fIprofile.snapshots.cron.nice\fR" 6 .RS Type: bool Allowed Values: true|false .br Run cronjobs with 'nice \-n19'. This will give BackInTime the lowest CPU priority to not interrupt any other working process. .PP Default: true .RE .IP "\fIprofile.snapshots.cron.redirect_stderr\fR" 6 .RS Type: bool Allowed Values: true|false .br redirect stderr to /dev/null in cronjobs .PP Default: False .RE .IP "\fIprofile.snapshots.cron.redirect_stdout\fR" 6 .RS Type: bool Allowed Values: true|false .br redirect stdout to /dev/null in cronjobs .PP Default: true .RE .IP "\fIprofile.snapshots.dont_remove_named_snapshots\fR" 6 .RS Type: bool Allowed Values: true|false .br Keep snapshots with names during smart_remove. .PP Default: true .RE .IP "\fIprofile.snapshots.exclude.bysize.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br Enable exclude files by size. .PP Default: false .RE .IP "\fIprofile.snapshots.exclude.bysize.value\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Exclude files bigger than value in MiB. With 'Full rsync mode' disabled this will only affect new files because for rsync this is a transfer option, not an exclude option. So big files that has been backed up before will remain in snapshots even if they had changed. .PP Default: 500 .RE .IP "\fIprofile.snapshots.exclude..value\fR" 6 .RS Type: str Allowed Values: file, folder or pattern (relative or absolute) .br Exclude this file or folder. must be a counter starting with 1 .PP Default: '' .RE .IP "\fIprofile.snapshots.exclude.size\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Quantity of profile.snapshots.exclude. entries. .PP Default: \-1 .RE .IP "\fIprofile.snapshots.include..type\fR" 6 .RS Type: int Allowed Values: 0|1 .br Specify if \fIprofile.snapshots.include..value\fR is a folder (0) or a file (1). .PP Default: 0 .RE .IP "\fIprofile.snapshots.include..value\fR" 6 .RS Type: str Allowed Values: absolute path .br Include this file or folder. must be a counter starting with 1 .PP Default: '' .RE .IP "\fIprofile.snapshots.include.size\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Quantity of profile.snapshots.include. entries. .PP Default: \-1 .RE .IP "\fIprofile.snapshots.keep_only_one_snapshot.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br NOT YET IMPLEMENTED. Remove all snapshots but one. .PP Default: false .RE .IP "\fIprofile.snapshots.local.nocache\fR" 6 .RS Type: bool Allowed Values: true|false .br Run rsync on local machine with 'nocache'. This will prevent files from being cached in memory. .PP Default: false .RE .IP "\fIprofile.snapshots.local_encfs.path\fR" 6 .RS Type: str Allowed Values: absolute path .br Where to save snapshots in mode 'local_encfs'. .PP Default: '' .RE .IP "\fIprofile.snapshots.log_level\fR" 6 .RS Type: int Allowed Values: 1-3 .br Log level used during takeSnapshot. .br 1 = Error .br 2 = Changes .br 3 = Info .PP Default: 3 .RE .IP "\fIprofile.snapshots.min_free_inodes.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br Remove snapshots until \fIprofile.snapshots.min_free_inodes.value\fR free inodes in % is reached. .PP Default: true .RE .IP "\fIprofile.snapshots.min_free_inodes.value\fR" 6 .RS Type: int Allowed Values: 1-15 .br Keep at least value % free inodes. .PP Default: 2 .RE .IP "\fIprofile.snapshots.min_free_space.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br Remove snapshots until \fIprofile.snapshots.min_free_space.value\fR free space is reached. .PP Default: true .RE .IP "\fIprofile.snapshots.min_free_space.unit\fR" 6 .RS Type: int Allowed Values: 10|20 .br 10 = MB .br 20 = GB .PP Default: 20 .RE .IP "\fIprofile.snapshots.min_free_space.value\fR" 6 .RS Type: int Allowed Values: 1-99999 .br Keep at least value + unit free space. .PP Default: 1 .RE .IP "\fIprofile.snapshots.mode\fR" 6 .RS Type: str Allowed Values: local|local_encfs|ssh|ssh_encfs .br Use mode (or backend) for this snapshot. Look at 'man backintime' section 'Modes'. .PP Default: local .RE .IP "\fIprofile.snapshots..password.save\fR" 6 .RS Type: bool Allowed Values: true|false .br Save password to system keyring (gnome-keyring or kwallet). must be the same as \fIprofile.snapshots.mode\fR .PP Default: false .RE .IP "\fIprofile.snapshots..password.use_cache\fR" 6 .RS Type: bool Allowed Values: true|false .br 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 .PP Default: true if home is not encrypted .RE .IP "\fIprofile.snapshots.no_on_battery\fR" 6 .RS Type: bool Allowed Values: true|false .br Don't take snapshots if the Computer runs on battery. .PP Default: false .RE .IP "\fIprofile.snapshots.notify.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br Display notifications (errors, warnings) through libnotify. .PP Default: true .RE .IP "\fIprofile.snapshots.one_file_system\fR" 6 .RS Type: bool Allowed Values: true|false .br Use rsync's "--one-file-system" to avoid crossing filesystem boundaries when recursing. .PP Default: false .RE .IP "\fIprofile.snapshots.path\fR" 6 .RS Type: str Allowed Values: absolute path .br Where to save snapshots in mode 'local'. This path must contain a folderstructure like 'backintime///' .PP Default: '' .RE .IP "\fIprofile.snapshots.path.host\fR" 6 .RS Type: str Allowed Values: text .br Set Host for snapshot path .PP Default: local hostname .RE .IP "\fIprofile.snapshots.path.profile\fR" 6 .RS Type: str Allowed Values: 1-99999 .br Set Profile-ID for snapshot path .PP Default: current Profile-ID .RE .IP "\fIprofile.snapshots.path.user\fR" 6 .RS Type: str Allowed Values: text .br Set User for snapshot path .PP Default: local username .RE .IP "\fIprofile.snapshots.path.uuid\fR" 6 .RS Type: str Allowed Values: text .br Devices uuid used to automatically set up udev rule if the drive is not connected. .PP Default: '' .RE .IP "\fIprofile.snapshots.preserve_acl\fR" 6 .RS Type: bool Allowed Values: true|false .br Preserve ACL. The source and destination systems must have compatible ACL entries for this option to work properly. .PP Default: false .RE .IP "\fIprofile.snapshots.preserve_xattr\fR" 6 .RS Type: bool Allowed Values: true|false .br Preserve extended attributes (xattr). .PP Default: false .RE .IP "\fIprofile.snapshots.remove_old_snapshots.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br Remove all snapshots older than value + unit .PP Default: true .RE .IP "\fIprofile.snapshots.remove_old_snapshots.unit\fR" 6 .RS Type: int Allowed Values: 20|30|80 .br 20 = days .br 30 = weeks .br 80 = years .PP Default: 80 .RE .IP "\fIprofile.snapshots.remove_old_snapshots.value\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Snapshots older than this times units will be removed .PP Default: 10 .RE .IP "\fIprofile.snapshots.rsync_options.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br Past additional options to rsync .PP Default: false .RE .IP "\fIprofile.snapshots.rsync_options.value\fR" 6 .RS Type: str Allowed Values: text .br rsync options. Options must be quoted e.g. \-\-exclude-from="/path/to/my exclude file" .PP Default: '' .RE .IP "\fIprofile.snapshots.smart_remove\fR" 6 .RS Type: bool Allowed Values: true|false .br Run smart_remove to clean up old snapshots after a new snapshot was created. .PP Default: false .RE .IP "\fIprofile.snapshots.smart_remove.keep_all\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Keep all snapshots for X days. .PP Default: 2 .RE .IP "\fIprofile.snapshots.smart_remove.keep_one_per_day\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Keep one snapshot per day for X days. .PP Default: 7 .RE .IP "\fIprofile.snapshots.smart_remove.keep_one_per_month\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Keep one snapshot per month for X month. .PP Default: 24 .RE .IP "\fIprofile.snapshots.smart_remove.keep_one_per_week\fR" 6 .RS Type: int Allowed Values: 0-99999 .br Keep one snapshot per week for X weeks. .PP Default: 4 .RE .IP "\fIprofile.snapshots.smart_remove.run_remote_in_background\fR" 6 .RS Type: bool Allowed Values: true|false .br If using mode SSH or SSH-encrypted, run smart_remove in background on remote machine .PP Default: false .RE .IP "\fIprofile.snapshots.ssh.check_commands\fR" 6 .RS Type: bool Allowed Values: true|false .br Check if all commands (used during takeSnapshot) work like expected on the remote host. .PP Default: true .RE .IP "\fIprofile.snapshots.ssh.check_ping\fR" 6 .RS Type: bool Allowed Values: true|false .br Check if the remote host is available before trying to mount. .PP Default: true .RE .IP "\fIprofile.snapshots.ssh.cipher\fR" 6 .RS Type: str Allowed Values: default | aes192-cbc | aes256-cbc | aes128-ctr | aes192-ctr | aes256-ctr | arcfour | arcfour256 | arcfour128 | aes128-cbc | 3des-cbc | blowfish-cbc | cast128-cbc .br Cipher that is used for encrypting the SSH tunnel. Depending on the environment (network bandwidth, cpu and hdd performance) a different cipher might be faster. .PP Default: default .RE .IP "\fIprofile.snapshots.ssh.host\fR" 6 .RS Type: str Allowed Values: IP or domain address .br Remote host used for mode 'ssh' and 'ssh_encfs'. .PP Default: '' .RE .IP "\fIprofile.snapshots.ssh.ionice\fR" 6 .RS Type: bool Allowed Values: true|false .br Run rsync and other commands on remote host with 'ionice \-c2 \-n7' .PP Default: false .RE .IP "\fIprofile.snapshots.ssh.max_arg_length\fR" 6 .RS Type: int Allowed Values: 0, >700 .br 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/sshMaxArg.py [initial_ssh_cmd_length]'. .br 0 = unlimited .PP Default: 0 .RE .IP "\fIprofile.snapshots.ssh.nice\fR" 6 .RS Type: bool Allowed Values: true|false .br Run rsync and other commands on remote host with 'nice \-n19' .PP Default: false .RE .IP "\fIprofile.snapshots.ssh.nocache\fR" 6 .RS Type: bool Allowed Values: true|false .br Run rsync on remote host with 'nocache'. This will prevent files from being cached in memory. .PP Default: false .RE .IP "\fIprofile.snapshots.ssh.path\fR" 6 .RS Type: str Allowed Values: absolute or relative path .br Snapshot path on remote host. If the path is relative (no leading '/') it will start from remote Users homedir. An empty path will be replaced with './'. .PP Default: '' .RE .IP "\fIprofile.snapshots.ssh.port\fR" 6 .RS Type: int Allowed Values: 0-65535 .br SSH Port on remote host. .PP Default: 22 .RE .IP "\fIprofile.snapshots.ssh.prefix.enabled\fR" 6 .RS Type: bool Allowed Values: true|false .br Add prefix to every command which run through SSH on remote host. .PP Default: false .RE .IP "\fIprofile.snapshots.ssh.prefix.value\fR" 6 .RS Type: str Allowed Values: text .br 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 \fIprofile.snapshots.rsync_options.value\fR with --rsync-path="FOO=bar:\\$FOO /usr/bin/rsync" .PP Default: 'PATH=/opt/bin:/opt/sbin:\\$PATH' .RE .IP "\fIprofile.snapshots.ssh.private_key_file\fR" 6 .RS Type: str Allowed Values: absolute path to private key file .br Private key file used for password-less authentication on remote host. .PP Default: ~/.ssh/id_dsa .RE .IP "\fIprofile.snapshots.ssh.user\fR" 6 .RS Type: str Allowed Values: text .br Remote SSH user .PP Default: local users name .RE .IP "\fIprofile.snapshots.take_snapshot_regardless_of_changes\fR" 6 .RS Type: bool Allowed Values: true|false .br Create a new snapshot regardless if there were changes or not. .PP Default: false .RE .IP "\fIprofile.snapshots.use_checksum\fR" 6 .RS Type: bool Allowed Values: true|false .br Use checksum to detect changes rather than size + time. .PP Default: false .RE .IP "\fIprofile.snapshots.user_backup.ionice\fR" 6 .RS Type: bool Allowed Values: true|false .br Run BackInTime with 'ionice \-c2 \-n7' when taking a manual snapshot. This will give BackInTime the lowest IO bandwidth priority to not interrupt any other working process. .PP Default: false .RE .IP "\fIprofile.user_callback.no_logging\fR" 6 .RS Type: bool Allowed Values: true|false .br 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. .PP Default: false .RE .IP "\fIprofiles\fR" 6 .RS Type: str Allowed Values: int separated by colon (e.g. 1:3:4) .br All active Profiles ( in profile.snapshots...). .PP Default: 1 .RE .IP "\fIprofiles.version\fR" 6 .RS Type: int Allowed Values: 1 .br Internal version of profiles config. .PP Default: 1 .RE .SH SEE ALSO .BR backintime (1), .BR backintime-qt (1), .BR backintime-askpass (1) .PP \fBBack In Time\fP project website: https://github.com/bit-team/backintime .PP \fBBack In Time\fP mailing list: https://mail.python.org/mailman3/lists/bit-dev.python.org .SH AUTHOR \fBBack In Time\fP Team backintime-1.5.2/common/man/C/backintime.1000066400000000000000000000334331465446530500202640ustar00rootroot00000000000000.TH backintime 1 "August 2024" "version 1.5.2" "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/blob/-/FAQ.md .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 characters 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 characters 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 AND ENCFS Because of security issues with EncFS it is planned to remove it from Back In Time. To continue the support of encrypted snapshots it is the goal to replace it with an alternative if possible. See this document for details and the current state of the transition process. .PP https://github.com/bit-team/backintime/doc/ENCRYPT_TRANSITION.md .PP The security issues are mentioned in a security audit from 2014 and not fixed until today. The EnCFS project is not maintained anymore and his former maintainer recommend to switch to GoCryptFS. See the following links for further readings: .IP EncFS Security Audit (updated blog post): https://defuse.ca/audits/encfs.htm .IP EncFS Security Audit (original mailing list entry): https://sourceforge.net/p/encfs/mailman/message/31849549 .IP Back In Time Issue #1734 about transition of the encryption feature: https://github.com/bit-team/backintime/issues/1734 .SH SEE ALSO .BR backintime-qt (1), .BR backintime-config (1), .BR backintime-askpass (1) .PP \fBBack In Time\fP project website: https://github.com/bit-team/backintime .PP \fBBack In Time\fP mailing list: https://mail.python.org/mailman3/lists/bit-dev.python.org .SH AUTHOR \fBBack In Time\fP Team backintime-1.5.2/common/mount.py000066400000000000000000001173451465446530500166600ustar00rootroot00000000000000# Copyright (C) 2012-2022 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. """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 os import subprocess import json import getpass from zlib import crc32 from time import sleep import config import logger import tools import password from exceptions import MountException, HashCollision class Mount(object): """ 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 BackInTime. If ``cfg`` is ``None`` this will load the default config. If ``profile_id`` is ``None`` it will use :py:func:`configfile.ConfigFileWithProfiles.currentProfile`. If the current profile uses Password-Cache and the Password-Cache is not 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 """ 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() self.tmp_mount = tmp_mount self.parent = parent if self.config.passwordUseCache(self.profile_id): 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) if proc.returncode: logger.error('Failed to %s pw-cache: %s' %(action, proc.returncode), self) pass 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 Returns: 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 """ 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 return 'local' else: 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) 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 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 Raises: exceptions.MountException: if a check failed """ 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 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): """ High-level check. Run :py:func:`MountControl.preMountCheck` to check if all conditions for :py:func:`Mount.mount` are set. Should be called with ``first_run = True`` to check if new settings are 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 Returns: bool: ``True`` if all checks where okay Raises: 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 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): """ High-level `remount`. Unmount the old profile presented by ``hash_id`` and mount new profile ``new_profile_id`` with mode ``mode``. If old and new mounts are the same just add new symlinks and keep the mount. Args map to profiles:: new_profile_id <= new profile mode <= new profile kwargs <= new profile hash_id <= old profile self.profile_id <= old profile Args: new_profile_id (str): Profile ID that should get mounted mode (str): mode to use for new mount. One of 'local', 'ssh', 'local_encfs' or 'ssh_encfs' hash_id (str): Hash ID used as mountpoint on the old mount, that should get unmounted **kwargs: keyword arguments paste to low-level :py:class:`MountControl` subclass backend for the new mount Returns: 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 """ 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) return 'local' if hash_id == 'local': #old profile don't need to umount. self.profile_id = new_profile_id 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) 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) return hash_id else: #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) class MountControl(object): """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__``. See module description (:py:mod:`mount`) for a detailed example. You **must** overwrite methods: - :py:func:`MountControl._mount` You **can** overwrite methods: - :py:func:`MountControl._umount` - :py:func:`MountControl.preMountCheck` - :py:func:`MountControl.postMountCheck` - :py:func:`MountControl.preUmountCheck` - :py:func:`MountControl.postUmountCheck` These arguments (all of type :py:obj:`str`) **must** be defined in ``self`` namespace by subclassing ``__init__`` method: - ``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 profile_id (str): profile ID that should be used hash_id (str): crc32 hash used to identify identical mountpoints tmp_mount (bool): if ``True`` mount to a temporary destination parent (QWidget): parent widget for QDialogs or ``None`` if there is no parent symlink (bool): if ``True`` set symlink to mountpoint mode (str): one of ``local``, ``local_encfs``, ``ssh`` or ``ssh_encfs`` hash_collision (int): global value used to prevent hash collisions on mountpoints """ def __init__(self, cfg = None, profile_id = None, hash_id = None, tmp_mount = False, parent = None, symlink = True, *args, **kwargs): # 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 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() self.tmp_mount = tmp_mount self.hash_id = hash_id self.parent = parent self.symlink = symlink self.local_host = self.config.host() self.local_user = getpass.getuser() self.pid = self.config.pid() self.all_kwargs = {} self.setattrKwargs('mode', self.config.snapshotsMode(self.profile_id), **kwargs) self.setattrKwargs('hash_collision', self.config.hashCollision(), **kwargs) def setDefaultArgs(self): """ Set some arguments which are necessary for all backends. ``self.all_kwargs`` need to be filled through :py:func:`setattrKwargs` before calling this. """ #self.destination should contain all arguments that are necessary for #mount. args = list(self.all_kwargs.keys()) self.destination = '%s:' % self.all_kwargs['mode'] 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. if self.hash_id is None: self.hash_id = self.hash(self.destination) 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.hash_id_path = self.hashIdPath() self.currentMountpoint = self.mountpoint() self.lock_path = self.lockPath() self.umount_info = self.umountInfoPath() def mount(self, check = True): """ 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 Returns: 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 """ self.createMountStructure() self.mountProcessLockAcquire() try: if self.mounted(): if not self.compareUmountInfo(): #We probably have a hash collision self.config.incrementHashCollision() 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) self.writeUmountInfo() except Exception: raise else: self.mountLockAquire() self.setSymlink() finally: self.mountProcessLockRelease() return self.hash_id def umount(self): """ Low-level `umount`. Set mountprocess lock, run umount checks and call :py:func:`_umount` for the subclassed backend. Finally release mount lock, remove symlink and release mountprocess lock. Raises: exceptions.MountException: if a check failed """ self.mountProcessLockAcquire() try: if not os.path.isdir(self.hash_id_path): logger.info('Mountpoint %s does not exist.' % self.currentMountpoint, self) else: if not self.mounted(): logger.info('Mountpoint %s is not mounted.' % self.currentMountpoint, self) else: if self.mountLockCheck(): logger.info('Mountpoint %s still in use. Keep mounted.' % self.currentMountpoint, self) else: self.preUmountCheck() self._umount() self.postUmountCheck() if os.listdir(self.currentMountpoint): logger.warning('Mountpoint %s not empty after unmount' %self.currentMountpoint, self) else: logger.info('unmount %s from %s' %(self.log_command, self.currentMountpoint), self) except Exception: raise else: self.mountLockRelease() self.removeSymlink() finally: self.mountProcessLockRelease() def _mount(self): """ Backend mount method. This **must** be overwritten in the backend which subclasses :py:class:`MountControl`. """ raise NotImplementedError('_mount need to be overwritten in backend') def _umount(self): """ Unmount with ``fusermount -u`` for fuse based backends. This **can** be overwritten by backends which subclasses :py:class:`MountControl`. Raises: exceptions.MountException: If unmount failed. """ try: subprocess.check_call(['fusermount', '-u', self.currentMountpoint]) except subprocess.CalledProcessError as exc: raise MountException( _("Can't 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 subclasses :py:class:`MountControl`. Returns: bool: ``True`` if all checks where okay Raises: exceptions.MountException: if backend can not mount Note: This can also be used to prepare things before running :py:func:`_mount` """ return True def postMountCheck(self): """ Check if the mount was successful. This **can** be overwritten in backends which subclasses :py:class:`MountControl`. Returns: bool: ``True`` if all checks where okay Raises: exceptions.MountException: if backend wasn't mount successful Note: This can also be used to clean up after running :py:func:`_mount` """ return True def preUmountCheck(self): """ Check if backend is safe to umount. This **can** be overwritten in backends which subclasses :py:class:`MountControl`. Returns: bool: ``True`` if all checks where okay Raises: exceptions.MountException: if backend can not umount Note: This can also be used to prepare things before running :py:func:`_umount` """ return True def postUmountCheck(self): """ Check if unmount was successful. This **can** be overwritten in backends which subclasses :py:class:`MountControl`. Returns: bool: ``True`` if all checks where okay Raises: exceptions.MountException: if backend wasn't unmounted successful Note: This can also be used to clean up after running :py:func:`_umount` """ return True def checkFuse(self): """ Check if command in ``self.mountproc`` is installed. Raises: exceptions.MountException: If either command is not available. """ if not tools.checkCommand(self.mountproc): 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): """ Check if the mountpoint is already mounted. Returns: bool: ``True`` if mountpoint is mounted Raises: exceptions.MountException: if mountpoint is not mounted but also not empty """ if os.path.ismount(self.currentMountpoint): return True else: try: if os.listdir(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 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): """ 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 Raises: exceptions.MountException: if timed out """ lock_path = self.mount_root lockSuffix = '.lock' lock = os.path.join(lock_path, self.pid + lockSuffix) count = 0 while self.checkLocks(lock_path, lockSuffix): count += 1 if count == timeout: raise MountException('Mountprocess lock timeout') sleep(1) logger.debug(f'Acquire mountprocess lock {lock}', self) with open(lock, 'w') as f: f.write(self.pid) def mountProcessLockRelease(self): """ Remove mountprocess lock. """ lock_path = self.mount_root lockSuffix = '.lock' lock = os.path.join(lock_path, self.pid + lockSuffix) 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. """ if self.tmp_mount: lockSuffix = '.tmp.lock' else: lockSuffix = '.lock' lock = os.path.join(self.lock_path, self.pid + lockSuffix) logger.debug('Set mount lock %s' %lock, self) with open(lock, 'w') as f: f.write(self.pid) def mountLockCheck(self): """ Check for locks on the current mountpoint. Returns: bool: ``True`` if there are any locks """ lockSuffix = '.lock' return self.checkLocks(self.lock_path, lockSuffix) def mountLockRelease(self): """ Remove mountpoint lock for this process. """ if self.tmp_mount: lockSuffix = '.tmp.lock' else: lockSuffix = '.lock' lock = os.path.join(self.lock_path, self.pid + lockSuffix) if os.path.exists(lock): logger.debug('Remove mount lock %s' %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. Args: path (str): full path to lock directory lockSuffix (str): last part of locks name Returns: bool: ``True`` if there are active locks in ``path`` """ for f in os.listdir(path): if not f[-len(lockSuffix):] == lockSuffix: continue is_tmp = os.path.basename(f)[-len(lockSuffix)-len('.tmp'):-len(lockSuffix)] == '.tmp' if is_tmp: lock_pid = os.path.basename(f)[:-len('.tmp')-len(lockSuffix)] else: lock_pid = os.path.basename(f)[:-len(lockSuffix)] if lock_pid == self.pid: if is_tmp == self.tmp_mount: 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)) return False 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 mountpoint name and also be written as unmount_info. Args: arg (str): argument name default: default value used if ``arg`` is not in ``kwargs`` store (bool): if ``True`` add ``arg`` to ``self.all_kwargs`` **kwargs: arguments given on backend constructor """ if arg in kwargs: value = kwargs[arg] else: value = default setattr(self, arg, value) if store: #make dictionary with all used args for umount self.all_kwargs[arg] = value def writeUmountInfo(self): """ Write content of ``self.all_kwargs`` to file ``~/.local/share/backintime/mnt//umount``. This will be used to unmount the filesystem later. """ data_string = json.dumps(self.all_kwargs) with open(self.umount_info, 'w') as f: f.write(data_string) f.close def readUmountInfo(self, umount_info = None): """ Read keyword arguments from file ``umount_info``. Args: umount_info (str): full path to /umount file. If ``None`` current ``/umount`` file will be used Returns: dict: previously written ``self.all_kwargs`` """ if umount_info is None: umount_info = self.umount_info with open(umount_info, 'r') as f: data_string = f.read() f.close() return json.loads(data_string) def compareUmountInfo(self, umount_info = None): """ Compare current ``self.all_kwargs`` with those from file ``umount_info``. This should prevent hash collisions of two different mounts. Args: umount_info (str): full path to /umount file Returns: bool: ``True`` if ``self.all_kwargs`` and ``kwargs`` read from ``umount_info`` file are identiacal """ #run self.all_kwargs through json first current_kwargs = json.loads(json.dumps(self.all_kwargs)) saved_kwargs = self.readUmountInfo(umount_info) if not len(current_kwargs) == len(saved_kwargs): return False for arg in list(current_kwargs.keys()): if not arg in list(saved_kwargs.keys()): return False if not current_kwargs[arg] == saved_kwargs[arg]: return False return True def compareRemount(self, old_hash_id): """ Compare mount arguments between current and ``old_hash_id``. If they are identical we could reuse the mount and don't need to remount. Args: old_hash_id (str): Hash ID of the old mountpoint Returns: bool: True if the old mountpoint and current are identiacal """ if old_hash_id == self.hash_id: return self.compareUmountInfo(self.umountInfoPath(old_hash_id)) return False def setSymlink(self, profile_id = None, hash_id = None, tmp_mount = None): """ If ``self.symlink`` is ``True`` set symlink ``~/.local/share/backintime/mnt/_``. Target will be either the mountpoint or a subfolder of the mountpoint if ``self.symlink_subfolder`` is set. Args: profile_id (str): Profile ID that should be linked. If ``None`` use ``self.profile_id`` hash_id (str): Hash ID of mountpoint where this sysmlink should point to. If ``None`` use ``self.hash_id`` tmp_mount (bool): Set a temporary symlink just for testing new settings """ 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) 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): """ 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 """ 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)) def hash(self, s): """ Create a CRC32 hash of string ``s``. Args: s (str): string that should be hashed Returns: str: hash of string ``s`` """ return '%X' % (crc32(s.encode()) & 0xFFFFFFFF) def hashIdPath(self, hash_id = None): """ Get path ``~/.local/share/backintime/mnt/``. Args: hash_id (str): Unique identifier for a mountpoint. If ``None`` use ``self.hash_id`` Returns: str: full path to ```` """ if hash_id is None: hash_id = self.hash_id return os.path.join(self.mount_root, self.hash_id) def mountpoint(self, hash_id = None): """ Get path ``~/.local/share/backintime/mnt//mountpoint``. Args: hash_id (str): Unique identifier for a mountpoint Returns: str: full path to ``/mountpoint`` """ return os.path.join(self.hashIdPath(hash_id), 'mountpoint') def lockPath(self, hash_id = None): """ Get path ``~/.local/share/backintime/mnt//locks``. Args: hash_id (str): Unique identifier for a mountpoint Returns: str: full path to ``/locks``` """ return os.path.join(self.hashIdPath(hash_id), 'locks') def umountInfoPath(self, hash_id = None): """ Get path ``~/.local/share/backintime/mnt//umount``. Args: hash_id (str): Unique identifier for a mountpoint Returns: str: full path to ``/umount``` """ return os.path.join(self.hashIdPath(hash_id), 'umount') backintime-1.5.2/common/password.py000066400000000000000000000271071465446530500173540ustar00rootroot00000000000000# Copyright (C) 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. # # 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 time import atexit import signal import subprocess import re import errno import config import configfile import tools import password_ipc import logger from exceptions import Timeout class Password_Cache(tools.Daemon): """ Password_Cache get started on User login. It provides passwords for BIT cronjobs because keyring is not available when the User is not logged in. Does not start if there is no password to cache (e.g. no profile allows to cache). """ PW_CACHE_VERSION = 3 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) self.dbKeyring = {} self.dbUsr = {} self.fifo = password_ipc.FIFO(self.config.passwordCacheFifo()) self.keyringSupported = tools.keyringSupported() def run(self): """ wait for password request on FIFO and answer with password from self.db through FIFO. """ info = configfile.ConfigFile() info.setIntValue('version', self.PW_CACHE_VERSION) info.save(self.config.passwordCacheInfo()) os.chmod(self.config.passwordCacheInfo(), 0o600) logger.debug('Keyring supported: %s' %self.keyringSupported, self) tools.envSave(self.config.cronEnvFile()) 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()): answer = 'pw:' + self.dbKeyring[key] elif key in list(self.dbUsr.keys()): 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 KeyboardInterrupt: logger.debug('Quit.', self) break except Timeout: logger.error('FIFO timeout', self) except Exception as e: logger.error('ERROR: %s' % str(e), self) def reloadHandler(self, signum, frame): """ reload passwords during runtime. """ time.sleep(2) cfgPath = self.config._LOCAL_CONFIG_PATH del self.config self.config = config.Config(cfgPath) del self.dbKeyring self.dbKeyring = {} self.collectPasswords() def collectPasswords(self): """ search all profiles in config and collect passwords from keyring. """ 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) password = tools.password(service_name, user_name) if password is None: continue self.dbKeyring['%s/%s' %(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): self.fifo.delfifo() super(Password_Cache, self).cleanupHandler(signum, frame) class Password(object): """Provide passwords for BIT either from keyring, Password_Cache or by asking user. """ 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() def password(self, parent, profile_id, mode, pw_id=1, only_from_keyring=False): """ 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)] except KeyError: pass password = '' if self.config.passwordUseCache(profile_id) and not only_from_keyring: # From cache password = self.passwordFromCache(service_name, user_name) if not password is None: self.setPasswordDb(service_name, user_name, password) return password if self.config.passwordSave(profile_id): # From keyring password = self.passwordFromKeyring(service_name, user_name) if not password is None: self.setPasswordDb(service_name, user_name, password) return password if 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): """ get password from system keyring (seahorse). The keyring is only available if User is logged in. """ if self.keyringSupported: try: return tools.password(service_name, user_name) except Exception: logger.error('get password from Keyring failed', self) return None 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: return None 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 = _('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') 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 + ' ') alarm.stop() except Timeout: password = '' return password # Use QDialog as graphical prompt password = messagebox.askPasswordDialog( 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): """ internal Password cache. Prevent to ask password several times during runtime. """ self.db['%s/%s' %(service_name, user_name)] = password 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) user_name = self.config.keyringUserName(profile_id) if self.config.passwordSave(profile_id): self.setPasswordKeyring(service_name, user_name, password) if self.config.passwordUseCache(profile_id): self.setPasswordCache(service_name, user_name, password) self.setPasswordDb(service_name, user_name, password) def setPasswordKeyring(self, service_name, user_name, password): return tools.setPassword(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) backintime-1.5.2/common/password_ipc.py000066400000000000000000000100431465446530500201760ustar00rootroot00000000000000# Copyright (C) 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. # # 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 sys import stat import tools import threading import tempfile from contextlib import contextmanager import logger class FIFO(object): """ interprocess-communication with named pipes """ def __init__(self, fname): self.fifo = fname self.alarm = tools.Alarm() def delfifo(self): """ remove FIFO """ try: os.remove(self.fifo) except: pass def create(self): """ create the FIFO in a way that only the current user can access it. """ if os.path.exists(self.fifo): self.delfifo() try: os.mkfifo(self.fifo, 0o600) except OSError as e: logger.error('Failed to create FIFO: %s' % str(e), self) sys.exit(1) def read(self, timeout = 0): """ read from fifo until timeout. If timeout is 0 it will wait forever for input. """ #sys.stdout.write('read fifo\n') if not self.isFifo(): sys.exit(1) self.alarm.start(timeout) with open(self.fifo, 'r') as fifo: ret = fifo.read() self.alarm.stop() return ret def write(self, string, timeout = 0): """ write to fifo 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(): sys.exit(1) self.alarm.start(timeout) with open(self.fifo, 'w') as fifo: fifo.write(string) self.alarm.stop() def isFifo(self): """ 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) return False mode = s.st_mode if not stat.S_ISFIFO(mode): logger.error('%s is not a FIFO' % self.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) return False return True class TempPasswordThread(threading.Thread): """ 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 self.temp_file = os.path.join(tempfile.mkdtemp(), 'FIFO') self.fifo = FIFO(self.temp_file) @contextmanager def starter(self): self.start() yield self.stop() def run(self): self.fifo.create() self.fifo.write(self.pw) self.fifo.delfifo() def read(self): """ read fifo to end the blocking fifo.write use only if thread timeout. """ self.fifo.read() def stop(self): self.join(5) if self.is_alive(): #threading does not support signal.alarm self.read() try: os.rmdir(os.path.dirname(self.temp_file)) except OSError: pass backintime-1.5.2/common/pluginmanager.py000066400000000000000000000273631465446530500203470ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 sys import tools tools.registerBackintimePath('common') tools.registerBackintimePath('plugins') tools.registerBackintimePath('common', 'plugins') tools.registerBackintimePath('qt', 'plugins') import logger from exceptions import StopException class Plugin: """ 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): return 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): """ Called to 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") Returns: ``None`` (return value will be ignored anyhow) """ return 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): """ 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) Returns: ``None`` """ if self.loaded and not force: return if snapshots is None: import snapshots as snapshots_ snapshots = snapshots_.Snapshots(cfg) self.loaded = True self.plugins = [] self.hasGuiPlugins = False 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('__'): logger.debug('Probing plugin %s' % f, self) 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: # A plugin must implement this class via inheritance 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) def processBegin(self): ret_val = True for plugin in self.plugins: try: plugin.processBegin() except StopException: ret_val = False except BaseException as e: self.logError(plugin, e) return ret_val def processEnd(self): for plugin in reversed(self.plugins): try: plugin.processEnd() except BaseException as e: self.logError(plugin, e) def error(self, code, message = ''): for plugin in self.plugins: try: plugin.error(code, message) except BaseException as e: self.logError(plugin, e) def newSnapshot(self, snapshot_id, snapshot_path): for plugin in self.plugins: try: plugin.newSnapshot(snapshot_id, snapshot_path) except BaseException as e: self.logError(plugin, e) 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) except BaseException as e: self.logError(plugin, e) def appStart(self): for plugin in reversed(self.plugins): try: plugin.appStart() except BaseException as e: self.logError(plugin, e) def appExit(self): for plugin in reversed(self.plugins): try: plugin.appExit() except BaseException as e: self.logError(plugin, e) def mount(self, profileID = None): for plugin in reversed(self.plugins): try: plugin.mount(profileID) except BaseException as e: self.logError(plugin, e) def unmount(self, profileID = None): for plugin in reversed(self.plugins): try: plugin.unmount(profileID) except BaseException as e: self.logError(plugin, e) def logError(self, plugin, e): logger.error('Plugin %s %s failed: %s' %(plugin.__module__, #plugin name sys._getframe(1).f_code.co_name, #method name str(e)), #exception self, 1) backintime-1.5.2/common/plugins/000077500000000000000000000000001465446530500166125ustar00rootroot00000000000000backintime-1.5.2/common/plugins/usercallbackplugin.py000066400000000000000000000113301465446530500230340ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 pluginmanager import logger import gettext from subprocess import Popen, PIPE from exceptions import StopException _ = 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): logger.openlog() return def init(self, snapshots): self.config = snapshots.config self.script = self.config.takeSnapshotUserCallback() 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(f'Call user-callback: {" ".join(cmd)}', self) if self.config.userCallbackNoLogging(): stdout, stderr = None, None else: stdout, stderr = PIPE, PIPE try: callback = Popen(cmd, stdout=stdout, stderr=stderr, universal_newlines=True) output = callback.communicate() # Stdout if output[0]: logger.info("user-callback returned '" + output[0].strip('\n') + "'", self) # Stderr if output[1]: logger.error("user-callback returned '" + output[1].strip('\n') + "'", self) if callback.returncode != 0: logger.warning( f'user-callback returncode: {callback.returncode}', self) raise StopException() except OSError as e: logger.error( f'Exception when trying to run user callback: {e.strerror}', self) def processBegin(self): self.callback('1') def processEnd(self): self.callback('2') def error(self, code, message): if not message: self.callback('4', code) else: self.callback('4', code, message) def newSnapshot(self, snapshot_id, snapshot_path): self.callback('3', snapshot_id, snapshot_path) def appStart(self): self.callback('5') def appExit(self): self.callback('6') def mount(self, profileID = None): self.callback('7', profileID = profileID) def unmount(self, profileID = None): self.callback('8', profileID = profileID) backintime-1.5.2/common/po/000077500000000000000000000000001465446530500155475ustar00rootroot00000000000000backintime-1.5.2/common/po/README.md000066400000000000000000000005001465446530500170210ustar00rootroot00000000000000Translation for Back In Time is done at https://translate.codeberg.org/engage/backintime. Please do NOT change files in this folder as they will get overwritten with files exported from translation platform. See [Maintenance documentation about translation and localization](../doc-dev/2_localization.md) for details. backintime-1.5.2/common/po/ar.po000066400000000000000000001752011465446530500165170ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-06 06:28+0000\n" "Last-Translator: buhtz \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 ? 4 : 5;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "تحذير" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "الملف الشخصي الرئيسي" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "تشفير محلي بصيغة EncFS" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (تشفير EncFS)" #: common/config.py:309 msgid "Local" msgstr "محلي" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "مفتاح SSH خاص" #: common/config.py:314 msgid "Local encrypted" msgstr "مشفرة محليا" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "التعمية" #: common/config.py:320 msgid "SSH encrypted" msgstr "تشفير SSH" #: common/config.py:327 msgid "Default" msgstr "إفتراضيّ" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "الملف الشخصي: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "مُجلد اللقطات غير صالح!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "يجب أن تُحدد على الأقل مُجلداً واحداً للنسخ الاحتياطي!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "لا يمكن تضمين مجلد النسخ الاحتياطي." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "لا يمكن تضمين المجلد الفرعي للنسخ الاحتياطي." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "اختيار غير صالح. {path} ليس مُجلداً." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "يجب ألا تكون حقول Host/User/Profile-ID فارغًا." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "لا يمكن الكتابة إلى: {path}\n" "هل أنت مُتأكد بأن لديك حق الكتابة؟" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "نظام الملفات الوجهة لـ {path} مُنسق باستخدام FAT، والذي لا يدعم الروابط " "الصعبة. يُرجى استخدام نظام ملفات لينكس أصلي." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "نظام الملفات الوجهة لـ {path} هو مشاركة مُركبة عبر SMB. يُرجى التأكد من أن " "خادم SMB البعيد يدعم الروابط الرمزية أو تفعيل {copyLinks} في " "{expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "نسخ الروابط (فك الارتباط بالروابط الرمزية)" #: common/config.py:497 msgid "Expert Options" msgstr "خيارات الخبراء" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "نظام الملفات الوجهة لـ {path} هو مشاركة مُركبة عبر sshfs. لا يدعم sshfs " "الروابط الصلبة. يُرجى استخدام وضع \"SSH\" بدلاً من ذلك." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "فشل كتابة crontab جديد." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "لا يعمل كرون (Cron) رغم توفر أمر crontab. لن يتم تشغيل مهام النسخ الاحتياطي " "المجدولة. قد يكون كرون مثبتًا ولكنه غير مفعل. جرب الأمر \"systemctl enable " "cron\" أو استشر قنوات الدعم لتوزيعة جنو / لينكس الخاصة بك." #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "جدول udev لا يعمل مع طريقة {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "لا يستطيع العثور على UUID لـ{path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "فشل في حفظ التكوين" #: common/configfile.py:143 msgid "Failed to load config" msgstr "فشل في جلب التكوين" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "الملف الشخصي \"{name}\" موجود أصلاً." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "لا يستطيع إزالة الملف الشخصي الآخر." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "لا يستطيع ركب '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "لم يوجد التكوين لالمجاد المشفر." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "انشاء مجلداً مشفرا جديدا؟" #: common/encfstools.py:151 msgid "Cancel" msgstr "إلغاء" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "يرجى تأكيد كلمة السر." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "كلمة السر ليس مطابق." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "خذ لقطة" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "لا يستطيع تفكيك {mountprocess} من {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "لم يتم العثور على الأمر \"{command}\". يرجى تثبيته (على سبيل المثال ، " "باستخدام \"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "نقطة التوصيل {mntpoint} ليست فارغة." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "ادخل كلمة المرور لملف التعريف لوضع {mode} يسمى \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "فشل" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "إستعادة الأذونات" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "تم" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "تأجيل النسخ الاحتياطي أثناء وجوده على البطارية" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "لم نتمكن من إيجاد مُجلد اللقطات.\n" "إذا كان في قرص قابل للإزالة فيرجى توصيله." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "ينتظر %s ثانية." msgstr[1] "ينتظر %s ثواني." msgstr[2] "ينتظر %s ثانية." msgstr[3] "ينتظر %s ثواني." msgstr[4] "ينتظر %s ثانية." msgstr[5] "ينتظر %s ثواني." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "فشل بأخذ لقطة {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "يُنْهيّ" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "لا يمكن إنشاء مجلد" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "يتم حفظ ملف التكوين…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "يتم حفظ الأذونات…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "وجد بقايا {snapshot_id} ويمكن الاستمرار فيه." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "يتم إزالة مجلد بقايا {snapshot_id} من العملية الاخيرة" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "لا يُمكن إزالة المُجلد" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "يتم اخذ لقطة" #: common/snapshots.py:1417 msgid "Success" msgstr "نجاح" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "عملية النقل جزئية بسبب حدوث خطأ" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "عملية النقل جزئية بسبب اختفاء ملفات المصدر (انظر 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "انتهت عملية 'rsync' مع رمز الخروج {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "اطلع على 'man rsync' للمزيد من التفاصيل" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "رموز الخروج السلبية لrsync هم ارقام إشارة, اطلع على 'kill -l' و'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "لا شيء تغير, لا حاجة للقطة جديدة" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "لا يمكن إعادة تسمية {new_path} إلى {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "إزالة ذكية" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "إزالة اللقطات القديمة" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "حاول إبقاء حد أدنى من المساحة فارغة" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "محاولة للحفاظ على الحد الأدنى من {perc} ضمن مساحة الـ inode حرة" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "الآن" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "غير ممكن تركيب {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "لم يتم العثور على ssh-agent. يرجى التأكد من أنه مثبت." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "لم يمكن فتح مفتاح SSH الخاص. كلمة المرور خاطئة أو كلمة المرور غير متاحة لـ " "cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "فشل تشفير {cipher} لـ {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "المسار البعيد موجود لكن ليس مجلدا." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "المسار البعيد غير قابل للكتابة." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "المسار المتباعد ليس قابل للتنفيذ." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "لا يمكن إنشاء المسار البعيد." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "المضيف البعيد {host} لا يدعم {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "انظر إلى 'man backintime' للحصول على مزيد من التعليمات" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "التأكد من الأوامر على المضيف {host} أعاد بخطأ غير معروف" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "المضيف البعيد {host} لا يدعم الروابط الصلبة" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "نسخ مفتاح SSH العام \"{pubkey}\" إلى المضيف البعيد \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "يرجى إدخال كلمة مرور لـ \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "عنْ" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "المطورون" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "الترجمات" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "الرخصة" #: qt/app.py:169 msgid "Shortcuts" msgstr "إختصارات" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "هذه المجلد غير موجود\n" "في اللقطة المختارة حاليا." #: qt/app.py:256 msgid "Add to Include" msgstr "اضف للتضمين" #: qt/app.py:258 msgid "Add to Exclude" msgstr "اضف للاستبعاد" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "يبدو أن تطبيق {app_name} يعمل لأول مرة حيث لم يتم العثور على أي إعدادات." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "هل ترغب باستيراد إعدادات موجودة (من مجلد احتياطي أو من حاسوب آخر)؟" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "لم نتمكن من العثور على مجلد النسخ الاحتياطي (مجلد اللقطات)." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "إن كان على وحدة تخزين خارجية، يرجى توصيلها ثم اضغط موافق." #: qt/app.py:481 msgid "Take a snapshot" msgstr "خذ لقطة" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "استخدم وقت التعديل والحجم لاكتشاف تغييرات الملفات." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "خذ لقطة (طريقة المجموعة الاختبارية)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "أستخدم المجامع الاختبارية لكشف التغيُّرات في الملفات." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "أوقف عملية التقاط اللقطة" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "استئناف عملية أخذ اللقطة" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "أوقف عملية أخذ اللقطة" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "حدث قائمة اللقطات" #: qt/app.py:508 msgid "Name snapshot" msgstr "سمي اللقطة" #: qt/app.py:512 msgid "Remove snapshot" msgstr "أزل اللقطة" #: qt/app.py:516 msgid "View snapshot log" msgstr "عرض سجل اللقطات" #: qt/app.py:520 msgid "View last log" msgstr "عرض اخر سجل" #: qt/app.py:524 msgid "Manage profiles…" msgstr "إدارة الملفات الشخصية…" #: qt/app.py:528 msgid "Shutdown" msgstr "أطفئ النظام" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "أطفئ النظام بعد اكتمال أخذ اللقطة." #: qt/app.py:532 msgid "Setup language…" msgstr "إعداد اللغة…" #: qt/app.py:536 msgid "Exit" msgstr "خروج" #: qt/app.py:540 msgid "Help" msgstr "مُساعدة" #: qt/app.py:544 msgid "Profiles config file" msgstr "ملف إعدادات الملفات الشخصية" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "الموقع الإلكتروني" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "سجل التغييرات" #: qt/app.py:553 msgid "FAQ" msgstr "الأسئلة الأكثر شيوعاً" #: qt/app.py:556 msgid "Ask a question" msgstr "إسأل سُؤالاً" #: qt/app.py:559 msgid "Report a bug" msgstr "بلّغ عن عِلة" #: qt/app.py:562 msgid "Translation" msgstr "الترجمة" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "عرض رسالة المشاركة في الترجمة." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "الإنتقال الى تشفير EncFS" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "عرض الرسالة حول إزالة ميزة التشفير EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "إسترجع" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "إسترجع الملفات والمجلدات المختارة الى اماكنهم الاصلية." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "إسترجع إلى …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "إسترجع الملفات والمجلدات المختارة إلى موقع جديد." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "إسترجع الملفات والمجلدات الظاهرة وكل محتوياتها الى اماكنهم الاصلية." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "إسترجع الملفات والمجلدات الظاهرة وكل محتوياتها إلى موقع جديد." #: qt/app.py:595 msgid "Up" msgstr "أعلى" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "أظهر الملفات المخفيّة" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "قارن اللقطات…" #: qt/app.py:660 msgid "Back In &Time" msgstr "العودة بالزمن" #: qt/app.py:665 msgid "&Backup" msgstr "النسخ الاحتياطي" #: qt/app.py:676 msgid "&Restore" msgstr "&استرجع" #: qt/app.py:682 msgid "&Help" msgstr "مُساعدة" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "إذا تم إغلاق هذه النافذة، فإن برنامج الاستعادة سيتوقف عن تشغيل النظام عند " "انتهاء اللقطة." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "هل أنت متأكد من رغبتك في إغلاق النافذة؟" #: qt/app.py:987 msgid "Working:" msgstr "يعمل:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "تم، لا حاجة إلى النسخ الاحتياطي" #: qt/app.py:1044 msgid "Working" msgstr "يعمل" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "خطأ" #: qt/app.py:1076 msgid "Sent" msgstr "أرسل" #: qt/app.py:1077 msgid "Speed" msgstr "السرعة" #: qt/app.py:1078 msgid "ETA" msgstr "الوقت المتبقي" #: qt/app.py:1140 msgid "Global" msgstr "عام" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "الرئيسية" #: qt/app.py:1170 msgid "Backup folders" msgstr "مجلدات النسخ الاحتياطي" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "اسم اللقطة" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "هل أنت متأكد أنك تريد إزالة هذه اللقطة؟" msgstr[1] "هل أنت متأكد أنك تريد إزالة هذه اللقطة؟" msgstr[2] "هل أنت مُتأكد أنك تود إزالة اللقطتان؟" msgstr[3] "هل أنت متأكد أنك تريد إزالة هذه اللقطات؟" msgstr[4] "هل أنت متأكد أنك تريد إزالة هذه اللقطات؟" msgstr[5] "هل أنت متأكد أنك تريد إزالة هذه اللقطات؟" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "إنشاء نسخ احتياطية ملحقة بـ {suffix}\n" "قبل الكتابة فوق العناصر المحلية أو إزالتها." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "سيتم إعادة تسمية الإصدارات الأحدث من الملفات باستخدام ملحق {suffix} قبل " "الاستعادة. إذا لم تعد تحتاج إليها ، يمكنك إزالتها باستخدام الأمر التالي:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "استعادة العناصر الغير موجودة\n" "أو التي هي أحدث من الموجودة في الوجهة فقط.\n" "باستخدام خيار \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "إزالة العناصر الأحدث في المجلد الأصلي." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "استعادة الملفات أو المجلدات المحددة إلى الوجهة الأصلية مع حذف الملفات أو " "المجلدات غير الموجودة في اللقطة. كن حذراً للغاية لأن هذه العملية ستؤدي إلى " "حذف الملفات والمجلدات التي تم استبعادها أثناء أخذ اللقطة." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" 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/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "لا توجد عناصر لاسترجاعها؟" msgstr[1] "هل أنت متأكد أنك ترغب في استرجاع عنصر واحد؟" msgstr[2] "هل أنت متأكد أنك ترغب في استرجاع عنصرين؟" msgstr[3] "هل أنت متأكد أنك ترغب في استرجاع {n} عناصر؟" msgstr[4] "هل أنت متأكد أنك ترغب في استرجاع {n} عنصرًا؟" msgstr[5] "هل أنت متأكد أنك ترغب في استرجاع {n} عنصرًا؟" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "هل أنت متأكد من حذف جميع الملفات الأحدث في {path}؟" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "هل أنت متأكد من حذف جميع الملفات الأحدث في المجلد الأصلي؟" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD} تحذير{BOLDEND}: حذف الملفات في جذر نظام الملفات قد يؤدي إلى تعطل " "نظامك بالكامل." #: qt/app.py:1750 msgid "Snapshot" msgstr "لقطة" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "استعادة {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "استرجع {path} إلى …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "إعدادات اللغة ستُفعّل فقط بعد إعادة تشغيل برنامج العودة بالزمن." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "سيُوقَف دعم EncFS في المستقبل القريب. وبالتالي لا يُوصى باستخدام هذا الوضع " "للملف الشخصي بعد الآن." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "لا يزال القرار بشأن بديل لدعم النسخ الاحتياطية المشفرة معلقًا، وذلك يعتمد " "على موارد المشروع وتوفر المساهمين. تتوفر المزيد من التفاصيل في هذه " "{whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "ورقة بيضاء" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "الدعم لملفات تعريف اللقطات المشفرة يشهد تغييرات كبيرة، وسيتم إزالة EncFS في " "المستقبل القريب." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "تستخدم الملفات التعريفية التالية التشفير مع EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "لا يزال القرار بشأن بديل لدعم النسخ الاحتياطية المشفرة معلقًا، اعتمادًا على " "موارد المشروع وتوفر المساهمين. يُدعى المستخدمون للانضمام إلى هذه المناقشة. " "تتوفر التفاصيل المحدثة حول الخطوات التالية في هذا {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "لن يتم عرض هذه الرسالة مرة أخرى. هذه النافذة متاحة في أي وقت من خلال قائمة " "المساعدة." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "فريق Back In Time الخاص بك" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "إعداد اللغة" #: qt/languagedialog.py:92 msgid "System default" msgstr "الإعدادات الافتراضية للنظام" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "استخدم لغة نظام التشغيل." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "مترجم: {percent}" #: qt/languagedialog.py:188 #, python-brace-format, ignore-placeholder-compare 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/languagedialog.py:217 msgid "translation platform" msgstr "منصة الترجمة" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "ترجمتك" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "عرض السجل الأخير" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "عرض سجل اللقطات" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "الملف الشخصي:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "لقطات:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "مُرَشِح:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "كل" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "تغييرات" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "أخطاء" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "لا توجد معلومات" msgstr[1] "توجد معلومات واحدة" msgstr[2] "توجد معلومتان" msgstr[3] "معلومات" msgstr[4] "معلومات" msgstr[5] "معلومات" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "فشل نقل rsync (تجريبي)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] خطأ، [I] معلومات، [C] تغيير" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "فك تشفير المسارات" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "سؤال" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "الملف الشخصي: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "عرض أخر سجل" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "تشغيل {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "جارٍ العمل…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "أُرسل:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "السرعة:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "الوقت المتبقي:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "لقطات" #: qt/qttools.py:427 msgid "Today" msgstr "اليوم" #: qt/qttools.py:434 msgid "Yesterday" msgstr "البارحة" #: qt/qttools.py:443 msgid "This week" msgstr "هذا الأسبوع" #: qt/qttools.py:450 msgid "Last week" msgstr "الأسبوع الماضي" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "هذه ليست لقطة ولكن عرض حي لملفاتك المحلية" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "آخر فحص {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "عرض السجل الكامل" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "وكيل SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "المُضيف:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "المنفذ:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "مستخدم:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "إدارة الملفات الشخصية" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "تعديل" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "أضف" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "أزل" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&عام" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "الوضع:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "أين ستُحْفَظ اللقطات" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "المجلد" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "إعدادات SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "المسار:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "تشفير:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "مفتاح تشفير خاص:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "اختر ملف مفتاح خاص موجود (عادة ما يسمى \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "إنشاء مفتاح SSH جديد بدون كلمة مرور (غير مسموح إذا تم اختيار ملف مفتاح خاص " "بالفعل)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "كلمة المرور" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "حفظ كلمة المرور في سلسلة المفاتيح" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "تخزين كلمة المرور في ذاكرة التخزين المؤقت لـ Cron (مشكلة أمنية: يمكن لـ root" " قراءة كلمة المرور)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "مُتقدم" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "مسار اللقطة الكاملة:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "جدول زمني" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "مُعطل" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "في كل إقلاع/إعادة إقلاع" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} 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/settingsdialog.py:448 #, 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/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} 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/settingsdialog.py:457 msgid "Custom hours" msgstr "ساعات مخصصة" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "كل يوم" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "متكررة (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "عند وصل وسيط تخزين (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "كل أسبوع" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "كل شهر" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "كل سنة" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "يوم:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "يوم الأسبوع:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "ساعة:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "ساعات:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "شغل \"Back In Time\" بشكل متكرر. هذا مفيد إذا لم يكن الكمبيوتر قيد التشغيل " "بانتظام." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "كل:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "ساعات" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "يوم(أيام)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "أسبوع(أسابيع)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "شهر(شهور)" #: qt/settingsdialog.py:555 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 "" "قم بتشغيل Back In Time بمجرد توصيل محرك الأقراص (مرة واحدة فقط كل X أيام).\n" "سيتم مطالبتك بكلمة مرور sudo الخاصة بك." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "تفعيل تسجيل رسائل التصحيح" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "يكتب رسائل مستوى التصحيح في سجل النظام عبر \"--debug\"." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "تحذير: استخدم هذا مؤقتًا فقط للتشخيص، لأنه يولد كمية كبيرة من البيانات." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&ضمْن" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "ضمْن ملفات ومُجلدات" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "أضف ملفاً" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "أضف مُجلدا" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&استثني" #: qt/settingsdialog.py:625 #, 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/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "استثني الأنماط, ملفات أو مُجلدات" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "أضف افتراضيًا" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "استبعاد الملفات الأكبر من:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "استثني الملفات التي يزيد حجمها عن {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "مع تعطيل 'وضع rsync الكامل'، ستؤثر هذه الميزة فقط على الملفات الجديدة، حيث " "إن ذلك يعد خيار نقل لـ rsync، وليس خيار استبعاد. لذلك، ستستمر الملفات " "الكبيرة التي نُسِخَت احتياطيًا سابقًا في الظهور في اللقطات حتى لو تم " "تعديلها." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&إزالة-تلقائية" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "أقدم من:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "سنة(سنوات)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "إذا كانت المساحة المتاحة أقل من:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "إذا كانت inodes المتوفرة أقل من:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "الإزالة الذكية:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "تشغيل في الخلفية على المضيف البعيد." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "تجريبي" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "أبقي كل اللقطات حتى نهاية" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "يوم (أيّام)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "احتفظ بلقطة واحدة يومياً لآخر" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "احتفظ بلقطة واحدة في الأسبوع لآخر" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "أسبوع(أسابيع)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "احتفظ بلقطة واحدة في الشهر لآخر" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "شهر(أشهر)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "احتفظ بلقطة واحدة لكل سنة لجميع السنوات." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "لا تُزيل اللقطات المُسماّة." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&خيارات" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "فَعْلّ التنبيهات" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "تعطيل اللقطات عند العمل على البطارية" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "حالة الطاقة غير متاحة من النظام" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "تشغيل لقطة واحدة فقط في كل مرة" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "ستكون اللقطات الأخرى محجوبة حتى يتم الانتهاء من اللقطة الحالية. هذه خيار " "عام. لذا، سيكون له تأثير على جميع الملفات الشخصية لهذا المستخدم. ولكن تحتاج " "إلى تفعيل ذلك لجميع المستخدمين الآخرين أيضًا." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "نسخ احتياطي للملفات المستبدلة في أثناء الاستعادة" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "ستُعَاد تسمية الإصدارات الأحدث من الملفات مع إضافة {suffix} قبل الاستعادة. " "إذا لم تكن بحاجة إليها بعد الآن، يمكنك إزالتها باستخدام {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "استمر عند حدوث خطأ (احتفظ باللقطات غير المكتملة)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "استخدم checksum لاكتشاف التغييرات" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "خذ لقطة جديدة، سواء كانت هناك تغييرات أم لا." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "مستوى السجل:" #: qt/settingsdialog.py:911 msgid "None" msgstr "لا شيء" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "خيارات الخبير" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "تحذير: قم بتغيير هذه الخيارات فقط إذا كنت تعرف حقًا ما تقوم به." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "قم بتشغيل 'rsync' باستخدام '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "كوظيفة cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "على المضيف البعيد" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "عند أخذ لقطة يدوية" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(يرجى تثبيت 'nocache' لتمكين هذا الخيار)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "في الجهاز المحلي" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "قم بإعادة توجيه stdout إلى /dev/null في وظائف cron." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "سيرسل نظام cron تلقائيًا بريدًا إلكترونيًا مع المخرجات المرفقة لوظائف cron " "إذا تم تثبيت MTA." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "قم بإعادة توجيه stderr إلى /dev/null في وظائف cron." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "سيرسل نظام cron تلقائيًا بريدًا إلكترونيًا مع الأخطاء المرفقة لوظائف cron " "إذا تم تثبيت MTA." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "تحديد حد سقف الـBandwidth لـ rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "كيلوبايت/ثانية" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "الحفاظ على ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "المُحافظة على الصفات المُوسعة (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "انسخ الروابط الغير الآمنة (تعمل فقط مع الروابط الأساسية)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "تقييد إلى نظام ملفات واحد" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "يجب أن تكون الخيارات بين علامات اقتباس، مثل {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "ألصق خيارات إضافية إلى rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "أضف بادئة إلى أوامر SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "بادئة للتشغيل قبل كل أمر على المضيف البعيد." #: qt/settingsdialog.py:1188 #, 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:1196 msgid "default" msgstr "الإفتراضي" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "تحقق مما إذا كان المضيف البعيد متصلًا بالإنترنت" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "تحذير: إذا تم تعطيل هذه الميزة ولم يكن المضيف البعيد متاحًا، فقد يؤدي ذلك " "إلى بعض الأخطاء الغريبة." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "تحقق مما إذا كان المضيف البعيد يدعم جميع الأوامر الضرورية." #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "استعادة الإعدادات" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "تحرير استدعاء المستخدم" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "سيتم إيقاف دعم EncFS في المستقبل القريب. لا يزال القرار بشأن بديل للحفاظ على" " دعم النسخ الاحتياطية المشفرة معلقًا، وذلك يعتمد على موارد المشروع وتوافر " "المساهمين. تتوفر مزيد من التفاصيل في هذه {whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "ملف شخصي جديد" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "أعِد تسمية الملف الشخصي" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "هل أنت متأكد أنك تريد حذف الملف الشخصي \"{name}\"؟" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "{BOLD}موصى به بشدة{ENDBOLD}: (جميع التوصيات مدرجة بالفعل.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}موصى به بشدة{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "لم تقم باختيار ملف مفتاح خاص لـ SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "هل ترغب في إنشاء زوج مفاتيح عام/خاص جديد بدون كلمة مرور؟" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "ملف المفتاح الخاص \"{file}\" غير موجود." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "هل ترغب في نسخ مفتاح SSH العام الخاص بك إلى المضيف البعيد لتمكين تسجيل " "الدخول بدون كلمة مرور؟" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "لا يمكن تأكيد مصداقية المضيف {host}." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "بصمة مفتاح {keytype} هي:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "يرجى التحقق من هذه البصمة. هل ترغب في إضافتها إلى ملف 'known_hosts' الخاص " "بك؟" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "إستثني نمط" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "إستثني ملف" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "إستثني مُجلد" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "ضمْن ملف" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" هو رابط رمزي. لن يتم نسخ الهدف المرتبط حتى تقوم بتضمينه أيضًا.\n" "هل ترغب في تضمين الهدف المرتبط بدلاً من ذلك؟" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "ضمْن مُجلد" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "هل أنت متأكد أنك تريد تغيير مجلد اللقطات؟" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "فشل في إنشاء مفتاح SSH جديد في {path}." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "معطل لأن هذه النمط غير فعال في وضع 'تشفير SSH'." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(افتراضي: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "مُعطَّل" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "مفعَّل" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "استيراد الإعدادات" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "لم يتم العثور على إعدادات" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "استورد" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "حدد مجلد اللقطة الذي يجب استيراد ملف التكوين منه. قد يبدو المسار كالتالي: " "{samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "إذا كان المجلد موجودًا على محرك خارجي أو بعيد، فيجب تركيبه يدويًا مسبقًا." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "خيارات حول مقارنة اللقطات" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "الأمر:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "المعلمات:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "استخدم %1 و %2 لوسائط المسار" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "يرجى تعيين أمر diff أو الضغط على إلغاء." #: qt/snapshotsdialog.py:90 #, 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:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "لم يتم تعيين أي معلمات لأمر الفرق. سيتم استخدام القيمة الافتراضية " "\"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "اللقطات المختلفة فقط" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "قم بإدراج اللقطات التي تساوي فقط:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "فحص بعمق (أكثر دقة, ولكن بطيء)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "احذف" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "تحديد الكل" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "مقارنة" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "أذهب إلى" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "خيارات" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "لا تستطيع مُقارنة اللقطة بنفسها." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "هل ترغب حقًا في حذف {file} من اللقطة {snapshot_id}؟" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "هل ترغب حقًا في حذف {file} في {count} لقطات؟" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "تحذير: لا يمكن التراجع عن ذلك." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "استبعاد {path} من اللقطات المستقبلية؟" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "لا يستطيع إيجاد crontab. هل أنت مُتأكد بأن cron مثبت فعلاً ؟ أن لم يكن كذلك " #~ "فعليك تعطيل جميع النسخ الإحتياطية الأوتوماتيكي." #~ msgid "Mode" #~ msgstr "الوضع" #, fuzzy #~ msgid "Profile" #~ msgstr "ملف شخصي" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "الملف الشخصي '{profile}': الرجاء دخول كلمة السر لـ{mode}: " #~ msgid "WARNING" #~ msgstr "تحذير" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "تحتوي الإصدار 1.7.2 او اقدم من encfs على علة مع خيار --reverse. الرجاء تحديث" #~ " encfs." backintime-1.5.2/common/po/bg.po000066400000000000000000001771221465446530500165110ustar00rootroot00000000000000# Bulgarian 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \n" "Language-Team: Bulgarian \n" "Language: bg\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Внимание" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Основен профил" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "криптиран" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH криптиран" #: common/config.py:309 msgid "Local" msgstr "Местен" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Частен SSH ключ" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "криптиран" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Криптиране" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH криптиран" #: common/config.py:327 msgid "Default" msgstr "По подразбиране" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Профил: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Папката за моментни архиви не е валидна!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Трябва да изберете поне една директория за архивиране!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Папката за резервно копие не може да бъде включена." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Не може да се включи подпапка за резервно копие." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Невалидна опция. {path} не е папка." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Хост/Потребител/Профил-ID не трябва да бъде празен." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Записването в {path} е невъзможно\n" "Сигурни ли сте, че имате достъп?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Крайната файлова система за {path} е от тип FAT и не поддържа преки пътища. " "Моля, използвайте Linux файлова система." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Крайната файлова система за {path} е от споделен тип SMB. Уверете се, че " "отдалеченият SMB сървър поддържа преки пътища или активирайте {copyLinks} в " "{expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Копирай връзките към файлове (следвай символните връзки)" #: common/config.py:497 msgid "Expert Options" msgstr "Експертни настройки" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Крайната файлова система за {path} е от споделен тип sshfs. Тя не поддържа " "преки пътища. Моля, използвайте режим 'SSH'." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Неуспех при създаването на нова периодична задача - crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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 Service " "{dbus_interface} не е открит" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Планираните при монтиране на диск архиви не работят в режим {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Не може да бъде намерен UUID за {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Запазването на конфигурацията се провали" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Зареждането на конфигурация се провали" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Профилът \"{name}\" вече съществува." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Последният профил не може да бъде премахнат." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Неуспех в монтирането '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Конфигурацията за криптираната папка не е намерена." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Създаване на нова криптирана папка?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Откажи" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Моля потвърдете паролата." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Паролата не съвпада." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Създайте моментен архив" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Не мога да демонтирам {mountprocess} от {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "{} не е намерен. Моля, инсталирайте например {}" #: common/mount.py:720 #, fuzzy, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Mountpoint {} не е празна." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "Провал" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Възстановете разрешенията" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Готово" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Отлагане на архивирането, докато устройството работи на батерия" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Папката за моментни архиви не може да бъде открита.\n" "Ако е на преносим драйв, моля включете го." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Изчакване %s секунда." msgstr[1] "Изчакване %s секунди." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Неуспех при създаването на моментен архив {snapshot_id} ." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Приключване" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Папката не може да бъде създадена" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Запазване на конфигурационен файл…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Запазване на правата…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Намерен е {snapshot_id}, който може да бъде продължен." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Премахване на остатъчна {snapshot_id} папка от последния курс" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Папката не може да бъде премахната" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Създаване на моментен архив" #: common/snapshots.py:1417 msgid "Success" msgstr "Успешно" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Частично прехвърляне поради грешка" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Частично прехвърляне поради изчезнали изходни файлове (вж. 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' завърши с код за изход {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "За повече информация вижте 'man rsync'" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Отрицателните изходни кодове на rsync са номера на сигнали, вижте 'kill -l' " "и 'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Няма промени, не е нужен нов моментен архив" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{new_path} не може да бъде преименуван на {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Умно премахване" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Премахване на старите моментни архиви" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Опит за запазване на минимално свободно пространство" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Опит за запазване на минимум {perc} свободни inode" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Сега" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Неуспех в монтирането на {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-агентът не е намерен. Моля, уверете се, че е инсталиран." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Не можа да отключи частния ключ на ssh. Грешна парола или паролата не е " "налична за cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Шифърът {cipher} е неуспешен за {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Отдалеченият път съществува, но не е директория." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Отдалеченият път не може да се записва." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Отдалеченият път не е изпълним." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Не може да се създаде отдалечен път." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Отдалеченият хост {host} не поддържа {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Вижте 'man backintime' за допълнителни инструкции" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Проверката на командите на хоста {host} върна непозната грешка" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Отдалеченият хост {host} не поддържа твърди връзки" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Копирай публичния ssh ключ \"{pubkey}\" в дистанционният хост \"{host}\"" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Моля въведете паролата за \"{user}\"" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Относно" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Създатели" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Преводачи" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Лизенз за изходния код" #: qt/app.py:169 msgid "Shortcuts" msgstr "Препратки" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Тази папка не съществува\n" "в текущата архив." #: qt/app.py:256 msgid "Add to Include" msgstr "Добавете, за да включите" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Добавете, за да изключите" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} изглежда, че се стартира за първи път, тъй като не е намерена " "конфигурация." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Импортиране на съществуваща конфигурация (от архивирана целева папка или от " "друг компютър)?" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Папката не може да бъде създадена" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Папката за моментни архиви не може да бъде открита.\n" "Ако е на преносимо устройство, моля включете го и натиснете ОК." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Направете моментен архив" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Използвайте времето и размера на модификацията за откриване на промени във " "файла." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Създаване на моментен архив (режим на контролна сума)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Използване на контролни суми за откриване на промени във файловете." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Пауза на моментното копие" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Продължаване на моментното копие" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Спиране на моментното копие" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Обновяване на списъка с архиви" #: qt/app.py:508 msgid "Name snapshot" msgstr "Наименувай архив" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Премахване на архив" #: qt/app.py:516 msgid "View snapshot log" msgstr "Преглед на дневника на архивите" #: qt/app.py:520 msgid "View last log" msgstr "Преглед на последния дневник" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Управление на профили…" #: qt/app.py:528 msgid "Shutdown" msgstr "Изключване" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Изключване на системата слад като моментното архивно копие е готово." #: qt/app.py:532 msgid "Setup language…" msgstr "Език на настройките…" #: qt/app.py:536 msgid "Exit" msgstr "Изход" #: qt/app.py:540 msgid "Help" msgstr "Помощ" #: qt/app.py:544 msgid "Profiles config file" msgstr "Файл за конфигуриране на профили" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Уебсайт" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "История на промените" #: qt/app.py:553 msgid "FAQ" msgstr "ЧЗВ" #: qt/app.py:556 msgid "Ask a question" msgstr "Задайте въпрос" #: qt/app.py:559 msgid "Report a bug" msgstr "Докладвайте неизправност" #: qt/app.py:562 msgid "Translation" msgstr "Превод" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Възстановяване" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Възстановете избраните файлове или папки на първоначалното място." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Възстановяване в …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Възстановете избраните файлове или папки на друго място." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Възстановете показаната папка и нейното съдържание на първоначалното място." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Възстановете показаната папка и нейното съдържание на ново място." #: qt/app.py:595 msgid "Up" msgstr "Нагоре" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Показване на скритите файлове" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Сравни архиви…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Назад във Времето" #: qt/app.py:665 msgid "&Backup" msgstr "&Резервно копие" #: qt/app.py:676 msgid "&Restore" msgstr "&Възстановяване" #: qt/app.py:682 msgid "&Help" msgstr "&Помощ" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Ако затворите този прозорец, Back in Time няма да може да изключи вашата система когато моментното копие е завършено.\n" "Наистина ли искате да го затворите?" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Наистина ли искате да възстановите този файл?" #: qt/app.py:987 msgid "Working:" msgstr "Изпълнение:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Готово, не е необходимо архивиране" #: qt/app.py:1044 msgid "Working" msgstr "Изпълняване" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Грешка" #: qt/app.py:1076 msgid "Sent" msgstr "Изпратено" #: qt/app.py:1077 msgid "Speed" msgstr "Скорост" #: qt/app.py:1078 msgid "ETA" msgstr "Приблизително оставащо време" #: qt/app.py:1140 msgid "Global" msgstr "Глобални" #: qt/app.py:1141 msgid "Root" msgstr "Файлова система" #: qt/app.py:1142 msgid "Home" msgstr "Домашна папка" #: qt/app.py:1170 msgid "Backup folders" msgstr "Резевни папки" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Име на моментния архив" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "" "Сигурни ли сте, че желаете окончателно да премахнете моментния архив?" msgstr[1] "" "Сигурни ли сте, че желаете окончателно да премахнете моментните архиви?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Създайте резервни копия, завършващи със {suffix} преди\n" "да бъдат презаписани или премахнати локални файлове." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "По-новите версии на файловете ще бъдат преименувани с последващ {suffix} " "преди възстановяването. Ако те не са необходими вече, може да ги премахнете " "посредством {cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Възстановете само файлове, които не съществуват,\n" "или са по-нови от тези в крайната папка.\n" "Използвайки опцията \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Премахнете по-новите файлове от първоначалната папка." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Възстановяване на избраните файлове или папки до първоначалната дестинация и" " изтриване на файлове/папки, които не са в моментният архив. Бъдете " "изключително внимателни,това ще доведе до изтриване на файлове/папки, които " "са били изключени по време на създаването на архива." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Наистина ли искате да възстановите този файл\n" "в новата директория {path}?" msgstr[1] "" "Наистина ли искате да възстановите тези файлове\n" "в новата директория {path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Наистина ли искате да възстановите този файл?" msgstr[1] "Наистина ли искате да възстановите файловете?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "Сигурни ли сте, че желаете окончателно да премахнете всички по-нови файлове " "в {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Сигурни ли сте, че искате да премахнете всички по-нови файлове в " "първоначалната директория?" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "ПРЕДУПРЕЖДЕНИЕ: Изтриването на файлове в коренната част на файловата система" " може да разруши цялата ви система!" #: qt/app.py:1750 msgid "Snapshot" msgstr "Моментно архивно копие" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Възстановете {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Възстановете {path} в …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Езиковите настройки влизат в сила само след рестартиране на Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 #, fuzzy msgid "Your Back In Time Team" msgstr "Назад във Времето" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Език на настройката" #: qt/languagedialog.py:92 msgid "System default" msgstr "Система по подразбиране" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Използвайте езика на операционните системи." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Преведено: {percent}" #: qt/languagedialog.py:188 #, 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/languagedialog.py:217 msgid "translation platform" msgstr "платформа за превод" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Вашият превод" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Последно преглеждане на записа със събития" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Преглеждане на записа със сибития относно моментните копия" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Профил:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Моментни архиви:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Филтър:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Всичко" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Промени" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Грешки" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Информация" msgstr[1] "Информация" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync неуспешен трансфер (експериментален)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[Г] Грешка, [И] Информация, [П] Промяна" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "декодиране на пътищата" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Въпрос" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Профил: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Преглед на последния запис" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Стартиране на {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Изпълняване…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Изпратено:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Скорост:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "приблизително оставащо време:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Моментни архиви" #: qt/qttools.py:427 msgid "Today" msgstr "Днес" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Вчера" #: qt/qttools.py:443 msgid "This week" msgstr "Тази седмица" #: qt/qttools.py:450 msgid "Last week" msgstr "Миналата седмица" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Това НЕ е моментно копие, а текущ изглед на локалните Ви файлове" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Последна проверка {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Покажи всички записани събития" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH Прокси" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Хост:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Порт:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Потребител:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Управление на профили" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Редактиране" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Добавяне" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Премахване" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Общи" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Режим:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Къде да се запазват моментните архиви" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Директория" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH настройки" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Път:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Шифър:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Частен ключ:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Изберете съществуващ частен ключ (обикновено озаглавен \"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Създайте нов SSH ключ без парола (не е позволено, ако файл с частен ключ е " "вече избран)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Парола" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Запазете паролата в ключодържателя" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Кеширайте паролата за периодичните задачи (Възможен проблем със сигурността:" " потребител root може да достъпва паролата)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Разширени" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Пълен път за моментни архивни копия:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Разписание" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Изключено" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "При всяко зареждане/рестартиране" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "На всяка {n} минута" msgstr[1] "На всеки {n} минути" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "На всеки час" msgstr[1] "На всеки час" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "На всеки {n} часа" msgstr[1] "На всеки {n} часа" #: qt/settingsdialog.py:457 #, fuzzy msgid "Custom hours" msgstr "Часове по предпочитание" #: qt/settingsdialog.py:458 #, fuzzy msgid "Every day" msgstr "Ежедневно" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Многократно (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Когато дискът бъде свързан (udev)" #: qt/settingsdialog.py:461 #, fuzzy msgid "Every week" msgstr "Ежеседмично" #: qt/settingsdialog.py:462 #, fuzzy msgid "Every month" msgstr "Ежемесечно" #: qt/settingsdialog.py:463 #, fuzzy msgid "Every year" msgstr "Ежегодишно" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Работен ден:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Час:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Часа:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Изпълнявайте Back In Time многократно. Това е полезно, когато компютърът не " "работи редовно." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Всеки:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Час (а)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Дни" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Седмици" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Месеци" #: qt/settingsdialog.py:555 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 "" "Изпълнявайте Back In Time веднага щом дискът бъде свързан (веднъж на X дни).\n" "Ще бъдете помолени да въведете вашата sudo парола." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Включване" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Включване на файлове и папки" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Добавяне на файл" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Добавяне на папка" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Изключване" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Изключване на шарки, файлове или папки" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Добавете по подразбиране" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Изключете файлове, по-големи от:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Изключете файлове, по-големи от: {size_unit}" #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Изключете файлове, по-големи от стойността в %(prefix)s.\n" "При изключен 'Full rsync mode', това ще засегне само новите файлове,\n" "защото това е преносна опция за rsync, а не изключваща такава.\n" "Така че големите файлове, които са били архивирани преди, ще останат в\n" "моментните копия дори и да са били променени." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Автоматично премахване" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "По-стари от:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Години" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Ако свободното пространство е по-малко от:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Ако има по-малко inodes от:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Умно премахване:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Изпълнява се във фонов режим на отдалечен хост." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "ЕКСПЕРИМЕНТАЛНО" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Пазете всички временни архиви за последните" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 #, fuzzy msgid "day(s)." msgstr "дни" #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Пазете едно моментно копие на ден за последните" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Пазете едно моментно копие на седмица за последните" #: qt/settingsdialog.py:818 #, fuzzy msgid "week(s)." msgstr "седмица(и)" #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Пазете едно моментно копие на месец за последните" #: qt/settingsdialog.py:825 #, fuzzy msgid "month(s)." msgstr "месеца" #: qt/settingsdialog.py:828 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Пазете едно моментно копие на година" #: qt/settingsdialog.py:837 #, fuzzy msgid "Don't remove named snapshots." msgstr "Да не се премахват именувани архиви" #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Настройки" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Включване на известяването" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Изключване на моментни архиви при работа на батерия" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Енергийното състояние не е налично от системата" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Изпълнявайте по едно моментно копие наведжъж" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Останалите моментни копия ще бъдат блокирани докато текущото такова не приключи.\n" "Това е глобална опция и ще повлияе на всички профили на потребителя.\n" "Но трябва да я изберете също и за останалите потребители." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Архивирайте подменените файлове при възстановяване" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "По-новите версии на файловете ще бъдат преименувани с последващ {suffix} " "преди възстановяването. Ако те не са необходими вече, може да ги премахнете " "посредством {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Продължаване при грешки (запазване на непълни моментни архиви)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Използвайте проверка на контролната сума, за да засечете промените" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Направете ново моментно копие независимо дали има промени или не." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Ниво на записа:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Нищо" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Е&кспертни настройки" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Внимание: Променяйте тези опции само ако наистина знаете какво правите." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Изпълнете 'rsync' посредством '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "като периодична задача" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "върху отдалечен хост" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "когато се изпълнява ръчно моментно копие" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Моля инсталирайте 'nocache', за да активирате тази опция)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "върху настоящата машина" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Пренасочете стандартният изход към /dev/null при периодичните задачи." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" "Пренасочете стандартният изход за грешки към /dev/null за периодични задачи." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "Ограничете използваният трафик от rsync" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/сек" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Запазване на ACL (списък за контрол на достъпа)" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Запазване на разширените атрибути (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Копиране на несигурните връзки (работи само с абсолютни връзки)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Опциите трябва да бъдат цитирани, напр. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Добавете допълнителни опции към rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Добавете префикс към SSH командите" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Префикс, който да се изпълнява преди всяка команда на отдалечения хост.\n" "Променливите трябва да се избягват с \\$FOO.\n" "Това не засяга rsync. Така че, за да добавите префикс\n" "за rsync използвайте \"%(cbRsyncOptions)s\" с\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "по подразбиране" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Проверете дали отдалеченият хост е онлайн" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Внимание: ако е изключено и отдалеченият хост\n" "не е наличен, това може да доведе до някои\n" "непредвидени грешки." #: qt/settingsdialog.py:1218 #, fuzzy msgid "Check if remote host supports all necessary commands." msgstr "Проверете дали отдалеченият хост поддържа всички необходими команди" #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Внимание: ако е изключено и отдалеченият хост\n" "не поддържа всички необходими команди,\n" "това може да довете до неочаквани грешки." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Възстановете конфигурацията" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Променете потребителския скрипт" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Нов профил" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Преименуване на профил" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Сигурни ли сте, че желаете да изтриете профила \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Силно препоръчително" #: qt/settingsdialog.py:1634 #, fuzzy 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Не сте избрали частен ключ за SSH.\n" "Бихте ли искали да създадете нова двойка частен/публичен ключ без парола?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Файлът с частния ключ \"{file}\" не съществува." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Бихте ли искали да копирате вашият публичен SSH ключ върху\n" "отдалеченият хост, за да активирате логин без парола?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "Достоверността на хоста {host} не може да бъде потвърдена.\n" "\n" "{keytype} отпечатъкът на ключа е:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Моля потвърдете този отпечатък! Бихте ли искали да го добавите към " "'known_hosts' файлът с позволени хостове?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Шаблон за изключване" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Изключване на файл" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Изключване на папка" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Включване на файл" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" е пряк път. Целта, към която сочи, няма да бъде архивирана докато не добавите и нея.\n" "Бихте ли искали да добавите крайната цел вместо това?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Включване на папка" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Сигурни ли сте, че желаете да промените папката за моментни архиви ?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Неуспех при създаване на нов SSH ключ в {path}" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "по подразбиране" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "деактивирано" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "активирано" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Не е намерена конфигурация" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Опции за сравняване на моментни архиви" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Команда" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Параметри:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Използване на %1 и %2 за параметри на пътя" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Само различаващи се моментни архиви" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Покажете само съвпадащи моментни архиви до: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Дълбока проверка (по-точна, но бавна)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Изтриване" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Избиране на всички" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Сравни" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Отиване до" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Настройки" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Не можете да сравнявате моментен архив със самия него." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Наистина ли искате да изтриете {file} от моментния архив {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Наистина ли искате да изтриете {file} от {count} моментни архиви?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "Това действие не може да бъде отменено." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Изключете {path} от бъдещите моментни архиви?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "crontab не може да бъде намерен.\n" #~ "Сигурни ли сте, че cron е инсталиран?\n" #~ "Ако не е, трябва да изключите всички автоматични архивирания." #~ msgid "Full snapshot path" #~ msgstr "Пълен път за моментното архивно копие" #~ msgid "Mode" #~ msgstr "Режим" #~ msgid "Profile" #~ msgstr "Профил" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Профил '{profile}': Въведете парола за {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Шебангът в потребителският скрипт не е изпълним." #~ msgid "WARNING" #~ msgstr "ВНИМАНИЕ" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "Във версия 1.7.2 и по-ранни версии на encfs има грешка с опцията --reverse. " #~ "Моля, актуализирайте encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "Потребителският скрипт няма шебанг (#!/bin/sh) на първия ред." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} използва EncFS за криптиране. Скорошен одит на сигурността разкри " #~ "възможни уязвимости. Моля погледнете \"A NOTE ON SECURITY\" в \"man " #~ "backintime\"." backintime-1.5.2/common/po/bs.po000066400000000000000000001204061465446530500165160ustar00rootroot00000000000000# 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: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-05 20:13+0000\n" "Last-Translator: buhtz \n" "Language-Team: Bosnian \n" "Language: bs\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.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Upozorenje" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Glavni profil" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "šifrirano" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH šifrirano" #: common/config.py:309 msgid "Local" msgstr "Lokalno" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH Privatni kljuc" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "šifrirano" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Šifriranje" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH šifrirano" #: common/config.py:327 msgid "Default" msgstr "Default" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Folder za slike ekrana nije validan !" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Moraš izabrati najmanje jedan folder za rezervu!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Backup folder nije nemoguće dodati." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Backup sub-folder nije moguće dodati." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opcija nije validna. {path} nije direktorij." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profil-ID ne smije biti prazna." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nemoguće pisati u: {path}\n" "Jeste sigurni da imate pravo pisanja?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Odredište sistema datoteka za {path} je formatiran pomoću FAT-a, koji ne " "podržava tvrde linkove. Molimo koristite Linuxov maternji sistem datoteka." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopiraj linkove (dereferenciraj simbolicke linkove)" #: common/config.py:497 msgid "Expert Options" msgstr "Expert opcije" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Odredište sistema datoteka za {path} je formatiran pomoću FAT-a, koji ne " "podržava tvrde linkove. Molimo koristite Linuxov maternji sistem datoteka." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Pokušaj pisanja novog crontab-a nije uspio." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Zakazani udev ne radi sa modom {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Nije bilo moguce pronaci UUID za {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Spremanje configuracije neuspješno" #: common/configfile.py:143 msgid "Failed to load config" msgstr "" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" već postoji." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Poslednji profil ne može biti obrisan." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "" #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Napravi novi encrypted folder?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Odustani" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Molimo Vas potvrdite šifru." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Šifre nisu iste." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Napravi sliku" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "{} nije pronađen. Molimo instalirajte npr. {}" #: common/mount.py:720 #, fuzzy, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Mountpoint {} nije prazan." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "NIJE USPJELO" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Spremi dozvole" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Završeno" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Čekaj %s sekundu." msgstr[1] "Čekaj %s sekunde." msgstr[2] "Čekaj %s sekundi." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "" #: common/snapshots.py:937 msgid "Finalizing" msgstr "Završava" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Nemoguće kreirati direktorij" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Spašava se konfiguracijski fajl…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Spremi dozvole …" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Nemoguće izbrisati direktorij" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "" #: common/snapshots.py:1417 msgid "Success" msgstr "" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Pametno brisanje" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Sad" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Nemoguće kreirati direktorij." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Molimo Vas potvrdite šifru" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "O" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "" #: qt/app.py:169 msgid "Shortcuts" msgstr "Prečice" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:256 msgid "Add to Include" msgstr "" #: qt/app.py:258 msgid "Add to Exclude" msgstr "" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Nemoguće kreirati direktorij" #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" #: qt/app.py:481 msgid "Take a snapshot" msgstr "" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "" #: qt/app.py:508 msgid "Name snapshot" msgstr "" #: qt/app.py:512 msgid "Remove snapshot" msgstr "" #: qt/app.py:516 msgid "View snapshot log" msgstr "" #: qt/app.py:520 msgid "View last log" msgstr "" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Glavni profil…" #: qt/app.py:528 msgid "Shutdown" msgstr "" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:532 msgid "Setup language…" msgstr "" #: qt/app.py:536 msgid "Exit" msgstr "Izlaz" #: qt/app.py:540 msgid "Help" msgstr "Pomoć" #: qt/app.py:544 msgid "Profiles config file" msgstr "" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "" #: qt/app.py:553 msgid "FAQ" msgstr "" #: qt/app.py:556 msgid "Ask a question" msgstr "" #: qt/app.py:559 msgid "Report a bug" msgstr "" #: qt/app.py:562 msgid "Translation" msgstr "" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Obnovi" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Obnovi …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:595 msgid "Up" msgstr "Gore" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Prikaži skrivene datoteke" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Napravi sliku…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 msgid "&Restore" msgstr "Ob&novi" #: qt/app.py:682 msgid "&Help" msgstr "&Pomoć" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Da li zelite izbrisati {file} u {count} snapshots?" #: qt/app.py:987 msgid "Working:" msgstr "" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "" #: qt/app.py:1044 msgid "Working" msgstr "" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "" #: qt/app.py:1076 msgid "Sent" msgstr "" #: qt/app.py:1077 msgid "Speed" msgstr "" #: qt/app.py:1078 msgid "ETA" msgstr "" #: qt/app.py:1140 msgid "Global" msgstr "Globalno" #: qt/app.py:1141 msgid "Root" msgstr "Korijen" #: qt/app.py:1142 msgid "Home" msgstr "" #: qt/app.py:1170 msgid "Backup folders" msgstr "" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "" #: qt/app.py:1313 #, fuzzy msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Da li zelite izbrisati {file} u {count} snapshots?" msgstr[1] "Da li zelite izbrisati {file} u {count} snapshots?" msgstr[2] "Da li zelite izbrisati {file} u {count} snapshots?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "Da li zelite izbrisati {file} u {count} snapshots?" msgstr[1] "Da li zelite izbrisati {file} u {count} snapshots?" msgstr[2] "Da li zelite izbrisati {file} u {count} snapshots?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Da li zelite izbrisati {file} u {count} snapshots?" msgstr[1] "Da li zelite izbrisati {file} u {count} snapshots?" msgstr[2] "Da li zelite izbrisati {file} u {count} snapshots?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" #: qt/app.py:1750 msgid "Snapshot" msgstr "" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Obnovi {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Obnovi {path} …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 msgid "System default" msgstr "" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:188 #, 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 "" "Pozdrav,\n" "Nekoliko puta do sad ste iskoristili Back In Time na {language} jeziku.\n" "Prijevod Back In Time na {language} na {perc} dovršenosti. Nevezano od vašeg nivoa tehničke vičnosti, možete doprinijeti prijevodu i samim time Back In Time.\n" "Molimo vas da posjetite {translation_platform_url} ukoliko želite učiniti doprinos. Za sva dalja pitanja i pomoć, molimo vas posjetite {back_in_time_project_website}.\n" "Izvinjavamo se na smetnji, neće vam se više prikazivati ova poruka. Ovom dijalogu možete pristupiti u bilo kojem trenutku putem menija za pomoć.\n" "Vaš tim iz Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Napravi sliku:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "" msgstr[1] "" msgstr[2] "" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "" #: qt/qttools.py:427 msgid "Today" msgstr "Danas" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Jučer" #: qt/qttools.py:443 msgid "This week" msgstr "Ove sedmice" #: qt/qttools.py:450 msgid "Last week" msgstr "Prošle sedmice" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Glavni profil" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Opšte" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "SSH Privatni kljuc:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Isključeno" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Svakih {n} minuta" msgstr[1] "Svakih {n} minuta" msgstr[2] "Svakih {n} minuta" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "" msgstr[1] "" msgstr[2] "" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Svakih {n} sati" msgstr[1] "Svakih {n} sati" msgstr[2] "Svakih {n} sati" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Svaki dan" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Svake sedmice" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Svaki mjesec" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Svake godine" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Svakih:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dan(i)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Sedmica(e)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:555 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:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Uključi" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Dodaje datoteku" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Dodaj direktorij" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Isključi" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "" #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Automatsko brisanje" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Starije od:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Godina(e)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Ako je sloboni prostor manji od:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Ako je sloboni prostor manji od:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Pametno brisanje:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Dan(i)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Sedmica(e)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "" #: qt/settingsdialog.py:849 msgid "&Options" msgstr "Op&cije" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Uključi obavijesti" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "" #: qt/settingsdialog.py:911 msgid "None" msgstr "" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Opcije &izvoza" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, 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/settingsdialog.py:1196 msgid "default" msgstr "" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Uključi direktorij" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Pokušaj pisanja novog crontab-a nije uspio." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "Default" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Komanda" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametri:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Koristite %1 i %2 za parametre putanje" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Idi na" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opcije" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "" #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Da li zelite izbrisati {file} u {count} snapshots?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Ne možemo pronaći crontab.\n" #~ "Da li ste sigurni da je cron instaliran?\n" #~ "Ukoliko nije, onesposobite sve automatske backup-e." #~ msgid "Profile" #~ msgstr "Profil" backintime-1.5.2/common/po/ca.po000066400000000000000000001603251465446530500165010ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Avís" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Local (xifrat EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (xifrat EncFS)" #: common/config.py:309 msgid "Local" msgstr "Local" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Clau privada SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Xifrat local" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Xifrat" #: common/config.py:320 msgid "SSH encrypted" msgstr "Xifrat amb SSH" #: common/config.py:327 msgid "Default" msgstr "Predeterminat" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "La carpeta d'instantànies no es vàlida!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "" "Heu de seleccionar almenys una carpeta per fer una còpia de seguretat!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "No s'hi pot incloure la carpeta de còpia de seguretat." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "No s'hi pot incloure la subcarpeta de la còpia de seguretat." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "L'opció no vàlida. {path} no és una carpeta." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID no pot estar buit." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "No es pot escriure a: {path}\n" "Segur que teniu permisos d'escriptura?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "El sistema de fitxers de destí per a {path} està formatat amb FAT que no " "admet hard-links. Utilitzeu un sistema de fitxers Linux natiu." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "El sistema de fitxers de destí 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/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Copia enllaços (dereferencia els enllaços simbòlics)" #: common/config.py:497 msgid "Expert Options" msgstr "Opcions avançades" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. 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/config.py:1658 msgid "Failed to write new crontab." msgstr "No s'ha pogut escriure el nou crontab ." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron no s'executa tot i que crontab command està disponible. Les tasques de " "còpia de seguretat programades no s'executaran. Cron pot estar instal·lat " "però no habilitat. Proveu el command \"systemctl enable cron\" o consulteu " "els canals de suport de la vostra distribució GNU Linux." #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "El planificador udev no funciona amb el mode {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "No s'ha pogut trobar l'UUID per a {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "No s'ha pogut desar la configuració" #: common/configfile.py:143 msgid "Failed to load config" msgstr "No s'ha pogut carregar la configuració" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "El perfil \"{name}\" ja existeix." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "No es pot eliminar l'últim perfil." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "No s'ha pogut muntar '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "No s'ha trobat la configuració de la carpeta xifrada." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Voleu crear una nova carpeta xifrada?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Canceŀla" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Si us plau confirmeu la contrasenya." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "La contrasenya no coincideix." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Fes una instantània" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "No es pot desmuntar {mountprocess} des de {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "No s'ha trobat {command}. Si us plau instal·leu p. ex. {installcommand}" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "El punt de muntatge {mntpoint} no és buit." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Introduïu la contrasenya per a {mode} profile \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "S'HA PRODUÏT UN ERROR" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Restaura els permisos" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Fet" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Es posposa la còpia de seguretat mentre s'utilitza la bateria" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "No es pot trobar la carpeta d'instantànies.\n" "Si és en una unitat extraïble, connecteu-la." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Esperant %s segon." msgstr[1] "Esperant %s segons." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "No s'ha pogut fer la instantània {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Està finalitzant" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "No es pot crear la carpeta" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Desa el fitxer de configuració …" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "S'estan desant els permisos …" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "S'ha trobat la resta {snapshot_id} que es pot continuar." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" "S'està suprimint la carpeta sobrant de l'última execució: {snapshot_id}" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "No es pot eliminar la carpeta" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "S'està fent una instantània" #: common/snapshots.py:1417 msgid "Success" msgstr "Èxit" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Transferència parcial deguda a un error" #: common/snapshots.py:1421 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/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' ha finalitzat amb el codi de sortida {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Consulteu 'man rsync' per a més detalls" #: common/snapshots.py:1445 msgid "" "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/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "No hi ha canvis, no cal una instantània nova" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "No es pot canviar el nom de {new_path} a {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Esborrat intel·ligent" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Elimina les instantànies antigues" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Proveu de mantenir un minim d'espai lliure" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Proveu de mantenir mínim un {perc} d'inodes lliures" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Ara" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "No s'ha pogut muntar {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "no s'ha trobat l'ssh-agent. Assegureu-vos que està instal·lat." #: common/sshtools.py:444 msgid "" "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/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "El xifratge {cipher} ha fallat per a {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "El camí remot existeix però no és un directori." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "No es pot escriure a la ruta remota." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "La ruta remota no és executable." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "No es pot crear la ruta remota." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "L'amfitrió remot {host} no admet {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Mireu 'man backintime' per a més instruccions" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Comproveu les ordres a l'amfitrió {host} ha retornat un error desconegut" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "L'amfitrió remot {host} no admet hardlinks" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Copieu la clau ssh pública \"{pubkey}\" al servidor remot \"{host}\"" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Confirmeu la contrasenya per \"{user}\"" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Quant a" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autors" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Traduccions" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Llicència" #: qt/app.py:169 msgid "Shortcuts" msgstr "Dreceres" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Aquesta carpeta no existeix\n" "a la instantània seleccionada." #: qt/app.py:256 msgid "Add to Include" msgstr "Afegeix a la inclusió" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Afegeix a l'exclusió" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} sembla que sigui el primer cop que s'executa perquè no s'ha " "trobat cap configuració." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Importeu una configuració existent (des d'una carpeta d'una còpia de " "seguretat o d'un altre ordinador)?" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "No es pot crear la carpeta" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "No es pot trobar la carpeta d'instantànies.\n" "Si es troba en una unitat extraïble, connecteu-la i premeu OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Fes una instantània" #: qt/app.py:483 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." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Pren una instantània (mode suma de verificació)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Utilitza sumes de verificació per a la detecció de canvis de fitxers." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pausa el procés d'instantània" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Reprèn el procés d'instantània" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Atura el procés d'instantània" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Refresca la llista d'instantànies" #: qt/app.py:508 msgid "Name snapshot" msgstr "Anomena la instantània" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Esborra la instantània" #: qt/app.py:516 msgid "View snapshot log" msgstr "Visualitza el registre de les instantànies" #: qt/app.py:520 msgid "View last log" msgstr "Visualitza l'últim registre" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Gestiona els perfils…" #: qt/app.py:528 msgid "Shutdown" msgstr "Aturada" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Apaga el sistema després d'acabar la instantània." #: qt/app.py:532 msgid "Setup language…" msgstr "Llenguatge de configuració…" #: qt/app.py:536 msgid "Exit" msgstr "Sortir" #: qt/app.py:540 msgid "Help" msgstr "Ajuda" #: qt/app.py:544 msgid "Profiles config file" msgstr "Fitxer de configuració de perfils" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Lloc web" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Registre de canvis" #: qt/app.py:553 msgid "FAQ" msgstr "PMF" #: qt/app.py:556 msgid "Ask a question" msgstr "Fer una pregunta" #: qt/app.py:559 msgid "Report a bug" msgstr "Informar d'un error" #: qt/app.py:562 msgid "Translation" msgstr "Traducció" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Mostra de nou el missatge sobre la participació en la traducció." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Encriptació en Transició (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Mostra de nou el missatge sobre l'eliminació d'EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Restaura" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Restaura els fitxers o carpetes seleccionades a la destinació original." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Restaura a …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Restaura els fitxers o carpetes seleccionades a una destinació nova." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Restaura la carpeta mostrada actualment i tot el seu contingut a la " "destinació original." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Restaura la carpeta mostrada actualment i tot el seu contingut a una nova " "destinació." #: qt/app.py:595 msgid "Up" msgstr "Amunt" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Mostra els fitxers ocults" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Compara les instantànies…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Retorn al Futur" #: qt/app.py:665 msgid "&Backup" msgstr "&Còpia de seguretat" #: qt/app.py:676 msgid "&Restore" msgstr "&Restaura" #: qt/app.py:682 msgid "&Help" msgstr "&Ajuda" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Si tanqueu aquesta finestra Back In Time no podrà apagar el sistema quan la instantània hagi finalitzat.\n" "De debò vols tancar?" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Segur que voleu restaurar aquest element?" #: qt/app.py:987 msgid "Working:" msgstr "S'està treballant:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Fet, no ha estat necessari crear una còpia de seguretat" #: qt/app.py:1044 msgid "Working" msgstr "Treballant" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Error" #: qt/app.py:1076 msgid "Sent" msgstr "Enviat" #: qt/app.py:1077 msgid "Speed" msgstr "Velocitat" #: qt/app.py:1078 msgid "ETA" msgstr "ETA" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Arrel" #: qt/app.py:1142 msgid "Home" msgstr "Inici" #: qt/app.py:1170 msgid "Backup folders" msgstr "Carpetes de còpia de seguretat" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nom de la instantània" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Segur que voleu esborrar la instantània?" msgstr[1] "Segur que voleu esborrar les instantànies?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "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/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Les versions més noves dels fitxers es canviaran de nom amb el final {suffix} abans de restaurar.\n" "Si ja no els necessiteu, podeu eliminar-los amb {cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Restaura només els fitxers que no existeixen o\n" "són més nous que els de destí.\n" "Utilitzant l'opció \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Esborra els fitxers més nous de la carpeta original." #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Restaura els fitxers o carpetes seleccionats al destí original i\n" "suprimeix els fitxers/carpetes que no són a la instantània.\n" "Això suprimirà els fitxers/carpetes que s'han exclòs durant la creació de la instantània!\n" "Aneu amb compte!" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Segur que voleu restaurar aquest element\n" "a la carpeta nova {path}?" msgstr[1] "" "Segur que voleu restaurar aquests elements\n" "a la carpeta nova {path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Segur que voleu restaurar aquest element?" msgstr[1] "Segur que voleu restaurar aquests elements?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Esteu segur que voleu eliminar tots els fitxers més nous a {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Esteu segur que voleu eliminar tots els fitxers més nous de la carpeta " "original?" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "AVÍS: Esborrar fitxers a l'arrel del sistema de fitxers podria trencar tot " "el sistema!" #: qt/app.py:1750 msgid "Snapshot" msgstr "Instantàna" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Restaura {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaura {path} a …" #: qt/app.py:1948 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/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "El suport per a EncFS s'interromprà en un futur previsible. A més, no es " "recomana utilitzar aquest mode per a un perfil." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Encara està pendent una decisió sobre un reemplaçament per al suport " "continuat de les còpies de seguretat xifrades, depenent dels recursos del " "projecte i la disponibilitat dels col·laboradors. Hi ha més detalls " "disponibles en aquest {whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "paper en blanc" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "El suport per als perfils snapshots xifrats està experimentant canvis " "significatius i EncFS s'eliminarà en un futur previsible." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Els perfils següents utilitzen xifratge amb EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Encara està pendent una decisió sobre un reemplaçament per al suport " "continuat de les còpies de seguretat xifrades, depenent dels recursos del " "projecte i la disponibilitat dels col·laboradors. Els usuaris estan " "convidats a unir-se a aquesta discussió. Els detalls actualitzats dels " "passos següents estan disponibles en aquest {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Aquest missatge no es tornarà a mostrar. Aquest diàleg està disponible en " "qualsevol moment mitjançant el menú d'ajuda." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "El teu equip de Retorn al Futur" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Llenguatge de configuració" #: qt/languagedialog.py:92 msgid "System default" msgstr "sistema per defecte" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Utilitza el llenguatge del sistema operatiu." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Traduït: {percent}" #: qt/languagedialog.py:188 #, 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 "" "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/languagedialog.py:217 msgid "translation platform" msgstr "plataforma de traducció" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "la teva traducció" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Última visualització dels registres" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Visualitza el registre d'instantànies" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Perfil:" #: qt/logviewdialog.py:84 #, fuzzy msgid "Snapshots:" msgstr "Instantànies" #: qt/logviewdialog.py:99 #, fuzzy msgid "Filter:" msgstr "Filtre" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Tots" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Canvis" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Errors" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Informació" msgstr[1] "Informació" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "Errors de transferència de rsync (experimental)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E]Error, [I]Informació, [C]Canvia" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "descodifica rutes" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Pregunta" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Perfil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Visualitza l'últim registre" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Inicia {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "S'està treballant…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Enviat:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Velocitat:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Instantànies" #: qt/qttools.py:427 msgid "Today" msgstr "Avui" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Ahir" #: qt/qttools.py:443 msgid "This week" msgstr "Aquesta setmana" #: qt/qttools.py:450 msgid "Last week" msgstr "La setmana passada" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "No és una instantània, sinó una vista en directe dels vostres fitxers locals" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Última comprovació {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Mostra el registre complet" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH Proxy" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Ordinador amfitrió:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Usuari:" #: qt/settingsdialog.py:120 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 "" "Connecteu-vos a l'amfitrió del destí mitjançant aquest servidor intermediari" " (també conegut com a jump host). Vegeu \"-J\" a la documentació de l'ordre " "\"ssh\" o \"ProxyJump\" a la pàgina del manual \"ssh_config\" per obtenir " "més informació." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Gestiona els perfils" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Edita" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Afegeix" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Elimina" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&General" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 #, fuzzy msgid "Mode:" msgstr "Mode" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "A on guardar Instanànies" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Carpeta" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Paràmetres SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Ruta:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Xifratge:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Clau privada:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Trieu un fitxer de clau privada existent (normalment anomenat \"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Crea una clau SSH nova sense contrasenya (no es permet si ja hi ha " "seleccionat un fitxer de clau privada)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Contrasenya" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Desa la contrasenya a l'anell de claus" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Contrasenya de la memòria cau per al Cron (problema de seguretat: " "l'administrador pot llegir la contrasenya)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Avançat" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Ruta completa de la instantània:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Planificació" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Deshabilitat" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "En cada inici/reinici" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Cada {n} minut" msgstr[1] "Cada {n} minuts" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Cada hora" msgstr[1] "Cada hora" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Cada {n} hora" msgstr[1] "Cada {n} hores" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Hores personalitzades" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Cada dia" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Repetidament (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Quan la unitat es connecti (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Cada setmana" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Cada mes" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Cada any" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Día:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Entre setmana:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Hora:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Hores:" #: qt/settingsdialog.py:521 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:528 msgid "Every:" msgstr "Cada:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Hora(es)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dia(es)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Setmana(es)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Mes(os)" #: qt/settingsdialog.py:555 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 "" "Executa enrere en el temps tan aviat com la unitat estigui connectada (només una vegada cada X dies).\n" "Se us demanarà la contrasenya sudo." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Activa el registre dels missatges de depuració" #: qt/settingsdialog.py:568 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:570 msgid "" "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:583 msgid "&Include" msgstr "&Inclou" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Inclou fitxers i carpetes" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Afegeix fitxer" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Afegeix un directori" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Exclou" #: qt/settingsdialog.py:625 #, 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ó{ENDBOLD}: en el mode \"encriptat SSH\", només funcionen els" " asteriscs simples o dobles (p. ex., {example2}). S'ignoraran altres tipus " "de comodins i patrons (p. ex., {example1}). Els noms de fitxers són " "impredictibles en aquest mode a causa del xifratge per EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Exclou fitxers i carpetes" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Afegeix per defecte" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Exclou els fitxers més grans que:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Exclou els fitxers més grans que: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Exclou els fitxers més grans que el valor a %(prefix)s.\n" "Amb el «mode rsync complet» desactivat, això només afectarà els fitxers nous\n" "perquè per al «rsync» aquesta és una opció de transferència, no una opció d'exclusió.\n" "Doncs els fitxers grans ja copiats previament romandran en les instantànies\n" "encara que hagin canviat." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Eliminació &automàtica" #: qt/settingsdialog.py:726 #, fuzzy msgid "Older than:" msgstr "Més antics que" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Any(s)" #: qt/settingsdialog.py:748 #, fuzzy msgid "If free space is less than:" msgstr "Si l'espai lliure és inferior a" #: qt/settingsdialog.py:768 #, fuzzy msgid "If free inodes is less than:" msgstr "Si els inodes lliures són inferiors a" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Esborrat intel·ligent:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Executa en segon pla en una màquina remota." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Mantingues totes les instantànies durant els darrers" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dia(es)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Mantingues una instantània al dia durant els darrers" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Mantingues una instantània a la setmana durant les darreres" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "setmana(es)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Mantingues una instantània al mes durant els darrers" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mes(os)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Conserva una instantània de cada any." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "No eliminis les instantànies amb nom." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opcions" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Activa les notificacions" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Desactiva les instantànies treballant en bateria" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "No està disponible l'estat de l'energia" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Executa només una instantània alhora" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Altres instantànies es bloquejaran fins que es faci la instantània actual.\n" "Aquesta és una opció global. Per tant, afectarà tots els perfils d'aquest usuari.\n" "Però també heu d'activar això per a tots els altres usuaris." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "La còpia de seguretat ha substituïts fitxers en restaurar" #: qt/settingsdialog.py:879 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Les versions més noves dels fitxers es canviaran de nom amb el final {suffix} abans de restaurar.\n" "Si ja no els necessiteu, podeu eliminar-los amb {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continua amb errors (Mantingues instantànies incompletes)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Utilitza el checksum per detectar canvis" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Preneu una nova instantània tant si hi ha canvis com si no." #: qt/settingsdialog.py:906 #, fuzzy msgid "Log Level:" msgstr "Nivell de registre" #: qt/settingsdialog.py:911 msgid "None" msgstr "Cap" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Opcions d'e&xperts" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Precaució: Canvieu aquestes opcions només si realment sabeu el que esteu " "fent." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Executa 'rsync' amb '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "com a tasca cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "a l'amfitrió remot" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "en prendre una instantània manual" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Si us plau, instal·leu 'nocache' per a activar aquesta opció)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "a la màquina local" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirigeix la stdout a /dev/null a les funcions cron." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Cron enviarà automàticament un correu electrònic amb la sortida adjunta de " "cronjobs si hi ha instal·lat un MTA." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirigeix stderr a /dev/null a les funcions cron." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Cron enviarà automàticament un correu electrònic amb errors adjunts de " "cronjobs si hi ha instal·lat un MTA." #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "Limita l'ús de l'amplada de banda 'rsync'" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/seg" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Preserva ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Preserva els atributs extesos (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Copia links insegurs (Només funciona amb links absoluts)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Limiteu-vos a un sistema de fitxers" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Les opcions s'han de citar, p. ex. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Enganxa les opcions addicionals al 'rsync'" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Afegeix un prefix a les ordres SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefix per executar-se abans de cada comanda a l'amfitrió remot." #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Prefix per a executar abans de cada ordre en una màquina remota.\n" "Les variables s'han d'escapar amb \"\\$FOO\".\n" "Això no toca el rsync. Per afegir un prefix\n" "per al rsync utilitzeu \"%(cbRsyncOptions)s\" amb\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "per defecte" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Comprova si l'amfitrió remot està connectat" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Avís: si està desactivat i l'amfitrió remot\n" "no està disponible, això podria portar a alguns\n" "errors estranys." #: qt/settingsdialog.py:1218 #, fuzzy msgid "Check if remote host supports all necessary commands." msgstr "Comprova si l'amfitrió remot admet totes les ordres necessàries" #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Avís: si està desactivat i l'amfitrió remot\n" "no admet totes les ordres necessàries,\n" "Això podria provocar alguns errors estranys." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Restaura la configuració" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Edita user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "El suport per a EncFS s'interromprà en un futur previsible. Encara està " "pendent una decisió sobre un reemplaçament per al suport continuat de les " "còpies de seguretat xifrades, depenent dels recursos del projecte i la " "disponibilitat dels col·laboradors. Hi ha més detalls disponibles en aquest " "{whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Perfil nou" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Reanomena el perfil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Esteu segurs de que voleu eliminar el perfil \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Molt recomanable{ENDBOLD}: (Totes les recomanacions ja estan " "incloses.)" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Molt recomanat" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "No heu escollit un fitxer de clau privada per a SSH." #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "No heu triat cap fitxer de clau privada per a SSH.\n" "Voleu generar un nou parell de claus públic/privat sense contrasenya?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "El fitxer de clau privada \"{file}\" no existeix." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Voleu copiar la vostra clau SSH pública a la\n" "màquina remota per a habilitar l'inici de sessió sense contrasenya?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "No es pot establir l'autenticitat de l'amfitrió {host}.\n" "\n" "L'empremta digital de la clau {keytype} és:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "L'empremta digital de la clau {keytype} és:" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Verifiqueu aquesta empremta digital! Voleu afegir-lo al fitxer " "'known_hosts'?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Exclou el patró" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Exclou el fitxer" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Exclou el directori" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Inclou un fitxer" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" és un enllaç simbòlic (symlink). L'objectiu enllaçat no es copiarà fins que no l'incloguin.\n" "Voleu incloure l'objectiu de l'enllaç simbòlic?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Inclou el directori" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Segur que voleu canviar la carpeta d'instantànies?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "No s'ha pogut crear una clau SSH nova a {path}" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" "S'ha desactivat perquè aquest patró no funciona en el mode \"SSH " "encriptat\"." #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "per defecte" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "desactivat" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "habilitat" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importa la configuració" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "No s'ha trobat cap configuració" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importar" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Seleccioneu la carpeta de snapshots des de la qual s'ha d'importar el fitxer" " de configuració. El path pot semblarse a: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Si la carpeta es troba en una unitat externa o remota, s'ha de muntar " "manualment prèviament." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opcions de comparació de les instantànies" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Ordre" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters:" msgstr "Paràmetres" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Utilitza %1 i %2 per els paràmetres de les rutes" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Definiu una diff command o premeu Cancel·lar." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "L'ordre \"{cmd}\" no es pot trobar en aquest sistema. Si us plau, proveu una" " altra cosa o premeu Cancel·la." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "No s'han establert paràmetres per a el command diff. S'utilitza el valor " "predeterminat \"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Només captures diferents" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Llista només les instantànies iguals a: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Comprovació a fons (més acurada, però lenta)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Suprimeix" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Selecciona-ho tot" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Compara" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Vés a" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opcions" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "No podeu compara una instantània amb sí mateixa." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Realment voleu suprimir {file} a la instantània {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Segur que voleu suprimir {file} a {count} instantànies?" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "Això no es pot revocar!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Exclou {path} de les instantànies futures?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "No es pot trobar el 'crontab'.\n" #~ "Segur que el 'cron' està instal·lat ?\n" #~ "Si no, hauríeu de desactivar totes les còpies de seguretat automàtiques." #~ msgid "Full snapshot path" #~ msgstr "Ruta completa de la instantània" #~ msgid "Mode" #~ msgstr "Mode" #~ msgid "Profile" #~ msgstr "Perfil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Perfil '{profile}': Introduïu la contrasenya per {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "El Shebang en l'script de user-callback no és executable." #~ msgid "WARNING" #~ msgstr "AVÍS" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs versió 1.7.2 i anterior té un error amb l'opció --reverse. Actualitzeu" #~ " encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "l'script de user-callback no té línia shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} utilitza EncFS per a l'encriptatge. Una auditoria de seguretat recent " #~ "va revelar diversos possibles vectors d'atac per això. Si us plau, mireu \"A" #~ " NOTE ON SECURITY\" a \"man backintime\"." backintime-1.5.2/common/po/cs.po000066400000000000000000001562001465446530500165200ustar00rootroot00000000000000# Czech 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \n" "Language-Team: Czech \n" "Language: cs\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>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Varování" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Hlavní profil" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "šifrováno" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "Na jiném stroji (skrze SSH tunel) – v šifrované podobě" #: common/config.py:309 msgid "Local" msgstr "Na tomto počítači" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Soukromou část SSH klíče" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "šifrováno" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Šifrování" #: common/config.py:320 msgid "SSH encrypted" msgstr "Na jiném stroji (skrze SSH tunel) – v šifrované podobě" #: common/config.py:327 msgid "Default" msgstr "Výchozí" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: „{name}“" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Složka zachycených stavů není platná!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Vyberte alespoň jednu složku, kterou zálohovat!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Složka pro uložení zálohy nemůže být její součástí (rekurze)." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Podsložka pro uložení zálohy nemůže být její součástí (rekurze)." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Neplatná volba. {path} není složka." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "" "Kolonka Stroj/Uživatel/Identifikátor-profilu nemůže zůstat nevyplněná." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nedaří se zapisovat do: {path}\n" "Máte oprávnění k zápisu tam?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Cílový souborový systém pro „{path}“ je typu FAT a ten nepodporuje pevné " "odkazy (hardlink). Použijte namísto něj nějaký běžný linuxový souborový " "systém." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Cílový souborový systém pro „{path}“ je SMB síťové sdílení. Ověřte, že tamní" " SMB server podporuje symbolické odkazy (symlink), případně v " "„{expertOptions}“ zapněte „{copyLinks}“." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopírovat až cíle odkazů (obejde symbolické odkazy)" #: common/config.py:497 msgid "Expert Options" msgstr "Pokročilé předvolby" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Cílový souborový systém pro {path} je sshfs sdílení. sshfs nepodporuje pevné" " odkazy (hardlink). Použijte namísto toho přímo „SSH“ režim." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Nepodařilo se zapsat novou úlohu (crontab)." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Nepodařilo se nainstalovat Udev pravidlo pro profil {profile_id}, protože " "systémová služba DBus „{dbus_interface}“ nebyla k dispozici" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Plán, používající správce zařízení udev, nefunguje v režimu {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Nedaří se najít univerzálně jedinečný identifkátor (UUID) pro {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Nastavení se nepodařilo uložit" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Nastavení se nepodařilo načíst" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil {name} už existuje." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Nelze odebrat poslední zbývající profil." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Nedaří se připojit „{command}“" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Nastavení pro šifrovanou složku nenalezeno." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Vytvořit novou šifrovanou složku?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Storno" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Potvrďte heslo." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Zadání hesla se neshodují." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Pořídit zachycený stav" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Nedaří se odpojit {mountprocess} z {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "{} nenalezeno. Nainstalujte např. {}" #: common/mount.py:720 #, fuzzy, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Přípojný bod {} není prázdný." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "NEZDAŘILO SE" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Obnovit oprávnění" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Dokončeno" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Zálohování odloženo po dobu napájení z akumulátoru" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Nedaří se nalézt složku se zachycenými stavy.\n" "Pokud se nachází na vyjímatelném zařízení, připojte ho." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Čekání %s sekundu." msgstr[1] "Čekání %s sekundy." msgstr[2] "Čekání %s sekund." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Nepodařilo se pořídit zachycený stav {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Dokončování" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Složku se nedaří vytvořit" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Ukládání souboru s nastaveními…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Ukládání oprávnění…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Nalezen pozůstatek „{snapshot_id}“ ve kterém lze pokračovat." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Odebírání složky „{snapshot_id}“, zanechané minulým průchodem" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Složku se nepodařilo odstranit" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Pořizování zachyceného stavu" #: common/snapshots.py:1417 msgid "Success" msgstr "Úspěch" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Přenos jen částečně úspěšný kvůli chybě" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Přenesena je část kvůli zmizelým zdrojovým souborům (viz „man rsync“)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "„rsync“ skončilo s návratovým kódem {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Podrobnosti získáte spuštěním „man rsync“" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Záporné návratovhé kódy z rsync jsou čísly signálů – viz příkazy „kill -l“ a" " „man kill“" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Nic se nezměnilo, nový zachycený stav není třeba" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Nepodařilo se přejmenovat {new_path} na {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Postupné odstranění" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Odstranění starých zachycených stavů" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Snaha o zachování nějakého volného místa" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Snaha o zachování alespoň {perc} volných i-uzlů" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Nyní" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Nedaří se připojit (mount) {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent nenalezeno. Ověřte, zda je nainstalované." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Nedaří se odemknout soukromou část ssh klíče. Heslo je buď chybně zadané " "nebo není k dispozici plánovači cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Použití šifry {cipher} se na stroji {host} nezdařilo." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Umístění na protistraně existuje, ale není to složka." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Do vzdáleného umístění nelze zapisovat." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "" "Umístění na protistraně nemá nastaveno oprávnění ke spouštění (tj. nelze do " "něho vstoupit)." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Umístění na protistraně se nedaří vytvořit." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Protějšek, stroj {host} nepodporuje {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Další pokyny naleznete v manuálové stránce aplikace (man backintime)" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Kontrolní příkazy na stroji {host} vrátily neznámou chybu" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" "Na protějšku, stroji {host} nejsou podporovány pevné odkazy (hardlink)" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" "Zkopírovat veřejnou část ssh klíče „{pubkey}“ na stroj „{host}“ na " "protistraně" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Vyplňte heslo pro „{user}“" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "O aplikaci" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Vývojáři" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Překladatelé" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licence" #: qt/app.py:169 msgid "Shortcuts" msgstr "Klávesové zkratky" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Tato složka v nyní označeném\n" "zachyceném stavu neexistuje." #: qt/app.py:256 msgid "Add to Include" msgstr "Přidat do seznamu zálohovaných" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Přidat do seznamu vynechaných" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Složku se nedaří vytvořit" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Složka se zachycenými stavy nebyla nalezena.\n" "Pokud se nachází na vyjímatelném médiu, připojte ho a klikněte na OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Pořídit zachycený stav" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Pro zjišťování změny souborů používat časy jejich úprav a velikost." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Pořídit zachycený stav (režim s kontrolními součty)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Zjišťovat změny v souborech pomocí jejich kontrolních součtů." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pozastavit pořizování zachyceného stavu" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Pokračovat v pořizování zachyceného stavu" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Ukončit toto pořizování zachyceného stavu" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Znovu načíst seznam zachycených stavů" #: qt/app.py:508 msgid "Name snapshot" msgstr "Nazvat zachycený stav" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Odstranit zachycený stav" #: qt/app.py:516 msgid "View snapshot log" msgstr "Zobrazit záznam událostí při pořizování zachyceného stavu" #: qt/app.py:520 msgid "View last log" msgstr "Zobrazit záznam nedávných událostí" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Spravovat profily…" #: qt/app.py:528 msgid "Shutdown" msgstr "Vypnout počítač po dokončení úlohy" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Po pořízení zachyceného stavu vypnout počítač." #: qt/app.py:532 msgid "Setup language…" msgstr "Nastavit jazyk…" #: qt/app.py:536 msgid "Exit" msgstr "Ukončit aplikaci" #: qt/app.py:540 msgid "Help" msgstr "Nápověda" #: qt/app.py:544 msgid "Profiles config file" msgstr "Soubor s nastaveními profilů" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Webové stránky projektu" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Co je v aplikaci nového" #: qt/app.py:553 msgid "FAQ" msgstr "Často kladené dotazy (FAQ)" #: qt/app.py:556 msgid "Ask a question" msgstr "Položit dotaz" #: qt/app.py:559 msgid "Report a bug" msgstr "Nahlásit chybu / dát podnět" #: qt/app.py:562 msgid "Translation" msgstr "Překlad" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Obnovit" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Obnovit označené soubory či složky do jejich původních umístění." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Obnovit do…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Obnovit označené soubory či složky do nového umístění." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Obnovit právě zobrazenou složku a veškerý její obsah do původního umístění." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Obnovit právě zobrazenou složku a veškerý její obsah do nového umístění." #: qt/app.py:595 msgid "Up" msgstr "Přejít do nadřazené složky" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Zobrazovat skryté soubory" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Porovnat zachycené stavy…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "&Záloha" #: qt/app.py:676 msgid "&Restore" msgstr "&Obnovit" #: qt/app.py:682 msgid "&Help" msgstr "&Nápověda" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Pokud toto okno zavřete, pak aplikace Back In Time nebude moci po dokončení pořízení zachyceného stavu vypnout počítač.\n" "Opravdu chcete okno zavřít?" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Opravdu chcete tento prvek obnovit?" #: qt/app.py:987 msgid "Working:" msgstr "Zpracovává se:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Dokončeno, záloha není potřebná" #: qt/app.py:1044 msgid "Working" msgstr "Zpracovává se" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Chyba" #: qt/app.py:1076 msgid "Sent" msgstr "Odesláno" #: qt/app.py:1077 msgid "Speed" msgstr "Rychlost" #: qt/app.py:1078 msgid "ETA" msgstr "Odhad. zbývající čas" #: qt/app.py:1140 msgid "Global" msgstr "Celkové" #: qt/app.py:1141 msgid "Root" msgstr "Kořen" #: qt/app.py:1142 msgid "Home" msgstr "Domovská složka" #: qt/app.py:1170 msgid "Backup folders" msgstr "Složky pro zálohu" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nazvat zachycený stav" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Opravdu chcete odstranit zachycený stav?" msgstr[1] "Opravdu chcete odstranit zachycené stavy?" msgstr[2] "Opravdu chcete odstranit zachycené stavy?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Stávající soubory, které by jinak byly odebrány nebo přepsány těmi ze zálohy,\n" "zachovat a jen připojit za jejich název „{suffix}“." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Novější verze souborů budou před zahájením obnovy přejmenovány přidáním předpony {suffix}.\n" "Až je už nebudete potřebovat, můžete je odstranit pomocí příkazu {cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Obnovit pouze soubory které neexistují\n" "nebo jsou novější, než ty v cíli.\n" "Využívá volbu „rsync --update“." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Odebrat novější prvky v původní složce." #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Obnovit označené soubory nebo složky do původního umístění a\n" "smazat soubory/složky, které nejsou obsažené v zachyceném stavu.\n" "Toto smaže soubor/složky, které byly vynechány během pořizování\n" "zachyceného stavu." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Opravdu chcete obnovit tento soubor\n" "do nové složky „{path}“?" msgstr[1] "" "Opravdu chcete obnovit tyto soubory\n" "do nové složky „{path}“?" msgstr[2] "" "Opravdu chcete obnovit tyto soubory\n" "do nové složky „{path}“?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Opravdu chcete tento prvek obnovit?" msgstr[1] "Opravdu chcete tyto prvky obnovit?" msgstr[2] "Opravdu chcete tyto prvky obnovit?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Opravdu chcete veškeré novější soubory v „{path}“ odebrat?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "Opravdu chcete z původní složky odebrat veškeré novější soubory?" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "VAROVÁNÍ: smazání souborů v kořenové složce souborového systému může " "způsobit nefunkčnost celého operačního systému!!!" #: qt/app.py:1750 msgid "Snapshot" msgstr "Zachycený stav" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Obnovit {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Obnovit {path} do…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "Nastavení jazyka se projeví až po příštím spuštění Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Jazyk nastavování" #: qt/languagedialog.py:92 msgid "System default" msgstr "Systémové výchozí" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Použít jazyk operačního systému." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Přeloženo: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Dobrý den,\n" "již několikrát jste použili Back In Time v jazyce {language}.\n" "Překlad vámi nainstalované verze Back In Time do {language} je na {perc} dokončen. Bez ohledu na úroveň vašich technických znalostí můžete přispět k překladu a tím i k samotnému programu Back In Time.\n" "Pokud chcete přispět, navštivte prosím {translation_platform_url}. Pro další pomoc a dotazy navštivte prosím {back_in_time_project_website}.\n" "Omlouváme se za vyrušení a tato zpráva se již nebude zobrazovat. Tento dialog je kdykoli k dispozici prostřednictvím nabídky nápovědy.\n" "Váš tým Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "překladatelská platforma" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Váš překlad" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Zobrazení nedávných událostí" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Zobrazení událostí při pořizování zachyceného stavu" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 #, fuzzy msgid "Snapshots:" msgstr "Zachycené stavy" #: qt/logviewdialog.py:99 #, fuzzy msgid "Filter:" msgstr "Filtr" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Vše" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Změny" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Chyby" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Informace" msgstr[1] "Informace" msgstr[2] "Informace" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "nezdary rsync přenosů (experimentální)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Chyba (Error), [I] Informace, [C] Změna (Change)" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "dekódovat popisy umístění" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Dotaz" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: „{profile_name}“" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Zobrazit záznam nedávných událostí" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Spustit {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Zpracování…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Odesláno:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Rychlost:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Zachycené stavy" #: qt/qttools.py:427 msgid "Today" msgstr "Dnes" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Včera" #: qt/qttools.py:443 msgid "This week" msgstr "Tento týden" #: qt/qttools.py:450 msgid "Last week" msgstr "Minulý týden" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Toto NENÍ zachycený stav, ale pohled přímo do stávajících souborů na tomto " "počítači" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Minulá kontrola {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Zobrazit kompletní záznam událostí" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Stroj:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Uživatel:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Spravovat profily" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Přejmenovat" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Přidat" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Odebrat" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Obecné" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Ukládání záloh:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Kam ukládat zachycené stavy" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Složka" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Nastavení pro SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Umístění:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Šifra:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Soukromá část klíče:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Zvolte existující soubor se soukromým klíčem (obvykle nazvaný „id_rsa“)" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Vytvořit nový SSH klíč bez hesla (není možné pokud je už vybrán soubor se " "soukromým klíčem)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Heslo pro" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Uložit heslo do klíčenky desktopového prostředí" #: qt/settingsdialog.py:378 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/settingsdialog.py:390 msgid "Advanced" msgstr "Pokročilé" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Úplný popis umístění zachyceného stavu:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Automatické zálohování" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Vypnuto" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Při každém startu/restartu počítače" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Každou {n} minutu" msgstr[1] "Každé {n} minuty" msgstr[2] "Každých {n} minut" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Každou hodinu" msgstr[1] "Každou hodinu" msgstr[2] "Každou hodinu" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Každou {n} hodinu" msgstr[1] "Každé {n} hodiny" msgstr[2] "Každých {n} hodin" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Uživatelsky určený interval" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Každý den" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Opakovaně (jako anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Při připojení (flash)disku (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Každý týden" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Každý měsíc" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Každý rok" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Den v týdnu:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "V hodinu:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "V hodiny:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Spouštět aplikaci Back In Time opakovaně. Užitečné pokud počítač není " "zapínán pravidelně." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Každých:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Hodin" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dnů" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Týdnů" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Měsíců" #: qt/settingsdialog.py:555 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 "" "Spouštět aplikaci Back In Time na základě připojení určené jednotky datového úložiště k počítači (s tím, že četnost záloh se neřídí těmito odpo/připojeními, ale nastaveným intervalem mezi zálohami).\n" "Pro účely zapsání tohoto nastavení budete vyzváni k zadání hesla k uživatelskému účtu, který má práva pro správu systému (sudo)." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Zahrnout" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Zahrnout soubory a složky" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Přidat soubor" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Přidat složku" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Vynechat" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Vynechat zástupně vyjádřené, soubory a složky" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Přidat výchozí" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Vynechat soubory větší než:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Vynechat soubory větší než: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Vynechat soubory větší, než zadaná hodnota v %(prefix)s.\n" "V případě, že není používán „Úplný rsync režim“, bude se toto týkat pouze nových\n" "souborů, protože pro nástroj rsync je toto předvolba přenosu, nikoli vynechání.\n" "Takže, velké soubory, které byly zazálohovány dříve, v zachycených stavech\n" "už zůstanou, i když se ve stávajícím stavu změní." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Automatické odstranění" #: qt/settingsdialog.py:726 #, fuzzy msgid "Older than:" msgstr "Starší než" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Let" #: qt/settingsdialog.py:748 #, fuzzy msgid "If free space is less than:" msgstr "Pokud zbývá volného místa méně než" #: qt/settingsdialog.py:768 #, fuzzy msgid "If free inodes is 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/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Postupné odstraňování:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Na protistraně spustit na pozadí." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTÁLNÍ" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Ponechávat všechny zachycené stavy za uplynulé" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dnů." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Ponechávat jeden zachycený stav za den z uplynulých" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Ponechávat jeden zachycený stav za týden za uplynulých" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "týdnů." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Ponechávat jeden zachycený stav za měsíc za uplynulých" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "měsíců." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Ponechat jeden zachycený stav co rok za všechna leta." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Neodstraňovat ty zachycené stavy, které uživatel nějak nazval." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "Př&edvolby" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "" "Zobrazovat oznámení (prostřednictvím centra oznámení desktopového prostředí)" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Vypnout pořizování zachycených stavů při provozu na akumulátor" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Údaje o stavu napájení nejsou dispozici" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Pořizovat pouze jeden zachycený stav naráz" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Pořizování ostatních zachycených stavů bude blokováno do dokončení toho právě probíhajícího.\n" "Toto je všeobecná předvolba, takže se bude týkat všech profilů tohoto uživatele.\n" "Zapíná se pro každého uživatele zvlášť." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Zazálohovat soubory, které by byly při procesu obnovování nahrazeny" #: qt/settingsdialog.py:879 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Novější verze souborů budou před zahájením obnovy přejmenovány přidáním předpony {suffix}.\n" "Až je už nebudete potřebovat, můžete je odstranit pomocí příkazu {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" "Pokračovat i v případě výskytu chyb (zachovat i neúplné zachycené stavy)" #: qt/settingsdialog.py:895 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:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Pořídit nový zachycený stav i v případě, že nedošlo k žádným změnám." #: qt/settingsdialog.py:906 #, fuzzy msgid "Log Level:" msgstr "Podrobnost zaznamenávání událostí (log)" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nic" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "&Pokročilé předvolby" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Upozornění: Tyto předvolby měňte pouze když opravdu víte, co děláte." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Spustit nástroj rsync s parametrem '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "když jako naplánovaná úloha (cron)" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "při zálohování na jiný stroj" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "při ručně spuštěném pořízení zachyceného stavu" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(pokud chcete zapnout tuto možnost, nainstalujte „nocache“)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "při ukládání záloh přímo na zálohovaném počítači" #: qt/settingsdialog.py:1000 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:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 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)." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "Omezit využití přenosové kapacity, zabrané nástrojem rsync, na" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Zálohovat včetně seznamů řízení přístupu (ACL)" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Zálohovat včetně rozšířených atributů (xattr)" #: qt/settingsdialog.py:1112 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/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, 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:1171 msgid "Paste additional options to rsync" msgstr "Dodatečné parametry, se kterými spouštět nástroj rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Před příkazy přes SSH přidávat" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Čí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:1196 msgid "default" msgstr "výchozí" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Zkontrolovat zda je stroj na protistraně dostupný na síti" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "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." #: qt/settingsdialog.py:1218 #, fuzzy msgid "Check if remote host supports all necessary commands." msgstr "Ověřit zda stroj na protistraně podporuje všechny nezbytné příkazy" #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "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." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Obnovit nastavení" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Upravit uživatelsky definované akce" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nový profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Přejmenovat profil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Opravdu chcete profil „{name}“ smazat?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Důrazně doporučeno" #: qt/settingsdialog.py:1634 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 "" "Uživatelsky určené doby mohou být pouze čárkou oddělovaný seznam hodin " "(například 8,12,18,23) nebo (např.) */3 pro pravidelné zálohy každé 3 " "hodiny." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Nezvolili jste soubor se soukromou částí klíče pro SSH.\n" "Přejete si vytvořit novou dvojici veřejná/soukromá část (přístupná bez hesla)?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Soubor „{file}“ se soukromou částí klíče neexistuje." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Přejete si zkopírovat veřejnou část vašeho SSH klíče\n" "na stroj na protějšku a umožnit tak přihlašování bez hesla?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "Nepodvrženost stroje „{host}“ se nepodařilo ověřit.\n" "\n" "{keytype} otisk klíče je:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Zkontrolujte tento otisk! Přejete si ho přidat do souboru „known_hosts“?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Zástupné vyjádření vynechaného" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Vynechat soubor" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Vynechat složku" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Zahrnout soubor" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "„{path}“ je symbolický odkaz. Odkazovaný cíl nebude zazálohován, dokud jeho umístění výslovně nezahrnete.\n" "Zahrnout tedy cíl symbolického odkazu namísto odkazu samotného?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Zahrnout složku" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Opravdu změnit složku se zachycenými stavy?" #: qt/settingsdialog.py:2194 #, fuzzy, 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/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "výchozí" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "vypnuto" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "zapnuto" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Nenalezena žádná nastavení" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Předvolby k porovnávání zachycených stavů" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Příkaz" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters:" msgstr "Parametry" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Parametry popisů umístění vyjádřete pomocí %1 a %2" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Pouze odlišující se zachycené stavy" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Vypsat pouze zachycené stavy, shodné s: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Hloubková kontrola (přesnější, ale pomalejší)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Smazat" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Vybrat vše" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Porovnat" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Přejít na" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Předvolby" #: qt/snapshotsdialog.py:355 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:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Opravdu chcete „{file}“ vymazat ze zachyceného stavu „{snapshot_id}“?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Opravdu chcete „{file}“ vymazat z(e) {count} zachycených stavů?" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "Toto nelze vzít zpět!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Vynechat {path} z budoucích zachycených stavů?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Nedaří se nalézt soubor s naplánovanými úlohami (crontab).\n" #~ "Opravdu máte nainstalovaný plánovač cron?\n" #~ "V případě že ne, měli byste vypnout veškeré automatizované zálohování." #~ msgid "Full snapshot path" #~ msgstr "Úplný popis umístění zachyceného stavu" #~ msgid "Mode" #~ msgstr "Ukládání záloh" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil „{profile}“: Zadejte heslo pro {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "" #~ "Interpret ve skriptu uživatelsky definovaných akcí nemá nastavený příznak " #~ "spustitelnosti." #~ msgid "WARNING" #~ msgstr "VAROVÁNÍ" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs verze 1.7.2 a dřívější má chybu ve volbě --reverse. Zaktualizujte ho." #~ 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)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} šifruje pomocí EncFS. U toho však nedávná bezpečnostní prověrka " #~ "odhalila několik závažných nedostatků. Přečtěte si proto odstavec „POZNÁMKA " #~ "K ZABEZPEČENÍ“ v manuálové stránce backintime (man backintime)." backintime-1.5.2/common/po/da.po000066400000000000000000001532611465446530500165030ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 07:07+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Advarsel" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "hovedprofil" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Lokal (EncFS krypteret)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (EncFS krypteret)" #: common/config.py:309 msgid "Local" msgstr "Lokal" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH privat nøgle" #: common/config.py:314 msgid "Local encrypted" msgstr "Lokalt krypteret" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Kryptering" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH krypteret" #: common/config.py:327 msgid "Default" msgstr "Standard" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Snapshots mappe er ugyldig!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Du skal vælge mindst én mappe, der skal sikkerhedskopieres!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Backup-mappe kan ikke inkluderes." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Backup-undermappe kan ikke inkluderes." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ugyldigt valg. {path} er ikke en mappe." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Maskine/Bruger/Profil-ID kan ikke være tom." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Kan ikke skrive til: {path}\n" "Er du sikker på, du har skrive-rettigheder?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Destinationsfilsystem for {path} er formatteret med FAT, som ikke kan " "håndtere hard-links. Brug venligst et understøttet Linux-filsystem." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. 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/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopier links (følg symbolske links)" #: common/config.py:497 msgid "Expert Options" msgstr "Avancerede indstillinger" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. 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/config.py:1658 msgid "Failed to write new crontab." msgstr "Kunne ikke skrive ny crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." 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" " kommandoen “systemctl enable cron” eller kontakt supportkanalerne for din " "GNU Linux-distribution." #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Kunne ikke installere Udev regel for profil {profile_id}. DBus Service " "'{dbus_interface}' var ikke tilgængelig" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Metoden udev virker ikke med indstilling {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Fandt ikke UUID for {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Kunne ikke gemme konfiguration" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Kunne ikke læse konfiguration" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" findes allerede." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Den sidste profil kan ikke slettes." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Kan ikke tilslutte '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Konfig for krypteret mappe ikke fundet." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Lav en ny krypteret mappe?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Annullér" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Bekræft venligst adgangskode." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Kodeord passer ikke." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Tag et tilstands-billede" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan ikke fjerne {mountprocess} fra {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} ikke fundet. Installér venligst (f.eks. med \"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Tilsluttelsespunkt {mntpoint} er ikke tomt." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Indtast adgangskode for {mode} profil \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "MISLYKKEDES" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Gendan tilladelser" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Færdig" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Venter med backup under batteridrift" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Kan ikke finde tilstands-billede-mappe.\n" "Hvis den er på et flytbart drev indsæt venligst dette." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Venter %s sekund." msgstr[1] "Venter %s sekunder." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Kunne ikke tage tilstands-billede {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Afslutter" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Kan ikke oprette mappe" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Gemmer konfigurationsfil…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Gemmer tilladelser…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Fandt efterladt {snapshot_id} som kan fortsættes med." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Sletter efterladt {snapshot_id}-mappe fra seneste kørsel" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Kan ikke fjerne mappe" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Tag et tilstands-billede" #: common/snapshots.py:1417 msgid "Success" msgstr "Succes" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Delvist overført på grund af fejl" #: common/snapshots.py:1421 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:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' afsluttede med slutværdien {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Kig i 'man rsync' for flere detaljer" #: common/snapshots.py:1445 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:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Ingen ændringer, så et nyt tilstands-billede er ikke nødvendigt" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Kan ikke omdøbe {new_path} til {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Smart sletning" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Fjern gamle tilstands-billeder" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Prøv at holde minimum fri plads" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Prøver at holde mindst {perc} inoder fri" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Nu" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan ikke tilslutte {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Kan ikke finde ssh-agent. Sørg venligst for at den er installeret." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Kunne ikke fjerne låsen på ssh privat nøgle. Forkert kodeord elle kodeordet " "er ikke tilgængelig for cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Chiffer {cipher} fungerede ikke for {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Stien findes i den anden ende, men er ikke en mappe." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Stien i den anden ende er ikke skrivbar." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Stien i den anden ende er ikke eksekverbar." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Kan ikke oprette fjernmappe." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Den anden maskine {host} understøtter ikke {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Se 'man backintime' for yderligere instruktioner" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Check-kommandoerne på vært {host} gav en ukendt fejl" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Den anden maskine {host} understøtter ikke hardlinks" #: common/sshtools.py:1164 #, 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:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Indtast venligst et kodeord for \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Om" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Forfattere" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Oversættelser" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licens" #: qt/app.py:169 msgid "Shortcuts" msgstr "Genveje" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Denne mappe findes ikke\n" "i det valgte tilstands-billede." #: qt/app.py:256 msgid "Add to Include" msgstr "Tilføj til Inkludér" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Tilføj til Spring over" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} synes at køre for den første gang, da ingen konfiguration blev " "fundet." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Importer en eksisterende konfiguration (fra en backup destitationsmappe " "eller anden computer)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Kan ikke finde tilstandsbillede-mappen." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "Hvis den er på et flytbar drev, indsæt venligst drevet og tryk OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Tag et tilstands-billede" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Brug ændringstid og størrelse til at opdage filændringer." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Opret et tilstands-billede (checksum modus)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Brug checksummer til at opdage filændringer." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Sæt tilstands-afbildning på pause" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Genoptag tilstands-afbildning" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Stop tilstands-afbildning" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Opdatér tilstands-billede-liste" #: qt/app.py:508 msgid "Name snapshot" msgstr "Navngiv tilstands-billede" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Fjern tilstands-billede" #: qt/app.py:516 msgid "View snapshot log" msgstr "Vis tilstands-billede-log" #: qt/app.py:520 msgid "View last log" msgstr "Vis seneste log" #: qt/app.py:524 msgid "Manage profiles…" msgstr "hovedprofil…" #: qt/app.py:528 msgid "Shutdown" msgstr "Luk ned" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Luk ned når tilstands-billedet er færdiggjort." #: qt/app.py:532 msgid "Setup language…" msgstr "Sæt sprog op…" #: qt/app.py:536 msgid "Exit" msgstr "Afslut" #: qt/app.py:540 msgid "Help" msgstr "Hjælp" #: qt/app.py:544 msgid "Profiles config file" msgstr "Profilkonfigurationsfil" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Websted" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Ændringslog" #: qt/app.py:553 msgid "FAQ" msgstr "OSS" #: qt/app.py:556 msgid "Ask a question" msgstr "Stil et spørgsmål" #: qt/app.py:559 msgid "Report a bug" msgstr "Rapporter en fejl" #: qt/app.py:562 msgid "Translation" msgstr "Oversættelse" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Vis beskeden om deltagelse i oversættelse igen." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Krypteringsovergang (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Vis besked fjernelse af EncFS igen." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Gendan" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Gendan de valgte filer eller mapper på det oprindelige sted." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Gendan …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Gendan de valgte filer eller mapper på et nyt sted." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "Gendan den viste mappe og al dens indhold på det oprindelige sted." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Gendan den viste mappe og al dens indhold på et nyt sted." #: qt/app.py:595 msgid "Up" msgstr "Op" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Vis skjulte filer" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Tag et tilstands-billede…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Sikkerhedskopiér" #: qt/app.py:676 msgid "&Restore" msgstr "&Gendan" #: qt/app.py:682 msgid "&Help" msgstr "&Hjælp" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Hvis du lukker dette vindue så kan Back In Time ikke lukke din maskine ned " "når tilstands-billedet er færdigt." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Er du sikker på at du ønsker at lukke det?" #: qt/app.py:987 msgid "Working:" msgstr "Arbejder:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Færdig, ingen sikkerhedskopiering nødvendig" #: qt/app.py:1044 msgid "Working" msgstr "Arbejder" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Fejl" #: qt/app.py:1076 msgid "Sent" msgstr "Sendt" #: qt/app.py:1077 msgid "Speed" msgstr "Hastighed" #: qt/app.py:1078 msgid "ETA" msgstr "Forventet afslutning" #: qt/app.py:1140 msgid "Global" msgstr "Globalt" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Hjemmemappe" #: qt/app.py:1170 msgid "Backup folders" msgstr "Sikkerhedskopi-mapper" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Tilstands-billede-navn" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Er du sikker på du ønsker at fjerne tilstands-billedet?" msgstr[1] "Er du sikker på du ønsker at fjerne tilstands-billederne?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Opret sikkerhedskopier med {suffix} tilføjet\n" "før lokale elementer overskrives." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them 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/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Gendan alene elementer som ikke findes eller\n" "som er nyere end dem der findes.\n" "Bruger \"rsync --update\" parameteren." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Fjern nyere elementer i oprindelsesmappen." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Gendan valgte filer eller mapper på den oprindelige plads og slet filer " "eller mapper som ikke er i tilstands-billedet. Vær ekstremt forsigtig for " "dette vil slette filer og mapper som blev sprunget over da tilstands-" "billedet blev dannet." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Er du sikker på at du vil gendanne dette element i den nye mappe\n" "{path}?" msgstr[1] "" "Er du sikker på at du vil gendanne disse elementer i den nye mappe\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to 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/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Er du sikker på du vil slette alle nyere filer i {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Er du sikker på du vil slette alle nyere filer i din oprindelige mappe?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}ADVARSEL{BOLDEND}: Hvis du sletter filer i filsystemets rod risikerer " "du at ødelægge hele dit system." #: qt/app.py:1750 msgid "Snapshot" msgstr "Tilstands-billede" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Gendan {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Gendan {path} …" #: qt/app.py:1948 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/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Understøttelse af EncFS vil blive fjernet i nær fremtid. Det er ikke længere" " anbefalet at bruge den tilstand i en profil." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "En beslutning om erstatning for fortsat support af krypteret " "sikkerhedskopier afventer stadig, afhængig af projektressourcer og " "tilgængelighed af bidragsydere. Flere detaljer er tilgængelig i dette " "{whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "whitepaper" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Understøttelsen for krypteret snapshot-profiler er ved at undergå betydelige" " forandringer, og EncFS vil blive fjernet i overskuelig fremtid." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Følgende profil(er) bruger kryptering med EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "En beslutning om erstatning for fortsat support af krypteret " "sikkerhedskopier afventer stadig, afhængig af projektressourcer og " "tilgængelighed af bidragsydere. Brugere are velkommen til at være del af " "diskussionen. Opdateret detaljer om de næste skridt er tilgængelig i dette " "{whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Denne besked vil ikke blive vist igen. Dialogboksen er tilgænglig på ethvert" " tidspunkt via hjælp-menuen." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Dit Back In Time Hold" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Sæt sprog op" #: qt/languagedialog.py:92 msgid "System default" msgstr "Systemstandard" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Brug styresystemets sprog." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Oversat: {percent}" #: qt/languagedialog.py:188 #, 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 "" "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/languagedialog.py:217 msgid "translation platform" msgstr "oversættelses platform" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Din oversættelse" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Se Sidste Log" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Se Snapshot Log" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Tilstands-billeder:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Alles" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Ændringer" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Fejl" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Information" msgstr[1] "Informationer" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync overførelsesfejl (eksperimentalt)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Fejl, [I] Information, [C] Ændre" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "afkod paths" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Spørgsmål" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Se Sidste Log" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Kør {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Arbejder…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Sendt:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Hastighed:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Tilstands-billeder" #: qt/qttools.py:427 msgid "Today" msgstr "I dag" #: qt/qttools.py:434 msgid "Yesterday" msgstr "I går" #: qt/qttools.py:443 msgid "This week" msgstr "Denne uge" #: qt/qttools.py:450 msgid "Last week" msgstr "Sidste uge" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Dette er IKKE et snapshot men et nutidigt syn af dine lokale filer" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Sidste check {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Vis Fulde Log" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH Proxy" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Vært:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Bruger:" #: qt/settingsdialog.py:120 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 "" "Forbind til destinationsvært via denne proxy (også kendt som en jump host). " "Se \"-J\" i \"ssh\"-kommandoens dokumentation eller \"ProxyJump\" i " "\"ssh_config\" man side for detaljer." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Håndtér profiler" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Redigér" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Tilføj" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Fjern" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Generelt" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Tilstand:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Hvor skal tilstands-billeder gemmes" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Folder" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH Indstillinger" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Sti:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Kryptering:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Privat SSH nøgle:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Vælg en eksisterende private key fil (normalt navngivet \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Opret en ny SSH-nøgle uden kodeord (ikke tilladt hvis en privat nøgle-fil " "allerede er valgt)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Kodeord" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Gem kodeord i nøglering" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "Husk kodeord for cron (sikkerhedsproblem: root kan læse kodeordet)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Avanceret" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Fuld sti til tilstands-billeder:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Planlæg" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Deaktiveret" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Ved hver opstart/genstart" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Hver {n} minut" msgstr[1] "Hver {n} minutter" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Hver time" msgstr[1] "Hver {n} timer" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Hver {n} time" msgstr[1] "Hver {n} timer" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Angiv interval" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Hver dag" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Gentag (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Når drev bliver tilsluttet (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Hver uge" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Hver måned" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Hvert år" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Dag:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Ugedag:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Time:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Timer:" #: qt/settingsdialog.py:521 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:528 msgid "Every:" msgstr "Hver:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Time(r)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dag(e)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Uge(r)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Måned(er)" #: qt/settingsdialog.py:555 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 "" "Kør Back In Time så snart drevet tilsluttes (kun en gang hver X'te dag).\n" "Du bliver spurgt om dit sudo kodeord." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Aktiver logning af debug beskeder" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "Skriver debugniveau-beskeder ind i system log via \"--debug\"." #: qt/settingsdialog.py:570 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:583 msgid "&Include" msgstr "&Inkludér" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Inkludér filer og mapper" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Tilføj fil" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Tilføj mappe" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Ekskludér" #: qt/settingsdialog.py:625 #, 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 krypteret' tilstand, er kun enkelt eller dobbelt" " stjerne funktionelt (f.eks. {example2}). Andre type wildcards eller mønstre" " vil blive ignoreret (f.eks. {example1}). Filnavne er uforudseelige i denne " "tilstand pga. kryptering med EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Ekskludéringmønstre, filer eller mapper" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Tilføj standard" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Ekskludér filer større end:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Ekskludér filer større end værdi i {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Med 'Full rsync mode' slået fra påvirker dette kun nye filer da dette er en " "overførselsesparameter for rsync, ikke en ekskluderingsparameter. Så store " "filer der tidligere er overført forbliver i tilstands-billederne selv hvis " "de er ændret." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Auto-fjern" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Ældre end:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "År" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Hvis fri plads er mindre end:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Hvis frie inodes er mindre end:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Smart fjern:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Kør i bagrunden på fjernværten." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTEL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Behold alle tilstands-billeder for de seneste" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Dag(e)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Behold et tilstands-billede per dag for de seneste" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Behold et tilstands-billede per uge for de seneste" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Uge(r)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Behold et tilstands-billede per måned for de seneste" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "måned(er)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Behold et tilstands-billede per år for alle år." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Fjern ikke navngivne tilstands-billeder." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "I&ndstillinger" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Aktivér bekendtgørelser" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "De-aktivér tilstands-billeder ved batteri-drift" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Strømstatus ikke tilgængelig fra system" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Dan kun et tilstands-billede af gangen" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Andre tilstands-billeder vil være blokeret indtil det nuværende er " "færdiggjort. Dette er en global indstilling. Så det påvirker alle profiler " "for denne bruger. Men du bliver nød til at aktivere det for alle andre " "brugere også." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Sikkerhedskopiering erstattede filer ved genddannelse" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" 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 " "{cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Fortsæt ved fejl (behold ufuldstændige tilstands-billeder)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Brug checksummer til at finde ændringer" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Dan et nyt tilstands-billede uanset om der var ændringer eller ej." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Logniveau:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Ingen" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "A&vancerede indstillinger" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Advarsel: Ændr kun på disse muligheder hvis du virkelig ved hvad du gør." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Kør 'rsync' med '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "som et cronjob" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "på en fjernvært" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "når et manuelt tilstands-billede dannes" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Installér venlist 'nocache' for at kunne vælge denne mulighed)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "på lokal maskine" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Send stdout til /dev/null i cronjobs." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Cron vil automatisk sende en email med vedhæftet output fra cronjobs this " "der er en MTA installeret." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Send stderr til /dev/null i cronjobs." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Cron vil automatisk sende en email med vedhæftet fejl af cronjobs hvis en " "MTA er installeret." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Begræns rsync's båndbreddeforbrug:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Behold ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Behold udvidede attributter (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopier usikre links (virker kun med absolutte links)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Begræns til ét filsystem" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Værdier skal i citationstegn, for eksempel {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Indsæt yderligere parametre til rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Foranstil alle SSH kommandoer" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Præfiks til at køre før hver kommando på fjernvært." #: qt/settingsdialog.py:1188 #, 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 "" "Variabler skal escapes med \\$FOO. Dette vil ikke berøre rsync. For at " "tilføje præfiks til rsync, brug \"{example_value}\" med " "{rsync_options_value}." #: qt/settingsdialog.py:1196 msgid "default" msgstr "standard" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Undersøg om fjernvært er på nettet" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Advarsel: hvis dette er slået fra og fjernværten ikke er tilgængelig, " "risikerer du at få nogle besynderlige fejl." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Undersøg om fjernværten understøtter alle nødvendige kommandoer." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Advarsel: hvis dette er slået fra og fjernværten ikke understøtter alle " "nødvendige kommandoer risikerer du at få nogle besynderlige fejl." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Gendan opsætning" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Redigér bruger-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "Understøttelse af EncFS vil ophøre i overskuelig fremtid. En beslutning om " "erstatning for fortsat understøttelse af krypteret sikkerhedskopier afventer" " stadig, afhængig af projektressourcer og tilgængelighed af bidragsydere. " "Flere detaljer er tilgængelig i dette {whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Ny profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Omdøb profil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Er du sikker på at du vil slette profilen \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Kraftigt anbefalet{ENDBOLD}: (Alle anbefalinger er allerede " "inkluderet.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Kraftigt anbefalet{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "Du har ikke valgt en privat nøglefil til SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "Vil du danne et nyt offentligt/private kodeordsløst nøglepar?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Den private nøglefil \"{file}\" mangler." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Vil du kopiere din offentlige SSH-nøgle til fjernværten for at gøre login " "uden kodeord mulig?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Autenticiteten af værten {host} kan ikke etableres." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} nøgle fingeraftryk er:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Verificér venligst dette fingeraftryk. Vil du tilføje det til din " "'known_hosts' fil?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Ekskludér mønster" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Ekskludér fil" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Ekskludér mappe" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Inkludér fil" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" er et symbolsk link. Det linket peger på vil ikke blive sikkerhedskopieret med mindre du også inkluderer den sti.\n" "Vil du inkludere det som linket peger på i stedet?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Inkludér mapper" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Er du sikker på du ønsker at skifte tilstands-billede-mappe ?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Oprettelsen af ny SSH nøgle i {path} mislykkedes." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" "Deaktiver fordi dette mønster ikke fungerer i tilstand 'SSH krypteret'." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(standard: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "slået fra" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "slået til" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importeer konfiguration" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Ingen opsætning fundet" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Import" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Vælg snapshot-mappen hvorfra konfigurationsfilen skal importeres fra. Stien " "kan se sådan ud: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Hvis mappen er placeret på en eksternt eller flytbart drev, skal det først " "monteres manuelt." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Valgmuligheder om sammenligning af tilstands-billeder" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Kommando:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametre:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Brug %1 og %2 som sti-parametre" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Venligst sæt en diff-kommando eller tryk Annuller." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "Kommandoen \"{cmd}\" blev ikke fundet på dette system. Venligst prøv noget " "andet eller tryk Annuller." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Ingen parametre sat for diff-kommandoen. Bruger standardværdi \"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Kun tilstands-billeder der er forskellige" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Vis kun tilstands-billeder der er ens med:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Grundig sammenligning (mere præcis, men langsom)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Slet" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Vælg alle" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Sammenlign" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Gå til" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Indstillinger" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Du kan ikke sammenligne et tilstands-billede med sig selv." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Er du sikker på at du vil slette {file} i tilstands-billedet {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Vil du virkelig slette {file} i {count} tilstands-billeder?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "ADVARSEL: Dette kan ikke tilbagekaldes." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Ekskludér {path} fra tilstands-billeder fremover?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? 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." #~ msgid "Full snapshot path" #~ msgstr "Fuld sti til tilstands-billeder" #~ msgid "Mode" #~ msgstr "Tilstand" #, fuzzy #~ msgid "Profile" #~ msgstr "Profil" #, fuzzy, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil '{profile}': Indtast kodeord for {mode}: " #~ msgid "WARNING" #~ msgstr "ADVARSEL" #, fuzzy #~ 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." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} bruger EncFS til encryptering. Et sikkerhedstjek har vist at der er " #~ "flere mulige angrebsvinkler til dette. Se venligst \"A NOTE ON SECURITY\" i " #~ "\"man backintime\"." backintime-1.5.2/common/po/de.po000066400000000000000000001615571465446530500165160ustar00rootroot00000000000000# German 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. # msgid "" msgstr "" "Project-Id-Version: Back In Time 0.9.5\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-17 08:56+0000\n" "Last-Translator: buhtz \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Warnung" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Hauptprofil" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Lokal (EncFS-verschlüsselt)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (EncFS-verschlüsselt)" #: common/config.py:309 msgid "Local" msgstr "Lokal" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Privater SSH Schlüssel" #: common/config.py:314 msgid "Local encrypted" msgstr "Lokal verschlüsselt" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Verschlüsselung" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH verschlüsselt" #: common/config.py:327 msgid "Default" msgstr "Standard" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: »{name}«" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Schnappschussordner ist ungültig!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Mindestens ein Ordner muss zum Sichern ausgewählt werden!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Ein Sicherungsordner kann nicht einbezogen werden." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Ein Unter-Sicherungsordner kann nicht einbezogen werden." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ungültige Option. {path} ist kein Verzeichnis." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profil-ID darf nicht leer sein." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nach {path} kann nicht geschrieben werden.\n" "Sind Sie sicher, dass Sie Schreibzugriff haben?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Das Zieldateisystem für {path} ist mit FAT formatiert, welches Hardlinks " "nicht unterstützt. Bitte benutzen Sie ein natives Linux-Dateisystem." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Das Zieldateisystem für {path} ist eine eingehängte SMB-Freigabe. Bitte " "stellen Sie sicher, dass der entfernte SMB-Server symbolische Verknüpfungen " "unterstützt oder aktivieren Sie {copyLinks} in {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Verknüpfungen kopieren (symbolische Verknüpfungen zurückverfolgen)" #: common/config.py:497 msgid "Expert Options" msgstr "Experten-Einstellungen" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Das Zieldateisystem für {path} ist eine eingehängte sshfs-Freigabe. Sshfs " "unterstützt keine Hardlinks. Bitte benutzen Sie stattdessen den Modus 'SSH'." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Schreiben einer neuen crontab ist fehlgeschlagen." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron läuft nicht, obwohl der Befehl crontab verfügbar ist. Geplante Backup-" "Aufgaben werden nicht ausgeführt. Cron könnte installiert, aber nicht " "aktiviert sein. Versuche den Befehl \"systemctl enable cron\" oder " "konsultiere die Support-Kanäle deiner GNU/Linux-Distribution." #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Udev-Regel für Profil {profile_id} konnte nicht erstellt werden. DBus-Dienst" " »{dbus_interface}« war nicht verfügbar" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Udev-Zeitplan funktioniert nicht mit Modus {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "UUID für »{path}« konnte nicht gefunden werden" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Speichern der Konfiguration ist fehlgeschlagen" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Laden der Konfiguration ist fehlgeschlagen" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil »{name}« bereits vorhanden." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Das letzte Profil kann nicht entfernt werden." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "»{command}« kann nicht eingehängt werden" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Einstellungen für den verschlüsselten Ordner wurde nicht gefunden." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Soll ein neuer verschlüsselter Ordner erstellt werden?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Abbruch" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Passwort bitte bestätigen." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Passwort stimmt nicht überein." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Schnappschuss erstellen" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kann {mountprocess} nicht von {mountpoint} aushängen." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} nicht gefunden. Bitte installieren (z.B. mit \"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Einhängepunkt {mntpoint} ist nicht leer." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Passwort für {mode} Profil \"{profile}\" eingeben:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "FEHLGESCHLAGEN" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Berechtigungen wiederherstellen" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Fertig" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Aufschieben der Sicherung im Akkubetrieb" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Schnappschussordner konnte nicht gefunden werden.\n" "Wenn er auf einem Wechsellaufwerk ist, bitte dieses verbinden." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Warte %s Sekunde." msgstr[1] "Warte %s Sekunden." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Schnappschuss {snapshot_id} konnte nicht erstellt werden." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Finalisiere" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Verzeichnis kann nicht erstellt werden" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Konfigurationsdatei wird gespeichert …" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Zugriffsrechte werden gespeichert …" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" "Schnappschuss »{snapshot_id}« gefunden, welcher fortgesetzt werden kann." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Schnappschuss »{snapshot_id}« vom letzten Durchgang wird entfernt" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Ordner kann nicht entfernt werden" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Schnappschuss wird erstellt" #: common/snapshots.py:1417 msgid "Success" msgstr "Erfolgreich" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Teilübertragung aufgrund eines Fehlers" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Partieller Transfer aufgrund von verschwundenen Quelldateien (siehe 'man " "rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' beendet mit Code {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Siehe 'man rsync' für weitere Details" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Negative Rückgabewerte von rsync sind Signalnummern. Siehe 'kill -l' und " "'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Keine Änderungen, es ist kein neuer Schnappschuss notwendig" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Umbenennen von {new_path} in {path} fehlgeschlagen" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Cleveres Löschen" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Alte Schnappschüsse werden entfernt" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Versuche das Minimum an freiem Speicher freizuhalten" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Es wird versucht mindestens {perc} freie Inodes zu behalten" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Jetzt" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs} kann nicht eingebunden werden" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" "ssh-agent nicht gefunden. Bitte sicherstellen, dass er installiert ist." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Privater SSH-Schlüssel konnte nicht entsperrt werden. Passwort falsch oder " "für cron nicht verfügbar." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Verschlüsselungsverfahren {cipher} für {host} fehlgeschlagen." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "" "Der Pfad existiert auf dem entfernten Rechner, ist aber kein Verzeichnis." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Der Pfad auf dem entfernten Rechner ist nicht beschreibbar." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Der Pfad auf dem entfernten Rechner ist nicht ausführbar." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Erstellen des Pfades auf dem entfernten Rechner ist fehlgeschlagen." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Der entfernte Rechner {host} unterstützt {command} nicht" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Weitere Anweisungen unter »man backintime«" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Überprüfung der Befehle auf dem Rechner {host} gab unbekannten Fehler zurück" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Der entfernte Rechner {host} unterstützt keine Hardlinks" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" "Den öffentlichen SSH-Schlüssel \"{pubkey}\" zum entfernten Rechner " "\"{host}\" kopieren." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Bitte Passwort für \"{user}\" eingeben." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Über" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autoren" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Übersetzungen" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Lizenz" #: qt/app.py:169 msgid "Shortcuts" msgstr "Verknüpfungen" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Dieser Ordner ist im aktuell ausgewählten\n" "Schnappschuss nicht vorhanden." #: qt/app.py:256 msgid "Add to Include" msgstr "Hinzufügen zum Einschließen" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Hinzufügen zum Ausschließen" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} scheint zum ersten Mal gestartet zu werden, da keine " "Konfiguration gefunden wurde." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Bestehende Konfiguration importieren (von einem Backup-Ordner oder einem " "anderen Computer)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Schnappschussordner wurde nicht gefunden." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Wenn er auf einem Wechsellaufwerk ist, bitte dieses verbinden und dann OK " "drücken." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Schnappschuss erstellen" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Nutzt Änderungszeit und Größe um geänderte Dateien zu erkennen." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Schnappschuss erstellen (Prüfsummen Modus)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Prüfsumme benutzen, um Änderungen von Dateien zu erkennen." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Schnappschuss anhalten" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Schnappschuss fortsetzen" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Schnappschuss stoppen" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Liste der Schnappschüsse auffrischen" #: qt/app.py:508 msgid "Name snapshot" msgstr "Schnappschuss benennen" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Schnappschuss entfernen" #: qt/app.py:516 msgid "View snapshot log" msgstr "Schnappschussprotokoll ansehen" #: qt/app.py:520 msgid "View last log" msgstr "Letztes Protokoll ansehen" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Profile verwalten…" #: qt/app.py:528 msgid "Shutdown" msgstr "Herunterfahren" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Rechner herunterfahren, nachdem der Schnappschuss erstellt wurde." #: qt/app.py:532 msgid "Setup language…" msgstr "Sprache einstellen…" #: qt/app.py:536 msgid "Exit" msgstr "Beenden" #: qt/app.py:540 msgid "Help" msgstr "Hilfe" #: qt/app.py:544 msgid "Profiles config file" msgstr "Profile-Konfigurationsdatei" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Internetseite" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Änderungsprotokoll" #: qt/app.py:553 msgid "FAQ" msgstr "Häufige Fragen (FAQ)" #: qt/app.py:556 msgid "Ask a question" msgstr "Eine Frage stellen" #: qt/app.py:559 msgid "Report a bug" msgstr "Fehler melden" #: qt/app.py:562 msgid "Translation" msgstr "Übersetzung" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Zeigt die Meldung über die Teilnahme an der Übersetzung erneut an." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Umstellung der Verschlüsselung (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Zeigt die Meldung über das Entfernen von EncFS erneut an." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Wiederherstellen" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Ausgewählte Dateien oder Ordner am Originalort wiederherstellen." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Wiederherstellen zu …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Ausgewählte Dateien oder Ordner an einem neuen Ort wiederherstellen." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Stellt den aktuell angezeigten Ordner und seinen gesamten Inhalt am " "ursprünglichen Zielort wieder her." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Stellt den aktuell angezeigten Ordner und seinen gesamten Inhalt an einem " "neuen Zielort wieder her." #: qt/app.py:595 msgid "Up" msgstr "Hoch" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Versteckte Dateien anzeigen" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Schnappschüsse vergleichen…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Backup" #: qt/app.py:676 msgid "&Restore" msgstr "&Wiederherstellen" #: qt/app.py:682 msgid "&Help" msgstr "&Hilfe" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Wenn Sie das Fenster schließen, kann Back In Time den Rechner nicht " "herunterfahren wenn die Sicherung beendet ist." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Möchten Sie es wirklich schließen?" #: qt/app.py:987 msgid "Working:" msgstr "In Bearbeitung:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Fertig, keine Sicherung notwendig" #: qt/app.py:1044 msgid "Working" msgstr "In Bearbeitung" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Fehler" #: qt/app.py:1076 msgid "Sent" msgstr "Gesendet" #: qt/app.py:1077 msgid "Speed" msgstr "Geschwindigkeit" #: qt/app.py:1078 msgid "ETA" msgstr "Verbleibend" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Wurzelverzeichnis" #: qt/app.py:1142 msgid "Home" msgstr "Persönlicher Ordner" #: qt/app.py:1170 msgid "Backup folders" msgstr "Sicherungsordner" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Schnappschussname" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Sind Sie sicher, dass Sie den Schnappschuss löschen wollen?" msgstr[1] "Sind Sie sicher, dass Sie diese Schnappschüsse löschen wollen?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Lokale Elemente vor dem Überschreiben oder\n" "Entfernen zur Sicherung mit angehängten {suffix} speichern." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Vor dem Wiederherstellen werden neuere Dateiversionen mit einem angehängten " "{suffix} umbenannt. Wenn sie nicht mehr benötigt werden, können sie mit dem " "folgenden Befehl entfernt werden:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Nur Elemente wiederherstellen, die im Zielordner nicht existieren\n" "oder in der Sicherung neuer sind als im Zielordner.\n" "Das nutzt die Option »rsync --update«." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Neuere Elemente im Originalordner entfernen." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Ausgewählte Dateien oder Ordner am ursprünglichen Ort wiederherstellen und " "Dateien/Ordner löschen, die nicht in dem Schnappschuss enthalten sind. Seien" " Sie sehr vorsichtig, den damit werden Dateien und Ordner gelöscht, die " "während des Erstellen des Schnappschusses ausgeschlossen waren." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Wollen Sie dieses Element wirklich\n" "in dem neuen Ordner {path} wiederherstellen?" msgstr[1] "" "Wollen Sie diese Elemente wirklich\n" "in dem neuen Ordner {path} wiederherstellen?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Möchten Sie wirklich dieses Element wiederherstellen?" msgstr[1] "Möchten Sie wirklich diese Elemente wiederherstellen?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "Sind Sie sicher, dass Sie alle neueren Dateien in {path} entfernen wollen?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Sind Sie sicher, dass Sie alle neueren Dateien in dem Ursprungsordner " "löschen möchten?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}Warnung{BOLDEND}: Das Löschen von Dateien im Wurzelverzeichnis kann " "das komplette System zerstören." #: qt/app.py:1750 msgid "Snapshot" msgstr "Schnappschuss" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Stelle {path} wieder her" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Stelle {path} wieder nach …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Die Spracheinstellungen wirken erst, nach dem Back In Time neu gestartet " "wurde." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Die Unterstützung für EncFS wird in absehbarer Zukunft eingestellt. Es wird " "nicht empfohlen, diesen Modus für ein Profil zu verwenden." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Eine Entscheidung über einen Ersatz, um die Unterstützung von " "verschlüsselten Backups fortzusetzen, steht noch aus und hängt von den " "Projektressourcen und der Verfügbarkeit Mitwirkender ab. Weitere Details " "sind im folgenden {whitepaper} verfügbar." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "Whitepaper" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Die Unterstützung für verschlüsselte Snapshot-Profile erfährt wesentliche " "Änderungen, und EncFS wird in absehbarer Zukunft entfernt." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" "Folgendes Profil bzw. folgende Profile nutzen Verschlüsselung mit EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Eine Entscheidung über einen Ersatz, um die Unterstützung von " "verschlüsselten Backups fortzusetzen, steht noch aus und hängt von den " "Projektressourcen und der Verfügbarkeit Mitwirkender ab. Nutzende sind " "eingeladen, sich an dieser Diskussion zu beteiligen. Aktualisierte " "Informationen zu den nächsten Schritten sind unter folgendem {whitepaper} " "verfügbar." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Die Nachricht wird nicht wieder angezeigt, ist aber jederzeit über das " "Hilfe-Menu abrufbar." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Dein Back In Time Team" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Sprache einstellen" #: qt/languagedialog.py:92 msgid "System default" msgstr "Standardeinstellung des Systems" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Nutze Sprache des Betriebssystems." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Übersetzt: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Hallo\n" "Sie haben Back In Time in der Sprache {language} nun schon einige Male genutzt.\n" "Die Übersetzung Ihrer installierten Version von Back In Time in {language} ist zu {perc} vollständig. Unabhängig von Ihren technischen Kenntnissen können Sie selbst zur Übersetzung und damit zu Back In Time beitragen.\n" "Bitte besuchen Sie die {translation_platform_url}, wenn Sie dazu beitragen möchten. Für weitere Hilfe und Fragen, besuchen Sie bitte die {back_in_time_project_website}.\n" "Wir entschuldigen uns für die Unterbrechung. Diese Meldung wird nicht noch einmal angezeigt, ist aber jederzeit über das Hilfe-Menü verfügbar.\n" "Ihr Back In Time-Team" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "Übersetzungsplattform" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Ihre Übersetzung" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Ansicht letztes Protokoll" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Ansicht Schnappschussprotokoll" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Schnappschüsse:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Alles" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Änderungen" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Fehler" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Information" msgstr[1] "Informationen" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync Übertragungsfehler (experimentell)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Fehler, [I] Information, [C] Änderung" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "Pfade entschlüsseln" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Frage" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Letztes Protokoll ansehen" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Starte {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "In Bearbeitung…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Gesendet:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Geschwindigkeit:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Schnappschüsse" #: qt/qttools.py:427 msgid "Today" msgstr "Heute" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Gestern" #: qt/qttools.py:443 msgid "This week" msgstr "Diese Woche" #: qt/qttools.py:450 msgid "Last week" msgstr "Letzte Woche" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Das hier ist KEIN Schnappschuss, aber eine aktuellen Ansicht Ihrer lokalen " "Daten" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Letzte Überprüfung {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Komplettes Protokoll anzeigen" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH Proxy" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Host:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "User:" #: qt/settingsdialog.py:120 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 "" "Verbindet sich mit dem Zielhost ü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:170 msgid "Manage profiles" msgstr "Profile verwalten" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Bearbeiten" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Hinzufügen" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Entfernen" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Allgemein" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modus:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Wo Schnappschüsse gespeichert werden" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Ordner" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH-Einstellungen" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Pfad:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Chiffre:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Privater Schlüssel:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Wählt eine existierende private Schüsseldatei (normalerweise als \"id_rsa\" " "benannt)" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Erzeugt einen neuen SSH Schlüssel ohne Passwort (nur wenn noch keine private" " Schlüsseldatei ausgewählt wurde)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Passwort" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Passwort im Schlüsselbund speichern" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Passwort für Cron zwischenspeichern (Sicherheitsproblem: Systemverwalter " "»root« kann das Passwort lesen)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Erweitert" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Vollständiger Schnappschusspfad:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Zeitplan" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Deaktiviert" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Bei jedem Hochfahren/Neustart" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Jede Minute" msgstr[1] "Alle {n} Minuten" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Jede Stunde" msgstr[1] "Alle {n} Stunden" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Jede Stunde" msgstr[1] "Alle {n} Stunden" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Benutzerdefinierte Stunden" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Täglich" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Wiederholend (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Sobald das Laufwerk angeschlossen wird (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Wöchentlich" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Monatlich" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Jährlich" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Tag:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Wochentag:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Stunde:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Stunden:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "»Back In Time« wiederholt starten. Das ist nützlich, wenn der Rechner nicht " "immer eingeschaltet ist." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Alle:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Stunde(n)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Tag(e)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Woche(n)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Monat(e)" #: qt/settingsdialog.py:555 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 "" "»Back In Time« starten, sobald die Festplatte angeschlossen wurde (nur einmal alle X Tage).\n" "Sie werden nach Ihrem sudo-Passwort gefragt werden." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Protokollierung von Debug-Nachrichten aktivieren" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" "Schreibt Debug-Nachrichten in das Systemprotokoll (nutzt \"--debug\" " "Option)." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Vorsicht: Nur vorübergehend zur Diagnose einsetzen, da eine große Menge an " "Ausgaben erzeugt wird." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Einbeziehen" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Dateien und Ordner einbeziehen" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Datei hinzufügen" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Verzeichnis hinzufügen" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "Aus&schließen" #: qt/settingsdialog.py:625 #, 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/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Ausschlussmuster, Dateien oder Ordner" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Voreinstellungen hinzufügen" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Dateien ausschließen, die größer sind als:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Dateien ausschließen, die größer sind als {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Wenn »Voller rsync-Modus« deaktiviert ist, wird das nur neue Dateien " "betreffen, weil das für rsync eine Transferoption und keine Ausschlussoption" " ist. Das bedeutet, dass größere Dateien, die bereits zuvor gesichert " "wurden, auch in neuen Schnappschüssen gesichert werden, selbst wenn sie " "verändert wurden." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Automatisch ent&fernen" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Älter als:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Jahr(e)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Falls freier Speicher kleiner ist als:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Falls weniger Inodes frei sind als:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Cleveres Löschen:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Auf entferntem Rechner im Hintergrund ausführen." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTELL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Alle Schnappschüsse behalten, der letzten" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Tag(e)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Einen Schnappschuss pro Tag behalten, der letzten" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Einen Schnappschuss pro Woche behalten, der letzten" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Woche(n)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Einen Schnappschuss pro Monat behalten, der letzten" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "Monat(e)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Einen Schnappschuss pro Jahr behalten, für alle Jahre." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Benannte Schnappschüsse nicht entfernen." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Optionen" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Benachrichtigungen aktivieren" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Schnappschüsse im Akkubetrieb deaktivieren" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Energiestatus des Systems nicht verfügbar" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Nur einen Schnappschuss zur selben Zeit ausführen" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Andere Schnappschüsse werden blockiert, bis der aktuelle Schnappschuss " "fertiggestellt ist. Das ist eine globale Option, die alle Profile dieses " "Benutzers betreffen wird. Die Option muss aber auch für alle anderen " "Benutzer aktiviert werden." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Beim Wiederherstellen Dateien ersetzen" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Vor dem Wiederherstellen werden neuere Dateiversionen mit einem angehängten " "{suffix} umbenannt. Wenn sie nicht mehr benötigt werden, können Sie sie mit " "{cmd} entfernen" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Bei Fehlern fortfahren (unvollständige Schnappschüsse behalten)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Prüfsumme benutzen, um Änderungen zu erkennen" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Neuen Schnappschuss unabhängig von Änderungen erstellen." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Protokollierungsstufe:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nichts" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "E&xperten-Einstellungen" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Vorsicht: Ändern Sie diese Optionen nur, wenn Sie wirklich wissen, was Sie " "da tun." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Starte »rsync« mit »{cmd}«:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "als cron-job" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "auf entfernten Rechnern" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "wenn ein Schnappschuss manuell erstellt wird" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Bitte 'nocache' installieren, um diese Option freizuschalten)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "auf dem lokalen Rechner" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Leitet stdout in cronjobs nach /dev/null um." #: qt/settingsdialog.py:1005 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:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Leitet stderr in cronjobs nach /dev/null um." #: qt/settingsdialog.py:1016 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/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Beschränke Nutzung der Bandbreite durch rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "ACLs bewahren" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Erweiterte Attribute (xattr) bewahren" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Unsichere Verknüpfungen kopieren (funktioniert nur mit absoluten " "Verknüpfungen)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Auf ein Dateisystem beschränken" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Optionen müssen in Anführungszeichen stehen z.B.: {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Weitere Optionen zu rsync hinzufügen" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Präfix zum SSH-Befehl hinzufügen" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" "Präfix der vor jedem Befehl auf dem entfernten Rechner ausgeführt wird." #: qt/settingsdialog.py:1188 #, 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 "" "Variablen müssen mit \\$FOO maskiert werden. Das betrifft nicht rsync. Um " "einen Präfix für rsync hinzuzufügen muss »{example_value}« mit " "{rsync_options_value} genutzt werden." #: qt/settingsdialog.py:1196 msgid "default" msgstr "Vorgabe" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Überprüfen, ob der entfernte Rechner am Netz ist" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Warnung: Wenn dies deaktiviert ist und der entfernte Rechner nicht verfügbar" " ist, kann es zu einigen verwirrenden Fehlern kommen." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" "Prüfen, ob der entfernte Rechner alle erforderlichen Befehle unterstützt." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Warnung: Wenn dies deaktiviert ist und der entfernte Rechner nicht alle " "erforderlichen Befehle unterstützt, kann es zu einigen verwirrenden Fehlern " "kommen." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Konfiguration wiederherstellen" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "User-callback bearbeiten" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "Die Unterstützung für EncFS wird in absehbarer Zukunft eingestellt. Eine " "Entscheidung über einen Ersatz, um die Unterstützung von verschlüsselten " "Backups fortzusetzen, steht noch aus und hängt von den Projektressourcen und" " der Verfügbarkeit Mitwirkender ab. Weitere Details sind im folgenden " "{whitepaper} verfügbar." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Neues Profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Profil umbenennen" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Sind Sie sicher, dass Sie das Profil »{name}« löschen wollen?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Ausdrücklich empfohlen{ENDBOLD}: (All Empfehlungen bereits " "eingeschlossen.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Ausdrücklich empfohlen{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "Benutzerdefinierte Stunden, bitte als Kommata getrennte Stundenliste " "schreiben (z.B. 8,12,18,23) oder */3 für wiederholte Sicherungen alle 3 " "Stunden." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Sie haben keine Datei für einen privaten SSH Schlüssel ausgewählt." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Möchten Sie ein neues öffentlich/privates und passwortloses Schlüsselpaar " "erstellen?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Die private Schlüsseldatei »{file}« ist nicht vorhanden." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Möchten Sie Ihren öffentlichen SSH-Schlüssel auf den entfernten Rechner " "kopieren, um die passwortlose Anmeldung zu aktivieren?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Die Echtheit des Rechners {host} kann nicht festgestellt werden." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} Fingerabdruck des Schlüssels ist:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Bitte überprüfen Sie diesen Fingerabdruck. Möchten Sie ihn zu Ihrer " "'known_hosts' Datei hinzufügen?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Ausschlussmuster" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Datei ausschließen" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Ordner ausschließen" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Datei einbeziehen" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "»{path}« ist ein symbolischer Link (Symlink). Das verknüpfte Ziel wird nicht gesichert, wenn Sie es nicht ebenfalls hinzufügen.\n" "Möchten Sie stattdessen das Ziel des Links hinzufügen?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Ordner einbeziehen" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Sind Sie sicher, dass Sie den Schnappschussordner ändern wollen?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Erstellen eines neuen SSH-Schlüssels in {path} ist fehlgeschlagen." #: qt/settingsdialog.py:2310 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/settingsdialog.py:2369 msgid "(default: {})" msgstr "(Vorgabe: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "deaktiviert" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "Aktiviert" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Konfiguration importieren" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Keine Konfiguration gefunden" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importieren" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Wähle den Schnappschussordner, aus dem die Konfiguration zu importieren ist." " Der Pfad könnte folgendermaßen aussehen: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Befindet sich der Ordner auf einem externen oder entfernten Laufwerk, muss " "dieses vorher manuell eingehängt werden." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Optionen zum Vergleichen von Schnappschüssen" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Befehl:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parameter:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Verwende %1 und %2 als Pfadparameter" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Bitte diff Befehl festlegen oder Abbrechen drücken." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "Der Befehl \"{cmd}\" wurde auf dem System nicht gefunden. Bitte etwas " "anderes versuchen oder Abbrechen drücken." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Keine Parameter für den diff Befehl vorhanden. Nutze die Standardwerte " "\"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Nur sich unterscheidende Schnappschüsse" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Nur Schnappschüsse auflisten, die gleich sind mit:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Gründliche Prüfung (genauer, aber langsamer)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Löschen" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Alles auswählen" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Vergleiche" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Gehen zu" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Optionen" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Sie können einen Schnappschuss nicht mit sich selbst vergleichen." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Soll in dem Schnappschuss »{snapshot_id}« die Datei »{file}« wirklich " "gelöscht werden?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" "Soll in {count} Schnappschüssen die Datei »{file}« wirklich gelöscht werden?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "WARNUNG: Das kann nicht rückgängig gemacht werden." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "»{path}« von zukünftigen Schnappschüssen ausschließen?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Crontab wurde nicht gefunden. Sind Sie sicher, dass cron installiert ist? " #~ "Wenn nicht, sollten Sie alle automatischen Sicherungen deaktivieren." #~ msgid "Full snapshot path" #~ msgstr "Vollständiger Schnappschusspfad" #~ msgid "Info" #~ msgstr "Information" #~ msgid "Mode" #~ msgstr "Modus" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil »{profile}«: Bitte Passwort für {mode} eingeben: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang im user-callback ist nicht ausführbar." #~ msgid "WARNING" #~ msgstr "WARNUNG" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs-Version 1.7.2 und davor haben einen Fehler mit der Option --reverse. " #~ "Bitte encfs aktualisieren." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "User-callback Skript hat keine Shebang-Zeile (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} nutzt EncFS zur Verschlüsselung. Eine Sicherheitsüberprüfung hat " #~ "kürzlich mehrere mögliche Angriffsmethoden aufgedeckt. Weitere Informationen" #~ " im Kapitel »A NOTE ON SECURITY« unter »man backintime«." backintime-1.5.2/common/po/el.po000066400000000000000000001516061465446530500165200ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:04+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Προειδοποίηση" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Κύριο Προφίλ" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "κρυπτογραφημένο" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH κρυπτογραφημένο" #: common/config.py:309 msgid "Local" msgstr "Τοπικό" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Ιδιωτικό κλειδί SSH" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "κρυπτογραφημένο" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Κρυπτογράφηση" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH κρυπτογραφημένο" #: common/config.py:327 msgid "Default" msgstr "Προεπιλογή" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Προφίλ: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Ο φάκελος στιγμιοτύπων δεν είναι έγκυρος!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "" "Πρέπει να επιλέξετε τουλάχιστον ένα φάκελο για να προστεθεί στο αντίγραφο " "ασφαλείας!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Ο φάκελος του αντιγράφου ασφαλείας δεν μπορεί να συμπεριληφθεί." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Ο υποφάκελος του αντιγράφου δεν μπορεί να εμπεριέχεται." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Μην έγκυρη επιλογή. Το {path} δεν είναι φάκελος." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Το Host/User/Profile-ID δεν πρέπει να είναι κενό." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Αδύνατη η εγγραφή στο: {path}\n" "Είστε σίγουροι ότι έχετε δικαίωμα εγγραφής;" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Το σύστημα αρχείων του προορισμού για το {path} είναι μορφοποιημένο με FAT " "το οποίο δεν υποστηρίζει hard-links. Παρακαλώ χρησιμοποιήστε ένα εγγενές " "σύστημα αρχείων Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Το σύστημα αρχείων προορισμού για το {path} είναι ένα προσαρτημένο τμήμα " "δίσκου SMB. Παρακαλώ σιγουρευτείτε ότι ο απομακρυσμένος SMB διακομιστής " "υποστηρίζει symlinks ή ενεργοποιήστε το {copyLinks} στις {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Αντιγραφή συνδέσμων (αφαίρεση αναφοράς των συμβολικών συνδέσμων)" #: common/config.py:497 msgid "Expert Options" msgstr "Προχωρημένες Ρυθμίσεις" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Το σύστημα αρχείων προορισμού για το {path} είναι ένα προσαρτημένο τμήμα " "δίσκου sshfs. Το sshfs δεν υποστηρίζει τα hard-links. Παρακαλώ " "χρησιμοποιήστε τη λειτουργία 'SSH'." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Αποτυχία εγγραφής καινούργιου crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Το cron δεν εκτελείται παρά το γεγονός ότι η εντολή crontab είναι διαθέσιμη." " Οι προγραμματισμένες εργασίες δημιουργίας αντιγράφων ασφαλείας δεν θα " "εκτελεστούν. Το Cron ενδέχεται να είναι εγκατεστημένο αλλά όχι " "ενεργοποιημένο. Δοκιμάστε την εντολή «systemctl enable cron» ή " "συμβουλευτείτε τα κανάλια υποστήριξης της διανομής σας GNU Linux." #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Ο χρονοπρογραμματισμός με udev δεν δουλεύει με τη λειτουργία {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Δεν βρέθηκε το UUID για το {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Αποτυχία αποθήκευσης της παραμετροποίησης" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Αποτυχία φόρτωσης της παραμετροποίησης" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Το προφίλ \"{name}\" υπάρχει ήδη." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Το τελευταίο προφίλ δεν μπορεί να αφαιρεθεί." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Αδυναμία προσάρτησης '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Η παραμετροποίηση για κρυπτογραφημένο φάκελο δεν βρέθηκε." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Δημιουργία ενός νέου κρυπτογραφημένου φακέλου;" #: common/encfstools.py:151 msgid "Cancel" msgstr "Ακύρωση" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Παρακαλώ επιβεβαιώστε τον κωδικό." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Ο κωδικός δεν ταιριάζει." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Λήψη στιγμιότυπου" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Αδυναμία αποπροσάρτησης {mountprocess} από το {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "Δεν βρέθηκε το {}. Παρακαλώ εγκαταστήστε, π.χ. {}" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Το σημείο προσάρτησης {mntpoint} δεν είναι κενό." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "ΑΠΟΤΥΧΙΑ" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Επαναφορά δικαιωμάτων" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Ολοκληρώθηκε" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Αναβολή του backup όσο το σύστημα βρίσκεται σε μπαταρία" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Δε βρέθηκε ο φάκελος στιγμιοτύπων.\n" "Εάν βρίσκεται σε αφαιρούμενο δίσκο, παρακαλώ συνδέστε τον." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Αναμονή %s δευτερόλεπτο." msgstr[1] "Αναμονή %s δευτερόλεπτα." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Αποτυχία λήψης στιγμιότυπου {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Οριστικοποίηση" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Αδυναμία δημιουργίας φακέλου" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Αποθήκευση αρχείου ρυθμίσεων…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Αποθήκευση δικαιωμάτων…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Βρέθηκε παλαιότερο {snapshot_id} το οποίο μπορεί να συνεχιστεί." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" "Αφαίρεση προηγούμενου {snapshot_id} φακέλου από την τελευταία εκτέλεση" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Αδυναμία αφαίρεσης φακέλου" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Λήψη στιγμιότυπου" #: common/snapshots.py:1417 msgid "Success" msgstr "Επιτυχία" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Μερική μεταφορά λόγω σφάλματος" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Μερική μεταφορά λόγω εξαφανισμένων αρχείων πηγής (δείτε 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "Η διεργασία 'rsync' τελείωσε με κωδικό {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Δείτε το 'man rsync' για περισσότερες λεπτομέρειες" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Οι αρνητικοί αριθμοί ως κωδικοί εξόδου του rsync είναι αριθμοί σήματος, " "δείτε 'kill -l' και 'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Δεν άλλαξε τίποτα, δεν είναι απαραίτητη η λήψη νέου στιγμιότυπου" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Αδυναμία μετονομασίας {new_path} σε {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Έξυπνη αφαίρεση" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Αφαίρεση παλαιών στιγμιοτύπων" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Προσπάθεια διατήρησης ελάχιστου ελεύθερου χώρου" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" "Προσπαάθεια διατήρησης ελάχιστου ποσοστού {perc} ελεύθερων συστημάτων " "αρχείου (inodes)" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Τώρα" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Αδυναμία προσάρτησης {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" "το ssh-agent δε βρέθηκε. Παρακαλώ βεβαιωθείτε ότι είναι εγκατεστημένο." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Δεν ήταν δυνατό το ξεκλείδωμα του ιδιωτικού κλειδιού SSH. Λάθος κωδικός ή " "δεν υπάρχει διαθέσιμος κωδικός για cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Το κρυπτογράφημα {cipher} απέτυχε για τον {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Η απομακρυσμένη διαδρομή υπάρχει αλλά δεν είναι φάκελος." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Η απομακρυσμένη διαδρομή δεν είναι εγγράψιμη." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Η απομακρυσμένη διαδρομή δεν είναι εκτελέσιμη." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Αδυναμία δημιουργίας απομακρυσμένης διαδρομής." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" "Ο απομακρυσμένος διακομιστής {host} δεν υποστηρίζει την εντολή {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Βλέπε 'man backintime' για περισσότερες οδηγίες" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Οι εντολές ελέγχου στον διακομιστή {host} επέστρεψαν ένα άγνωστο σφάλμα" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "O απομακρυσμένος διακομιστής {host} δεν υποστηρίζει hardlinks" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" "Αντιγράψτε το δημόσιο κλειδί SSH \"{pubkey}\" στον απομακρυσμένο διακομιστή " "\"{host}\"" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Παρακαλώ επιβεβαιώστε τον κωδικό του {user}" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Σχετικά" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Δικαιούχοι" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Μεταφράσεις" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Άδεια Χρήσης" #: qt/app.py:169 msgid "Shortcuts" msgstr "Συντομεύσεις" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Αυτός ο φάκελος δεν υπάρχει\n" "στο επιλεγμένο στιγμιότυπο." #: qt/app.py:256 msgid "Add to Include" msgstr "Προσθήκη στα Περιλαμβανόμενα" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Προσθήκη στα Αποκλειόμενα" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Αδυναμία δημιουργίας φακέλου" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Δε βρέθηκε φάκελος στιγμιοτύπων.\n" "Εάν βρίσκεται σε αφαιρούμενη συσκευή, παρακαλώ συνδέστε την." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Λήψη στιγμιοτύπου" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Χρήση ώρας τροποποίησης και μεγέθους, για ανίχνευση αλλαγής αρχείου." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Λήψη στιγμιοτύπου (κατάσταση checksum)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Χρήση των checksum για ανίχνευση αλλαγών αρχείου." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Παύση της διαδικασίας στιγμιοτύπου" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Συνέχιση διαδικασίας στιγμιοτύπου" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Διακοπή διαδικασίας στιγμιοτύπου" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Ανανέωση λίστας στιγμιοτύπου" #: qt/app.py:508 msgid "Name snapshot" msgstr "Ονομασία στιγμιότυπου" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Αφαίρεση στιγμιότυπου" #: qt/app.py:516 msgid "View snapshot log" msgstr "Προβολή καταγραφής στιγμιότυπου" #: qt/app.py:520 msgid "View last log" msgstr "Προβολή τελευταίας καταγραφής" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Διαχείριση προφιλ…" #: qt/app.py:528 msgid "Shutdown" msgstr "Τερματισμός" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "" "Τερματισμός λειτουργίας συστήματος μετά την ολοκλήρωση του στιγμιοτύπου." #: qt/app.py:532 msgid "Setup language…" msgstr "Καθορισμός γλώσσας…" #: qt/app.py:536 msgid "Exit" msgstr "Έξοδος" #: qt/app.py:540 msgid "Help" msgstr "Βοήθεια" #: qt/app.py:544 msgid "Profiles config file" msgstr "Αρχείο ρυθμίσεων προφίλ" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Ιστοσελίδα" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Αρχείο αλλαγών" #: qt/app.py:553 msgid "FAQ" msgstr "Συχνές Ερωτήσεις" #: qt/app.py:556 msgid "Ask a question" msgstr "Κάνε μια ερώτηση" #: qt/app.py:559 msgid "Report a bug" msgstr "Αναφορά προβλήματος" #: qt/app.py:562 msgid "Translation" msgstr "Μετάφραση" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Επαναφορά" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Επαναφορά των επιλεγμένων αρχείων ή φακέλων στον αρχικό τους προορισμό." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Επαναφορά στο…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Επαναφορά των επιλεγμένων αρχείων ή φακέλων σε ένα νέο προορισμό." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Επαναφορά του τρέχοντος εμφανιζόμενου φακέλου και όλων των περιεχομένων του," " στον αρχικό προορισμό." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Επαναφορά του τρέχοντος εμφανιζόμενου φακέλου και όλων των περιεχομένων του " "σε ένα νέο προορισμό." #: qt/app.py:595 msgid "Up" msgstr "Επάνω" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Προβολή κρυφών αρχείων" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Σύγκριση στιγμιοτύπων…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "&Backup" #: qt/app.py:676 msgid "&Restore" msgstr "&Restore" #: qt/app.py:682 msgid "&Help" msgstr "&Βοήθεια" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Αν κλείσετε αυτό το παράθυρο το Back In Time δεν θα μπορεί να τερματίσει το σύστημά σας όταν ολοκληρωθεί το στιγμιότυπο.\n" "Θέλετε πραγματικά να κλείσει;" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "" "Είστε σίγουροι ότι θέλετε να διαγράψετε το {file} από {count} στιγμιότυπα;" #: qt/app.py:987 msgid "Working:" msgstr "Εργασία:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Ολοκληρώθηκε, δεν χρειάζεται backup" #: qt/app.py:1044 msgid "Working" msgstr "Εργασία" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Σφάλμα" #: qt/app.py:1076 msgid "Sent" msgstr "Στάλθηκαν" #: qt/app.py:1077 msgid "Speed" msgstr "Ταχύτητα" #: qt/app.py:1078 msgid "ETA" msgstr "Αναμενόμενος χρόνος" #: qt/app.py:1140 msgid "Global" msgstr "Γενικά" #: qt/app.py:1141 msgid "Root" msgstr "Ρίζα" #: qt/app.py:1142 msgid "Home" msgstr "Οικία" #: qt/app.py:1170 msgid "Backup folders" msgstr "Φάκελοι backup" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Όνομα στιγμιότυπου" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Είστε σίγουροι ότι θέλετε να διαγράψετε το παρακάτω στιγμιότυπο;" msgstr[1] "Είστε σίγουροι ότι θέλετε να διαγράψετε τα παρακάτω στιγμιότυπα;" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Δημιουργία αντιγράφων backup με επίθεμα {suffix}\n" "πριν την αντικατάσταση ή αφαίρεση τοπικών στοιχείων." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Οι νεότερες εκδόσεις αρχείων θα μετονομαστούν με επίθεμα {suffix} πριν " "επανέλθουν. Αν δεν τα χρειάζεστε πια, μπορείτε να τα αφαιρέσετε με {cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Είστε σίγουροι ότι θέλετε να επαναφέρετε αυτό το στοιχείο μέσα στον νέο φάκελο\n" "{path};" msgstr[1] "" "Είστε σίγουροι ότι θέλετε να επαναφέρετε αυτά τα στοιχεία μέσα στον νέο φάκελο\n" "{path};" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "" "Είστε σίγουροι ότι θέλετε να διαγράψετε το {file} από {count} στιγμιότυπα;" msgstr[1] "" "Είστε σίγουροι ότι θέλετε να διαγράψετε τα {file} από {count} στιγμιότυπα;" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" #: qt/app.py:1750 msgid "Snapshot" msgstr "Λήψη στιγμιοτύπου" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 msgid "System default" msgstr "" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:188 #, 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} γλώσσα\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/languagedialog.py:217 msgid "translation platform" msgstr "πλατφόρμα μετάφρασης" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Προφίλ:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Λήψη στιγμιοτύπου:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Φίλτρο:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Όλα" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Αλλαγές" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Σφάλματα" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "" msgstr[1] "" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Προφίλ: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Στάλθηκαν:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Ταχύτητα:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "" #: qt/qttools.py:427 msgid "Today" msgstr "" #: qt/qttools.py:434 msgid "Yesterday" msgstr "" #: qt/qttools.py:443 msgid "This week" msgstr "" #: qt/qttools.py:450 msgid "Last week" msgstr "" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Χρήστης:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Κύριο Προφίλ" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "" #: qt/settingsdialog.py:210 msgid "&General" msgstr "" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Λειτουργία:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Κρυπτοαλγόριθμος:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Ιδιωτικό κλειδί:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Διάλεξε ένα υπάρχων αρχείο ιδιωτικού κλειδιού (συνήθως ονομάζεται " "\"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Χρονοπρογραμματισμός" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Απενεργοποιημένο" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Σε κάθε εκκίνηση/επανεκκίνηση" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Κάθε {n} λεπτό" msgstr[1] "Κάθε {n} λεπτά" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Κάθε{n} ώρες" msgstr[1] "Κάθε ώρα" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Κάθε {n} ώρα" msgstr[1] "Κάθε {n} ώρες" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Ώρες που μπορείτε να τις προσαρμόσετε" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Καθημερινά" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Επανειλημμένως" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Όταν συνδέεται δίσκος (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Κάθε εβδομάδα" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Κάθε μήνα" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Κάθε χρόνο" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Ημέρα:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Ώρα(ες):" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Ώρα(ες):" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Κάθε:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Ώρα(ες)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Ημέρα(ες)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Εβδομάδα(ες)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Μήνας(ες)" #: qt/settingsdialog.py:555 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:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Πρόσθεσε αρχείο" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Πρόσθεσε φάκελο" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Εξαίρεση αρχείου μεγαλύτερου από:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "" #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Έτος(η)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Έξυπνη αφαίρεση:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "ΠΕΙΡΑΜΑΤΙΚΟ" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Ημέρα(ες)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Εβδομάδα(ες)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "Μήνας(ες)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Μην αφαιρέσετε επώνυμα στιγμιότυπα." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Οι νεότερες εκδόσεις αρχείων θα μετονομαστούν με επίθεμα {suffix} πριν " "επανέλθουν. Αν δεν τα χρειάζεστε πια, μπορείτε να τα αφαιρέσετε με {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Χρησιμοποίησε checksum για να εντοπίσεις αλλαγές" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "" #: qt/settingsdialog.py:911 msgid "None" msgstr "Κανένα" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Τρέξε 'rsync' με '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "στην τοπική μηχανή" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/δευτερόλεπτο" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, 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/settingsdialog.py:1196 msgid "default" msgstr "" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "Επαναφορά παραμετροποίησης" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Νέο προφίλ" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Μετονομασία προφίλ" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Εξαίρεση αρχείου" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Συμπερίληψη αρχείου" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Αποτυχία εγγραφής καινούργιου crontab." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "Προεπιλογή" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "ανενεργό" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "ενεργό" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "εντολή" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Παράμετροι:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Διαγραφή" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Δεν μπορείτε να συγκρίνετε ένα στιγμιότυπο με τον εαυτό του." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Θέλετε σίγουρα να διαγράψετε το αρχείο {file} που υπάρχει στο στιγμιότυπο " "{snapshot_id};" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" "Είστε σίγουροι ότι θέλετε να διαγράψετε το {file} από {count} στιγμιότυπα;" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "Αυτό δεν μπορεί να αναιρεθεί!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Εξαίρεση {path} από μελλοντικά στιγμιότυπα;" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Δε βρέθηκε το crontab.\n" #~ "Είστε σίγουροι ότι το cron είναι εγκατεστημένο;\n" #~ "Αν όχι, θα πρέπει να απενεργοποιήσετε όλα τα αυτοματοποιημένα backup." #~ msgid "Mode" #~ msgstr "Λειτουργία" #~ msgid "Profile" #~ msgstr "Προφίλ" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Προφίλ '{profile}': Εισαγωγή κωδικού για {mode}: " #~ msgid "WARNING" #~ msgstr "ΠΡΟΣΟΧΗ" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "Οι εκδόσεις του encfs από την έκδοση 1.7.2 και πριν, περιέχουν ένα σφάλμα " #~ "στην επιλογή --reverse. Παρακαλώ αναβαθμίστε το encfs." backintime-1.5.2/common/po/eo.po000066400000000000000000001531721465446530500165230ustar00rootroot00000000000000# Esperanto translation for backintime # Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 # This file is distributed under the same license as the backintime package. # FIRST AUTHOR , 2011. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-03 15:02+0000\n" "Last-Translator: zvavybir \n" "Language-Team: Esperanto \n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Averto" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Ĉefa profilo" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Loka (EncFS-kriptita)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (EncFS-kriptita)" #: common/config.py:309 msgid "Local" msgstr "Loka" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "privata ŝlosilo de ssh" #: common/config.py:314 msgid "Local encrypted" msgstr "Kriptita loke" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "kriptante" #: common/config.py:320 msgid "SSH encrypted" msgstr "kriptita SSH" #: common/config.py:327 msgid "Default" msgstr "defaŭlto" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profilo: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Dosiero por ekrankopio ne estas valida!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Vi devas elekti almenaŭ unun dosierujon por sekurkopio!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Savkopi-dosierujon ne eblis inkluzivi." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Savkopi-subdosierujon ne eblis inkluzivi." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Nevalida opcio. {path} ne estas dosierujo." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "" "Gastiga komputilo/Uzanto/Identigilo de profilo ne devas esti malplena." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Ne povas skribi al: {path}\n" "Ĉu vi certas ke vi havas permeson por skribi?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Celdosiersistemo por {path} estas formita kun FAT, kio ne subtenas senperajn" " ligilojn. Bonvolu uzi indiĝenan Linuksan dosiersistemo." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Celdosiersistemo por {path} estas muntita retdividejo je SMB. Bonvolu " "certiĝi, ke la fora SMB servilo subtenas simbolajn ligilojn, aŭ aktivigi " "{copyLinks} en {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopii lingilojn (forigi simbolajn lingilojn)" #: common/config.py:497 msgid "Expert Options" msgstr "Spertaj Agordoj" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Celdosiersistemo por {path} estas muntita retdividejo je sshfs. sshfs ne " "subtenas senperajn ligilojn. Bonvolu uzi la 'SSH' reĝimo anstataŭe." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Malsukcesis skribi novan crontab-on." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron ne rulas malgraŭ la ekzisto de la komando \"crontab\". La planitaj " "savkopi-loboroj ne rulos. Cron eble estas instalita, sed ne ŝaltita. Provu " "la komandon \"systemctl enable cron\" aŭ konsulti la subtenaĵoj de via " "GNU/Linux distribuo." #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Ne povis instali udev-regulo por profilo {profile_id}. DBus-servo " "{dbus_interface} ne estis havebla" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "udev-planado ne funkcias je reĝimo {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Ne povas trovi UUID por {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Malsukcesis savi agordon" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Malsukcesis meti agordo" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilo \"{name}\" jam ekzistas." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Ĉi tio ne povas esti nuligita." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Ne povas surmeti '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Agordo por ĉifritaj dosierujoj ne trovis." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Krei novan kriptan dosieron?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Ĉesi" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Bonvolu konfirmi pasvorton." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Pasvorto ne kongruas." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Krei momentaĵon" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Ne povas demeti {mountprocess} disde {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} ne estas trovita. Bonvolu instali ĝin ekz. per " "\"{installcommand}\"" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Surmetingo {mntpoint} ne estas malplena." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Entajpi pasvorton por {mode} profilo \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "Malpravis" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Restaŭri permesojn" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Farite" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Atendas baterion antaŭ komenci savkopii" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Ne povas trovi momentaĵdosierujon.\n" "Se ĝi estas je demetabla memorilo, bonvolu konekti ĝin." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Atendi %s sekundon." msgstr[1] "Atendi %s sekundojn." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Malsukcesas krei momentaĵon {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Finanta" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Ne povas krei dosieron" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Konservante agordan dosieron…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Savante permesojn…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Trovis {snapshot_id}, kiun eblas plukreas." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Forigante {snapshot_id} dosierujon de lasta momentaĵkreo" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Ne povas forigi dosieron" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Faras ekrankopion" #: common/snapshots.py:1417 msgid "Success" msgstr "Sukceso" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Parta transmeto pro eraro" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Parta transmeto, ĉar la fontaj dosieroj malaperis (vidu 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' finas kun elira kodo {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Vidu 'man rsync' por pli multe da detaloj" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Negativaj eliraj kodoj de rsync estas signalkodoj, vidu 'kill -l' kaj 'man " "kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Nenio estas ŝanĝita, nova momentaĵo ne estas necesa" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Ne povas renomi {new_path} al {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Inteligenta forigo" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Malnovaj momentaĵoj estas forigitaj" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Provas konservi la minimumo da neokupita diskospaco" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Provante konservi minimume {perc} da malplenaj indeksnodoj" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Nun" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Ne povas surmeti {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Ne trovas ssh-agent. Bonvolu certiĝi, ke ĝin estas instalita." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Ne povas malŝlosi privata ŝlosilo de ssh. Neĝusta pasvorto aŭ pasvorto ne " "ricevebla por cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Ĉifro {cipher} malsukcesas por {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Fora vojo ekzistas, sed ne estas dosierujo." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Fora vojo ne estas skribebla." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Fora vojo ne estas plenumebla." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Ne povis krei foran vojon." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Foriga gastiganto {host} ne subtenas {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Vidu al 'man backintime' por pli multe da instrukcioj" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Kontrolkomando en gastiganto {host} revenigis nekonatan eraron" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Fora gastiganto {host} ne subtenas senperan ligilon" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Kopii publika ssh-ŝloliso \"{pubkey}\" al la fora gastiganto \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Bonvolu enigi pasvorton por uzanto \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Pri" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Aŭtoroj" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Tradukoj" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Permesilo" #: qt/app.py:169 msgid "Shortcuts" msgstr "Klavkombinoj" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Tio dosierujo ne ekzistas\n" "en la nuntempe selektita momentaĵo." #: qt/app.py:256 msgid "Add to Include" msgstr "Aldoni al la inkluzivitoj" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Aldoni al la ekskluzivitoj" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "Ŝajnas, ke {app_name} rulas unue, ĉar agordo ne estas trovita." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Ĉu importi ekzistantan agordon (de savkopi-celdosierujo aŭ alia komputilo)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Ne povas trovi momentaĵ-dosierujon." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Se ĝi estas je demetebla disko, bonvolu konekti ĝin kaj post tio premi OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Fari momentaĵon" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Uzi tempon kaj grandon de modifo por detektado de dosierŝanĝoj." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Krei momentaĵon (kontrolsumreĝimo)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Uzi kontrolsumojn por detektado de dosierŝanĝoj." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Paŭzi momentaĵkreon" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Malpaŭzi momentaĵkreon" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Ĉesi momentaĵkreon" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Aktualigi liston da momentaĵoj" #: qt/app.py:508 msgid "Name snapshot" msgstr "Nomi momentaĵon" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Forigi momentaĵon" #: qt/app.py:516 msgid "View snapshot log" msgstr "Vidi momentaĵprotokolon" #: qt/app.py:520 msgid "View last log" msgstr "Vidi lastan protokolon" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Administri profilojn…" #: qt/app.py:528 msgid "Shutdown" msgstr "Sistemfermo" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Malŝati sistemon post fini momentaĵkreon." #: qt/app.py:532 msgid "Setup language…" msgstr "Agordi lingvon…" #: qt/app.py:536 msgid "Exit" msgstr "Eliri" #: qt/app.py:540 msgid "Help" msgstr "Helpo" #: qt/app.py:544 msgid "Profiles config file" msgstr "Agorddosiero por profiloj" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Retejo" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Ŝanĝoprotokolo" #: qt/app.py:553 msgid "FAQ" msgstr "Oftaj demandoj" #: qt/app.py:556 msgid "Ask a question" msgstr "Demandi" #: qt/app.py:559 msgid "Report a bug" msgstr "Raporti cimon" #: qt/app.py:562 msgid "Translation" msgstr "Traduko" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Vidigas la mesaĝon pri partopreni je la tradukoj ree." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Ŝanĝi la kriptadon (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Vidigas ree la mesaĝon pri la fortigado de EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Restaŭri" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Restaŭri la selektitajn dosierojn aŭ dosierujojn al originala celo." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Restaŭri al …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Restaŭri la selektitajn dosierojn aŭ dosierujojn al nova celo." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Restaŭri la nuntempe vidigitajn dosierujojn kaj ĉiojn de ilia enhavo al " "originala celo." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Restaŭri la nuntempe vidigitajn dosierujojn kaj ĉiojn de ilia enhavo al nova" " celo." #: qt/app.py:595 msgid "Up" msgstr "Supren" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Montri kaŝitajn dosierojn" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Kompari momentaĵojn…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Savkopio" #: qt/app.py:676 msgid "&Restore" msgstr "&Restaŭri" #: qt/app.py:682 msgid "&Help" msgstr "&Helpo" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Se vi fermi tiun fenestron, Back in Time ne eblos malŝalti vian sistemon." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Ĉu vi vere volas fermi ĝin?" #: qt/app.py:987 msgid "Working:" msgstr "Faranta:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Farita, savkopio ne estas bezonata" #: qt/app.py:1044 msgid "Working" msgstr "Laborante" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Eraro" #: qt/app.py:1076 msgid "Sent" msgstr "Sendita" #: qt/app.py:1077 msgid "Speed" msgstr "Rapideco" #: qt/app.py:1078 msgid "ETA" msgstr "Restanta" #: qt/app.py:1140 msgid "Global" msgstr "Tutmonda" #: qt/app.py:1141 msgid "Root" msgstr "Ĉefuzanto" #: qt/app.py:1142 msgid "Home" msgstr "Hejmo" #: qt/app.py:1170 msgid "Backup folders" msgstr "Savkopiodosierujoj" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nomo de momentaĵo" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Ĉu vi certas ke vi volas forigi tion momentaĵon?" msgstr[1] "Ĉu vi certas ke vi volas forigi tiojn momentaĵojn?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Krei kopioj de savkopioj kun vosta {suffix}\n" "antaŭ anstataŭigi aŭ forigi lokajn elementojn." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Pli novaj versioj de dosieroj estos renomata je vosta {suffix} antaŭ " "restaŭri. Se vi ne plu bezonas ilin, vi povas forigi ilin je la sekvanta " "komando:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Sole restaŭri elementojn, kiuj ne ekzistas aŭ\n" "estas pli novaj ol tiuj, kiuj estas je la celon.\n" "Tio uzas la \"rsync --update\" opcion." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Forigi pli novaj elementoj en la originala dosierujo." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Restaŭri selektajn dosierojn aŭ dosierujon al la originala celon kaj forigi " "dosierojn aŭ dosierujojn, kiuj ne estas en la momentaĵo. Estu treege zorga, " "ĉar tio forigos dosierojn kaj dosierujon, kiuj estis ekskluzivitaj kiam la " "momentaĵo estis kreita." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Ĉu vi vere volas restaŭri ĉi tiun dosier(uj)on al la nova dosierujo\n" "{path}?" msgstr[1] "" "Ĉu vi vere volas restaŭri ĉi tiujn dosier(uj)ojn al la nova dosierujo\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Ĉu vi vere volas restaŭri ĉi tiun dosieron?" msgstr[1] "Ĉu vi vere volas restaŭri ĉi tiujn dosierojn?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Ĉu vi certas ke vi volas forigi ĉiujn pli novajn dosierojn en {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Ĉu vi estas certa, ke vi volas forigi ĉiujn pli novajn dosierojn en la " "originala dosierujo?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}AVERTO{BOLDEND}: Forigi dosierojn en la radika dosierujo povas rompi " "vian tutan sistemon." #: qt/app.py:1750 msgid "Snapshot" msgstr "Momentaĵo" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Restaŭri {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaŭri {path} al …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "La lingoagordoj ne efikas antaŭ restarti Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Post ne longe EncFS ne estos subtenata. Ne estas rekomendita uzi tio ĉi " "reĝimo por profilo plue." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Decido pri anstataŭaĵo por plu-subteni de kriptitaj savkopioj estas ankoraŭ " "pritraktata, depende je rimedo de la projekto kaj havebleco de " "kontribuantoj. Pli detaloj estas in tio {whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "dokumento" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Subteno por profiloj kun kriptitaj momentaĵoj estas ŝanĝata nuntempe, kaj " "EncFS estos forigita post ne longe." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "La sekvanta(j) profilo(j) uzi ĉifrado je EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Decido pri anstataŭaĵo por plu-subteni de kriptitaj savkopioj estas ankoraŭ " "pritraktata, depende je rimedo de la projekto kaj havebleco de " "kontribuantoj. Uzantoj estas kuraĝigita partopreni je la diskuto. Ĝisdataj " "detaloj je la sekvontaj paŝoj estas in tio {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Ĉi tiu mesaĝo ne plu aperos. Ĉi tio dialogo estos ĉiam disponebla en la " "helpmenuo." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Via Back In Time teamo" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Agordi lingvon" #: qt/languagedialog.py:92 msgid "System default" msgstr "Defaŭlto de la sistemo" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Uzi la lingvon de la operaciumo." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Tradukita: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Saluton\n" "Vi estas uzinta Back In Time en {language} kelkfoje nun.\n" "La {language}-traduko de la instalita versio estas {perc} kompleta. Sendistinge de via nivelo de komputil-kono, vi povas kontribui al la traduko kaj tiumaniere al Back In Time mem.\n" "Bonvolu viziti la {translation_platform_url} se vi deziras kontribui. Por plua helpo kaj demandoj, bonvolu viziti la {back_in_time_project_website}.\n" "Ni pardonpetas pri la interrompo, kaj ĉi tiu mesaĝo ne plu aperos. Ĉi tio dialogo estos ĉiam disponebla en la helpmenuo.\n" "Via Back In Time teamo" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "tradukplatformon" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Via traduko" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Vidaĵo je lastaj protokoloj" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Vidaĵo je momentaĵoj" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profilo:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Momentaĵoj:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtrilo:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Ĉiuj" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Ŝanĝoj" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Eraroj" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informoj" msgstr[1] "Informoj" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "Transmetpaneo je rsync (eksperimenta)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Eraro, [I] Informoj, [C] Ŝanĝo" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "Malĉifri vojojn" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Demando" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profilo: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Vidi lastan protokolon" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Komencu {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Laborante…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Sendita:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Rapideco:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "Restanta:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Momentaĵoj" #: qt/qttools.py:427 msgid "Today" msgstr "Hodiaŭ" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Hieraŭ" #: qt/qttools.py:443 msgid "This week" msgstr "Nuna semajno" #: qt/qttools.py:450 msgid "Last week" msgstr "Lasta semajno" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Tio Ne estas momentaĵo, sed liva vivo de viaj lokaj dosieroj" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Lasta kontrolo {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Vidigi tutan protokolon" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH-prokurilo" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Gastiganto:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Pordo:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Uzanto:" #: qt/settingsdialog.py:120 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 dokumentado je la " "programo \"man\"." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Administri profilojn" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Redakti" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Aldoni" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Forigi" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Ĝenerale" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Maniero:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Kien savi la momentaĵojn" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Dosierujo" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Agordo de SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Vojo:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Ĉifro:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Privata Ŝlosilo:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Elekti ekzistantan dosieron je privata ŝlosilo (normale nomita \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Krei novan SSH-ŝlosilon sen pasvorto (ne permesita se privata ŝlosilo estas " "jam elektita)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Pasvorto" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Savi pasvorton je ŝlosilaro" #: qt/settingsdialog.py:378 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:390 msgid "Advanced" msgstr "Progresinta" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Plena momentaĵvojo:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Horaro" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Malŝaltita" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Je ĉiu startigo/restartigo" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Je ĉiu minuto" msgstr[1] "Je ĉiuj {n} minutoj" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Ĉiu horon" msgstr[1] "Ĉiu {n} horojn" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Ĉiu horoj" msgstr[1] "Ĉiu {n} horojn" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Uzdefinitaj horoj" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Ĉiutage" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Ripete (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Kiam diskingo estas kontektate (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Ĉiusemajne" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Ĉiumonate" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Ĉiujare" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Tago:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Labortago:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Horo:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Horoj:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Ruli Back In Time ripetfoje. Tio estas utila se la komputilo ne rulas " "regule." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Ĉiuj:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Horo(j)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Tago(j)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Semajno(j)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Monato(j)" #: qt/settingsdialog.py:555 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 "" "Ruli Back In Time tuj kiam la disko estas konektita (sole unu fojon en X tagoj).\n" "Vi estos demandata je via sudo-pasvorton." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Ŝalti protokoli de sencimig-mesaĝoj" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "Skribas mesaĝoj kun sencimig-nivelo en sistem-protokolo per \"--debug\"." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Singardo: Sole uzu tion kelktempe pro diagnozo, ĉar ĝin generas larĝan " "kvanton de eligo." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Inkluzivi" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Inkluzivi dosierojn kaj dosierujojn" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Aldoni dosieron" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Aldoni dosierujon" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Ekskluzivi" #: qt/settingsdialog.py:625 #, 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/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Ekskluzivi ŝablonojn, dosierojn aŭ dosierujojn" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Aldoni defaŭlton" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Ekskluzivi dosierojn pli grandajn ol:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Ekskluzivi dosierojn pli grandajn ol {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Se 'Tuta rsync reĝimo' estas malvalidigita, tio sole influos novajn " "doiserojn, ĉar por rsync tio estas transmetopcio, ne ekskluzivopcio. Do " "grandaj dosieroj, kiujn estis savkopiita antaŭe, restos en momentaĵoj, eĉ se" " ili ŝanĝis." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Forigi aŭtomate" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Pli malnova ol:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Jaro(j)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Se neokupita loko estas malpli ol:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Se neokupita indeksnodo estas malpli ol:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Inteligenta forigo:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Ruli fone je la foriga gastiganto." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTA" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Savi ĉiujn momentaĵojn de la lasta(j)" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "tago(j)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Savi unu momentaĵon por tago je la lasta(j)" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Savi unu momentaĵon por semajno je la lasta(j)" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "semajno(j)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Savi unu momentaĵon por monato je la lasta(j)" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "monato(j)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Savi unu momentaĵon por jaro je ĉiuj jaroj." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ne forigi nomitajn momentaĵojn." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opcioj" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Aktivigi sciigojn" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Malvalidigi momentaĵkreado kiam ruli je baterio" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Kurentstato ne estas havebla de la sistemo" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Ne ruli plurajn momentaĵojn samtempe" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Aliaj momentaĵoj estos ŝtopita ĝis la aktuala momentaĵo 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:876 msgid "Backup replaced files on restore" msgstr "Anstataŭigi dosierojn dum restaŭri" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Pli novaj versioj de dosieroj estos renomata je vosta {suffix} antaŭ " "restaŭri. Se vi ne plu bezonas ilin, vi povas forigi ilin je {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Kontinui je eraroj (savi nekompletajn momentaĵojn)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Uzi kontrolsumo por detekti ŝanĝojn" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Krei novan momentaĵon eĉ se nenio ŝanĝis." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Nivelo de protokoli:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Neniu" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "&Specialaj Opcioj" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Singardo: Ŝanĝu tiojn opciojn sole se vi reale scias, kiun vi faras." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Startas 'rsync' je '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "kiel cron laboro" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "je la fora gastiganto" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "kiam fari manan momentaĵon" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Bonvolu instali 'nocache' por ŝalti tion opcion)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "je loka komputilo" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Alidirekti stdout al /dev/null en cron laboro." #: qt/settingsdialog.py:1005 msgid "" "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:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Alidirekti stderr al /dev/null en cron laboro." #: qt/settingsdialog.py:1016 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:1024 msgid "Limit rsync bandwidth usage:" msgstr "Trafiklimigi rsync-on:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Konservi ACL-ojn" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Konservi etenditajn atributojn (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopii danĝerajn ligilojn (sole funkcias je absolutaj ligiloj)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Limigi al unu dosiersistemo" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opcioj devas esti citita ekz. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Aldoni pluajn opciojn al rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Aldoni prefikso al SSH-komandoj" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefikso antaŭ ĉiuj komandoj en la fora gastiganto." #: qt/settingsdialog.py:1188 #, 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:1196 msgid "default" msgstr "Defaŭlto" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Kontroli, ke la fora gastiganto estas konektita" #: qt/settingsdialog.py:1214 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:1218 msgid "Check if remote host supports all necessary commands." msgstr "Kontroli, ke la fora gastiganto subtenas ĉiujn necesajn komandojn." #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "Restaŭri agordon" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Redakti user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "Post ne longe EncFS ne estos subtenata. Decido pri anstataŭaĵo por plu-" "subteni de kriptitaj savkopioj estas ankoraŭ pritraktata, depende je rimedo " "de la projekto kaj havebleco de kontribuantoj. Pli detaloj estas in tio " "{whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nova profilo" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Renomi profilon" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Ĉu vi certas ke vi volas forigi la profilon \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Treege rekomenditoj{ENDBOLD}: (Ĉiuj rekomendoj estas inkluzivitaj " "jam.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Treege rekomenditoj{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "Uzdefinitaj horoj sole eblas esti perkome disigita listo (ekz. 8,12,18,23) " "aŭ io kiel */3 por periodaj savkopioj je ĉiu 3 horoj." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Vi ne elektis privata-ŝlosil-dosieron por SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Ĉu vi volas generi novan paron de publika/privatan ŝlosilojn sen pasvorto?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Private ŝlosila dosiero {file} ne ekzistas." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Ĉu vi volas kopii vian publikan SSH-ŝlosilon al la fora gastiganto por " "ebligi ensaluton sen pasvorto?" #: qt/settingsdialog.py:1878 #, 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:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} ŝlosilfingropremo estas:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Bonvolu konfirmi tion fingropremon! Ĉu vi volas aldoni ĝin al via " "'known_hosts' dosiero?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Ekskludi ŝablonon" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Ekskludi dosieron" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Ekskludi dosierujon" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Inkludi dosieron" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" estas simbola ligilo. La ligita celo ne estos savkopiata ĝis vi inkludi ĝin ankaŭ.\n" "Ĉu vi volas inkludi la simbole ligitan celon anstataŭ?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Inkludi dosierujon" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Ĉu vi certas ke vi volas ŝanĝi la momentaĵdosierujon?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Malsukcesi krei novan SSH-ŝlosilon en {path}." #: qt/settingsdialog.py:2310 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/settingsdialog.py:2369 msgid "(default: {})" msgstr "(defaŭlto: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "malŝaltita" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "ŝaltita" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importi agordon" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Ne trovas agordon" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importi" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Elekti la momentaĵ-dosierujon de kie la agord-dosiero devus importota. La " "vojo eble aspektas tiel: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder 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/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opcioj de kompari momentaĵojn" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Komando:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametroj:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Uzi %1 kaj %1 por vojparametroj" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Bonvolu agordi diff-komandon aŭ premi \"Ĉesi\"." #: qt/snapshotsdialog.py:90 #, 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:98 #, 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:134 msgid "Differing snapshots only" msgstr "Sole malsamaj momentaĵoj" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Sole listigi momentaĵojn samajn al:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Kompleta kontrolo (pli precize, sed pli malrapide)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Forigi" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Elekti ĉion" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Kompari" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Iri al" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opcioj" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Vi ne povas kompari momentaĵon je ĝin mem." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Ĉu vi vere volas forigi {file} en momentaĵo {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Ĉu vi vere volas forigi {file} en {count} momentaĵoj?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "AVERTO: Ĉi tio ne povas esti nuligita." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Ekskluzivi {path} de estontaj momentaĵoj?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Ne povas trovi crontab-o.\n" #~ "Ĉu vi certas, ke cron estas instalita?\n" #~ "Se ne, vi devus malvalidigi ĉiujn aŭtomatajn savkopiojn." #~ msgid "Full snapshot path" #~ msgstr "Tuta momentaĵvojo" #~ msgid "Mode" #~ msgstr "Maniero" #~ msgid "Profile" #~ msgstr "Profilo" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profilo'{profile}': Enmeti pasvorton por {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang en skripto de user-callback ne estas plenumebla." #~ msgid "WARNING" #~ msgstr "AVERTO" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "Estas cimo kun opcio --reverse en encfs de la versio 1.7.2 kaj plu fruaj " #~ "versioj. Bonvolu ĝisdatigi encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "Skripto de user-callback ne havas shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} uzas EncFS por ĉifrado. Freŝa sekurekzameno vidigas kelkaj da atakoj " #~ "contraŭ tio. Bonvolu rigardi \"A NOTE ON SECURITY\" en \"man backintime\"." backintime-1.5.2/common/po/es.po000066400000000000000000001602021465446530500165170ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-05 09:28+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Aviso" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Local (cifrado EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (encriptado EncFS)" #: common/config.py:309 msgid "Local" msgstr "Local" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Clave privada SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Cifrado localmente" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Encriptación" #: common/config.py:320 msgid "SSH encrypted" msgstr "Encriptado SSH" #: common/config.py:327 msgid "Default" msgstr "Por defecto" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "¡La carpeta de instantáneas no es válida!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "¡Se debe seleccionar al menos una carpeta para la copia de seguridad!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "No se puede incluir la carpeta para la copia de seguridad." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "No se puede incluir la subcarpeta para la copia de seguridad." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opción inválida. {path} no es una carpeta válida." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "El ID para el host/usuario/perfil no puede estar vacío." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "No se puede escribir en: {path}\n" "¿Está seguro de que tiene permiso de escritura?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "El sistema de ficheros de destino para la ruta {path} está formateada como " "FAT, la cual no permite enlaces duros. Por favor, use un sistema de ficheros" " nativo de Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "El sistema de archivos de destino para {path} es un recurso compartido " "montado en SMB. Asegúrese de que el servidor SMB remoto admite enlaces " "simbólicos o active {copyLinks} en {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Copiar vínculos (descarta vínculos simbólicos)" #: common/config.py:497 msgid "Expert Options" msgstr "Opciones avanzadas" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "El sistema de archivos de destino de {path} es un recurso compartido montado" " en formato sshfs. sshfs no admite enlaces físicos. Utilice el modo \"SSH\" " "en su lugar." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Error al escribir nuevo crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron no se ejecuta aunque el comando crontab esté disponible. Las tareas de " "copia de seguridad programadas no se están ejecutando. Cron podría " "instalarse pero no activarse. Prueba el comando \"systemctl enable cron\" o " "consulta los canales de soporte de tu distribución GNU/Linux." #: common/config.py:1746 #, 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 está disponible" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Programar udev no funciona con el modo {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "No se pudo encontrar el UUID para \"{path}\"" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Error al guardar la configuración" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Se produjo un fallo al cargar la configuración" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "El perfil \"{name}\" ya existe." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "No puede eliminar el último perfil." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "No se pudo montar '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "No se ha encontrado la configuración para el directorio encriptado." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "¿Crear un nuevo directorio encriptado?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Cancelar" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Por favor, confirme la contraseña." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "La contraseña no coincide." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Tomar instantánea" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "No se puede desmontar {mountprocess} de {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "No se encontró el comando {command} . Instálelo (por ejemplo, mediante " "\"{installcommand}\"" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "El punto de montaje {mntpoint} no está vacío." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Introduzca la contraseña para el perfil {mode} \"{profile} \":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "Falló" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Restaurar permisos" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Finalizado" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Aplazando la copia de seguridad mientras se usa la batería" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "No puedo encontrar el directorio de instantáneas.\n" "Si se halla en una unidad extraíble, por favor conéctela." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Esperando %s segundo." msgstr[1] "Esperando %s segundos." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Error al tomar la instantánea {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Terminando" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "No se puede crear carpeta" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Guardando archivo de configuración…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Salvando permisos…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Encontrada {snapshot_id} sobrante que puede continuarse." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Encontrada carpeta de {snapshot_id} sobrante de la última ejecución" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "No se puede eliminar la carpeta" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Tomar instantánea" #: common/snapshots.py:1417 msgid "Success" msgstr "Exitoso" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "La transferencia fue parcial por un error" #: common/snapshots.py:1421 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/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' finalizó con código de salida {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Consulte 'man rsync' para más detalles" #: common/snapshots.py:1445 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/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Nada ha cambiado, no es necesaria una nueva instantánea" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "No se puede renombrar {new_path} a {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Eliminación inteligente" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Eliminando instantáneas antiguas" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Intentar mantener el espacio libre mínimo" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Intentando mantener un mínimo de {perc} de inodos libres" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Ahora" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "No se puede montar {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "No se halló ssh-agent. Por favor, asegúrese de que esté instalado." #: common/sshtools.py:444 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/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "El cifrado {cipher} falló para {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "La ruta remota existe pero no es un directorio." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "La ruta remota no admite escritura." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "La ruta remota no es ejecutable." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "No se ha podido crear la ruta remota." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "El host {host} remoto no admite {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Consulte 'man backintime' para obtener instrucciones adicionales" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Los comandos de verificación en el host {host} devolvieron un error " "desconocido" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "El host remoto {host} no admite enlaces duros" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Copiar clave pública ssh\"{pubkey} \"Al host remoto\"{host} \"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Por favor, introduzca una contraseña para \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Acerca de" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autores" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Traducciones" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licencia" #: qt/app.py:169 msgid "Shortcuts" msgstr "Accesos directos" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Esta carpeta no existe\n" "en la instantánea actualmente seleccionada." #: qt/app.py:256 msgid "Add to Include" msgstr "Agregar a incluir" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Añadir a Excluidos" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} parece estar ejecutándose por primera vez ya que no se encuentra " "ninguna configuración." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "¿Importar una configuración ya existente (desde una carpeta de destino de la" " copia de seguridad u otra computadora)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "" "No se puede encontrar la carpeta para la copia de seguridad instantánea." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Si está en una unidad removible, por favor conéctela y luego presione de " "acuerdo." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Tomar una instantánea" #: qt/app.py:483 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." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Tomar una instantánea (en modo suma de verificación)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Utiliza sumas de verificación para detectar cambios en los archivos." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pausar el proceso de instantánea" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Reanudar el proceso de instantáneas" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Parar el proceso de instantánea" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Recargar la lista de instantáneas" #: qt/app.py:508 msgid "Name snapshot" msgstr "Renombrar instantánea" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Eliminar instantánea" #: qt/app.py:516 msgid "View snapshot log" msgstr "Ver registro de instantánea" #: qt/app.py:520 msgid "View last log" msgstr "Ver último registro" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Perfil principal…" #: qt/app.py:528 msgid "Shutdown" msgstr "Apagar" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Apagar el sistema al terminar instantánea." #: qt/app.py:532 msgid "Setup language…" msgstr "Configurar idioma…" #: qt/app.py:536 msgid "Exit" msgstr "Salir" #: qt/app.py:540 msgid "Help" msgstr "Ayuda" #: qt/app.py:544 msgid "Profiles config file" msgstr "Archivo de configuración de perfiles" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Sitio web" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Registro de cambios" #: qt/app.py:553 msgid "FAQ" msgstr "PREGUNTAS FRECUENTES" #: qt/app.py:556 msgid "Ask a question" msgstr "Haz preguntas" #: qt/app.py:559 msgid "Report a bug" msgstr "Reportar fallos" #: qt/app.py:562 msgid "Translation" msgstr "Traducción" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" "Muestra nuevamente el mensaje sobre la participación en la traducción." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Transición de cifrado (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Muestra nuevamente el mensaje sobre la eliminación de EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Restaurar" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Restaurar los archivos o carpetas seleccionadas a su destino original." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Recuperar en …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Restaurar los archivos o carpetas seleccionados en un nuevo destino." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Restaura la carpeta mostrada actualmente y todo su contenido al destino " "original." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Restaurar la carpeta mostrada actualmente y todo su contenido a un nuevo " "destino." #: qt/app.py:595 msgid "Up" msgstr "Arriba" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Mostrar archivos ocultos" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Comparar instantáneas…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Copia de seguridad" #: qt/app.py:676 msgid "&Restore" msgstr "&Restaurar" #: qt/app.py:682 msgid "&Help" msgstr "&Ayuda" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Si cierra la ventana, Back In Time no podrá apagar el ordenador cuando " "finalice la copia de seguridad." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "¿De verdad quieres cerrarla?" #: qt/app.py:987 msgid "Working:" msgstr "Trabajando:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Hecho, no se necesita una copia de respaldo" #: qt/app.py:1044 msgid "Working" msgstr "En curso" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Error" #: qt/app.py:1076 msgid "Sent" msgstr "Enviado" #: qt/app.py:1077 msgid "Speed" msgstr "Velocidad" #: qt/app.py:1078 msgid "ETA" msgstr "Restante" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Administrador" #: qt/app.py:1142 msgid "Home" msgstr "Carpeta Personal" #: qt/app.py:1170 msgid "Backup folders" msgstr "Carpeta de copias de seguridad" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nombre de la instantánea" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "¿Estás seguro de que quieres eliminar esta instantánea?" msgstr[1] "¿Estás seguro de que quieres eliminar estas instantáneas?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Crear copias de seguridad con {suffix} al final\n" "antes de sobrescribir o eliminar elementos locales." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Antes de restaurar, las versiones más nuevas de los archivos se añaden con " "un {suffix} renombrado. Si ya no son necesarios, se pueden eliminar usando " "el siguiente comando:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Sólo restaura elementos que no existen o\n" "son más recientes que los del destino.\n" "Utilizando la opción \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Elimina los elementos más nuevos de la carpeta original." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Restaura los archivos o carpetas seleccionados en el destino original y " "elimina los archivos o carpetas que no estén en la instantánea. Ten mucho " "cuidado porque esto eliminará archivos y carpetas que se excluyeron durante " "la toma de la instantánea." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "¿Realmente desea restaurar este elemento en la nueva carpeta\n" "{path}?" msgstr[1] "" "¿Realmente desea restaurar estos elementos en la nueva carpeta\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "¿De verdad quieres restaurar este elemento?" msgstr[1] "¿De verdad quieres restaurar estos elementos?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "¿Está seguro de que desea eliminar todos los archivos más recientes en " "{path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "¿Estás seguro de que quieres eliminar todos los archivos nuevos de la " "carpeta original?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}Atención{BOLDEND}: La eliminación de archivos en la raíz del sistema " "de archivos podría romper todo el sistema." #: qt/app.py:1750 msgid "Snapshot" msgstr "Instantánea" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Restaurar {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurar {path} en …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Los ajustes de idioma sólo surten efecto después de reiniciar Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "El soporte para EncFS se discontinuará en el futuro cercano. No se " "recomienda utilizar ese modo para un perfil." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Aún está pendiente una decisión sobre un reemplazo para el soporte continuo " "de copias de seguridad cifradas, dependiendo de los recursos del proyecto y " "la disponibilidad de los colaboradores. Hay más detalles disponibles en este" " {whitepaper} ." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "whitepaper" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "El soporte para perfiles de instantáneas encriptadas está experimentando " "cambios significativos y EncFS se eliminará en el futuro cercano." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "El siguiente perfil o perfiles utilizan cifrado con EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Aún está pendiente una decisión sobre un reemplazo para el soporte continuo " "de copias de seguridad cifradas, dependiendo de los recursos del proyecto y " "la disponibilidad de los colaboradores. Se invita a los usuarios a unirse a " "esta discusión. Los detalles actualizados sobre los próximos pasos están " "disponibles en este {whitepaper} ." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Este mensaje no se volverá a mostrar. Este cuadro de diálogo está disponible" " en cualquier momento a través del menú de ayuda." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Tu equipo de Back In Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Configurar el idioma" #: qt/languagedialog.py:92 msgid "System default" msgstr "Predeterminado del sistema" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Utilizar el lenguaje de los sistemas operativos." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Traducido: {percent}" #: qt/languagedialog.py:188 #, 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 "" "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/languagedialog.py:217 msgid "translation platform" msgstr "plataforma de traduccion" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "La traducción" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Ver el último registro" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Ver registro de instantáneas" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Perfil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Instantáneas:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtrar:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Todo" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Cambios" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Errores" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Información" msgstr[1] "Informaciones" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "Fallos en las transferencias rsync (experimental)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Error, [I] Información, [C] Cambiar" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "decodificar rutas" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Pregunta" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Perfil: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Ver último registro" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Iniciar {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Trabajando…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Enviado:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Velocidad:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "Tiempo restante estimado:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Instantáneas" #: qt/qttools.py:427 msgid "Today" msgstr "Hoy" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Ayer" #: qt/qttools.py:443 msgid "This week" msgstr "Esta semana" #: qt/qttools.py:450 msgid "Last week" msgstr "Última semana" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Esto NO es una instantánea, sino una vista en directo de tus archivos " "locales" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Última comprobación {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Mostrar el registro completo" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Proxy SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Host:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Puerto:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Usuario:" #: qt/settingsdialog.py:120 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 al host de destino a través de este proxy (también conocido como " "host de salto). Consulte \"-J\" en la documentación del comando \"ssh\" o " "\"ProxyJump\" en la página de manual \"ssh_config\" para obtener más " "detalles." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Administrar perfiles" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Editar" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Añadir" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Quitar" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&General" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modo:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "¿Dónde guardar las instantáneas?" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Carpeta" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Ajustes de SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Ruta:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Cifrado:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Clave privada:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Elija un archivo de clave privada existente (normalmente llamado \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Genera una nueva clave SSH sin contraseña (solo si aún no se ha seleccionado" " ningún archivo de clave privada)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Contraseña" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Guardar la contraseña en el llavero" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Contraseña de caché para Cron (problema de seguridad: root puede leer la " "contraseña)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Avanzado" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Ruta completa de la instantánea:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Tareas programadas" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Desactivado" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "En cada arranque/reinicio" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Cada {n} minuto" msgstr[1] "Cada {n} minutos" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Cada hora" msgstr[1] "Cada {n} horas" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Cada {n} hora" msgstr[1] "Cada {n} horas" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Horario personalizado" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Todos los días" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Repetidamente (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Cuando la unidad se conecta (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Semanalmente" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Mensualmente" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Anualmente" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Día:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Día laborable:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Hora:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Horas:" #: qt/settingsdialog.py:521 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:528 msgid "Every:" msgstr "Cada:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Hora(s)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Día(s)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Semana(s)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Mes(es)" #: qt/settingsdialog.py:555 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 "" "Ejecute Back In Time en cuanto se conecte la unidad (sólo una vez cada X días).\n" "Se le pedirá su contraseña sudo." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Activar el registro de mensajes de depuración" #: qt/settingsdialog.py:568 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:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Precaución: Utilícelo sólo temporalmente para diagnósticos, ya que genera " "una gran cantidad de salida." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Incluir" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Incluir archivos y carpetas" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Añadir archivo" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Añadir carpeta" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Excluir" #: qt/settingsdialog.py:625 #, 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}: En el modo 'SSH encriptado', solo son " "funcionales los asteriscos simples o dobles (por ejemplo {example2}). Otros " "tipos de comodines y patrones serán ignorados (por ejemplo,{example1}). Los " "nombres de archivo son impredecibles en este modo debido a la encriptación " "de EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Excluir patrones, archivos o carpetas" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Agregar valor predeterminado" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Excluir archivos mayores que:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Excluir archivos mayores que el valor en {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Con el \"Modo rsync completo\" desactivado, esto sólo afectará a los " "archivos nuevos, ya que para rsync se trata de una opción de transferencia, " "no de exclusión. Por lo tanto, los archivos de gran tamaño de los que se " "haya realizado una copia de seguridad anteriormente persistirán en las " "instantáneas aunque se hayan modificado." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Eliminar &automáticamente" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Mayor que:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Año(s)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Si el espacio libre es inferior a:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Si los inodos libres es menor que:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Eliminación inteligente:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Se ejecuta en segundo plano en el host remoto." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Mantener todas las instantáneas hasta la última" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "día(s)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Conserve una instantánea por día para el último día" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Conserve una instantánea por semana para la última semana" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "semana(s)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Conserve una instantánea por mes para el último mes" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mes(es)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Conserve una instantánea al año para todos los años." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "No elimine las instantáneas con nombre." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opciones" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Activar notificaciones" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Desactivar instantáneas cuando se esté con batería" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "El estado de la energía no está disponible en el sistema" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Ejecutar sólo una instantánea a la vez" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Las demás instantáneas se bloquearán hasta que finalice la instantánea " "actual. Esta es una opción global. Así que afectará a todos los perfiles de " "este usuario. Pero es necesario activar esto para todos los demás usuarios, " "también." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Copia de seguridad de los archivos sustituidos al restaurar" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Las versiones más recientes de los archivos se renombrarán con {suffix} al " "final antes de restaurarlos. Si ya no los necesita, puede eliminarlos con " "{cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuar si hay errores (mantener imágenes incompletas)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Utilizar la suma de verificación (checksum) para detectar cambios" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Tome una nueva instantánea tanto si ha habido cambios como si no." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Nivel del registro:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Ninguno" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "A&justes avanzados" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Atención: Cambie estas opciones sólo si realmente sabe lo que está haciendo." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Ejecute 'rsync' con '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "como tarea cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "en un host remoto" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "al tomar una instantánea manualmente" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Instale 'nocache' para activar esta opción)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "en un equipo local" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirigir stdout a /dev/null en cronjobs." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Cron enviará automáticamente un correo electrónico con la salida adjunta de " "cronjobs si hay un MTA instalado." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirigir stderr a /dev/null en cronjobs." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Cron enviará automáticamente un correo electrónico con errores adjuntos de " "cronjobs si hay un MTA instalado." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Limita el uso del ancho de banda de rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "kbit/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Preservar ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Preservar atributos extendidos (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Copiar vínculos incompletos (funciona solo con vínculos absolutos)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Restringir a un sistema de archivos" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Las opciones deben entrecomillarse, por ejemplo {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Pegar opciones adicionales a rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Añadir prefijo a comandos SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefijo a ejecutar antes de cada comando en el host remoto." #: qt/settingsdialog.py:1188 #, 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 "" "Las variables deben tener como escape \\$FOO. Esto no afecta a rsync. Para " "agregar un prefijo para rsync tienes que » {example_value} « con Se puede " "utilizar {rsync_options_value} ." #: qt/settingsdialog.py:1196 msgid "default" msgstr "por defecto" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Comprobar si el host remoto está en línea" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Advertencia: Si se desactiva y el host remoto no está disponible, esto " "podría dar lugar a algunos errores extraños." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Compruebe si el host remoto admite todos los comandos necesarios." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Advertencia: Si se desactiva y el host remoto no soporta todos los comandos " "necesarios, esto podría dar lugar a algunos errores extraños." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Restablecer configuración" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Editar user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "El soporte para EncFS se suspenderá en un futuro previsible. Está pendiente " "una decisión sobre un reemplazo para continuar admitiendo copias de " "seguridad cifradas y depende de los recursos del proyecto y la " "disponibilidad de los contribuyentes. Más detalles están a continuación " "{whitepaper} disponible." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nuevo perfil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Renombrar perfil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "¿Estás seguro de que quieres borrar el perfil \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Muy recomendable{ENDBOLD}: (Todas las recomendaciones ya incluidas.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Muy recomendable{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "Las horas personalizadas sólo pueden ser una lista de horas separadas por " "comas (por ejemplo, 8,12,18,23) o */3 para copias de seguridad periódicas " "cada 3 horas." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "No ha elegido un archivo de clave privada para SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "¿Desea generar un nuevo par de claves pública/privada sin contraseña?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "El archivo de clave privada \"{file}\" no existe." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "¿Le gustaría copiar su clave pública SSH en el host remoto para permitir el " "inicio de sesión sin contraseña?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "No se puede establecer la autenticidad del host {host}." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "La huella digital de la clave {keytype} es:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Por favor, verifique esta huella digital. ¿Desea agregarla a su archivo " "'known_hosts'?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Excluir patrón" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Excluir archivo" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Excluir carpeta" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Incluir archivo" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" es un enlace simbólico. El destino vinculado no se incluirá en la copia de seguridad hasta que usted también lo incluya.\n" "¿Desea incluir el enlace simbólico en su lugar?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Incluir carpeta" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "¿Estás seguro de que quieres cambiar la carpeta de instantáneas?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Error al crear una nueva clave SSH en {path}." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" "Deshabilitado porque este patrón no es funcional en el modo 'SSH " "encriptado'." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(por defecto: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "desactivado" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "activado" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importar la configuración" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "No se ha encontrado ninguna configuración" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importar" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Seleccione la carpeta de instantáneas desde la que debe importarse el " "archivo de configuración. La ruta puede tener el siguiente aspecto " "{samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Si la carpeta se encuentra en una unidad externa o remota, debe montarse " "previamente de forma manual." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opciones sobre la comparación de instantáneas" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Comando:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parámetros:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Usar %1 y %2 para los parámetros de ruta" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Por favor, establezca un comando diff o pulse Cancelar." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "El comando \"{cmd}\" no se encuentra en este sistema. Por favor, intente " "otra cosa o pulse Cancelar." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "No hay parámetros establecidos para el comando diff. Usando el valor por " "defecto \"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Sólo instantáneas diferentes" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Enumere solo instantáneas que sean iguales a:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Comprobación rigurosa (más precisa, pero lenta)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Borrar" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Seleccionar todo" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Comparar" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Ir a" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opciones" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "No se puede comparar una misma instantánea." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "¿Realmente quieres borrar {file} en la instantánea {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "¿Realmente quiere borrar \"{file}\" en {count} instantáneas?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "ADVERTENCIA: No se puede revocar." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "¿Excluir {path} de futuras instantáneas?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "No se puede encontrar crontab. ¿Estás seguro de que tienes cron instalado? " #~ "Si no está instalado, debe desactivar todas las copias de seguridad " #~ "automáticas." #~ msgid "Full snapshot path" #~ msgstr "Ruta completa de la instantánea" #~ msgid "Mode" #~ msgstr "Modo" #~ msgid "Profile" #~ msgstr "Perfil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Perfil '{profile}': Ingrese clave para {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang en el script user-callback no es ejecutable." #~ msgid "WARNING" #~ msgstr "ADVERTENCIA" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "La versión 1.7.2 de encfs y anteriores presentan un error con option " #~ "--reverse. Por favor, actualice encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "El script user-callback no tiene línea shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} utiliza EncFS para el cifrado. Una reciente auditoría de seguridad " #~ "reveló varios posibles vectores de ataque para esto. Por favor, echa un " #~ "vistazo a \"UNA NOTA SOBRE SEGURIDAD\" en \"man backintime\"." backintime-1.5.2/common/po/et.po000066400000000000000000001266341465446530500165330ustar00rootroot00000000000000# Estonian 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: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-28 17:24+0000\n" "Last-Translator: Priit Jõerüüt \n" "Language-Team: Estonian \n" "Language: et\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Hoiatus" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Peamine profiil" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Kohalik (krüptitud EncFS abil)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (krüptitud EncFS abil)" #: common/config.py:309 msgid "Local" msgstr "Kohalik" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH privaatvõti" #: common/config.py:314 msgid "Local encrypted" msgstr "Kohalik krüptituna" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Krüptimine" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH krüptituna" #: common/config.py:327 msgid "Default" msgstr "Vaikimisi" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profiil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Hetktõmmiste kaust ei ole korrektne!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Sa pead valima vähemalt ühe kataloogi tagavarakoopia tegemiseks!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Varukoopia kausta ei saa kaasata." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Varukoopia alamkausta ei saa kaasata." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Kõlbamatu valik. {path} ei ole kaust." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Serveri nimi/Kasutajanimi/Profiil ei tohi olla tühjad." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Ei saa kirjutada: {path}\n" "Oled kindel, et sul kirjutamis õigused?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Failisüsteem, kuhu „{path}“ asukohta tahad luua varukoopiat, on FAT-" "vormingus ega toeta püsilinke. Palun kasuta mõnda Linuxile mõeldud " "failisüsteemi." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Failisüsteem, kuhu „{path}“ asukohta tahad luua varukoopiat, on SMB-põhisel " "andmekandjal. Palun kontrolli, et see SMB server toetab sümbollinkide " "kasutamist või seadistuste alajoatusest „{expertOptions}“ lülita sisse " "„{copyLinks}“." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopeeri lingid (eemalda sümbollinkide seosed)" #: common/config.py:497 msgid "Expert Options" msgstr "Lisaseadistused asjatundjatele" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Failisüsteem, kuhu „{path}“ asukohta tahad luua varukoopiat, on haagitud " "sshfs abil. Kahjuks sshfs ei toeta püsilinke. Palun kasuta selle asemel SSH " "töörežiimi." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Uue crontabi loomine ei õnnestunud." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "„{path}“ asukoha jaoks ei suuda tuvastada UUID-d" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Seadistuste salvestamine ei õnnestu" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Seadistuste laadimine ei õnnestu" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profiil \"{name}\" on juba olemas." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Viimase profiili eemaldamine pole võimalik." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Haakimine pole võimalik: „{command}“" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Krüptitud kausta seadistusi ei õnnestu leida." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Kas loome uue krüptitud kausta?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Katkesta" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Palun korda salasõna." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Salasõnad ei klapi omavahel." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Tee hetktõmmis" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "„{mountprocess}“ lahtihaakimine asukohast „{mountpoint}“ ei õnnestu." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "„{command}“ käsku ei leidu. Palun paigalda ta (näiteks nii: " "„{installcommand}“)" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "„{mntpoint}“ haakepunkt pole vaba." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Sisesta salasõna valitud profiili jaoks „{mode} - {profile}“:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "EBAÕNNESTUS" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Taastamise õigused" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Valmis" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Viivitame varukoopia tegemist seni, kuni arvuti on akutoitel" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Ei leia hetktõmmiste kausta.\n" "Kui kaust asub eemaldatavas seadmes, siis palun ühenda see." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Ootame %s sekundi." msgstr[1] "Ootame %s sekundit." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Hetktõmmise nr {snapshot_id} tegemine ei õnnestunud." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Lõpetan" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Ei saa luua kataloogi" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Salvesta seade fail …" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Salvesta õigused …" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Leidsime varasema hetktõmmise nr {snapshot_id}, mida saad täiendada." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Eemaldame varasemast ülejäänud hetktõmmise nr {snapshot_id} kausta" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Ei saa eemaldada kataloogi" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Teeme hetktõmmist" #: common/snapshots.py:1417 msgid "Success" msgstr "Õnnestus" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Mitte midagi pole muutunud, uue hetktõmmise tegemine pole vajalik" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Nutikas eemaldamine" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Eemaldame vanad hetktõmmised" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Nüüd" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Ei saa luua kataloogi." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Programmist" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "" #: qt/app.py:169 msgid "Shortcuts" msgstr "Otseteed" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Seda kausta pole praegu\n" "valitud hetktõmmises olemas." #: qt/app.py:256 msgid "Add to Include" msgstr "" #: qt/app.py:258 msgid "Add to Exclude" msgstr "" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Ei õnnestu leida hetktõmmiste kausta." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Kui ta asub eemaldataval andmekandjal, siis palun ühenda see ja vajuta " "Sobib." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Tee hetktõmmis" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Tee hetktõmmis (kontrollsummadega)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Peata hetktõmmise tegemine" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Jätka hetktõmmise tegemist" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Lõpeta hetktõmmise tegemine" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Uuenda hetktõmmiste loendit" #: qt/app.py:508 msgid "Name snapshot" msgstr "Anna hetktõmmisele nimi" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Eemalda hetktõmmis" #: qt/app.py:516 msgid "View snapshot log" msgstr "Vaata hetktõmmiste logi" #: qt/app.py:520 msgid "View last log" msgstr "Vaata Viimast Logi" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Peamine profiil…" #: qt/app.py:528 msgid "Shutdown" msgstr "" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Kui hetktõmmise tegemine on lõppenud, siis pane arvuti kinni." #: qt/app.py:532 msgid "Setup language…" msgstr "" #: qt/app.py:536 msgid "Exit" msgstr "Välju" #: qt/app.py:540 msgid "Help" msgstr "Abi" #: qt/app.py:544 msgid "Profiles config file" msgstr "Salvesta seade fail" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Veebileht" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "" #: qt/app.py:553 msgid "FAQ" msgstr "" #: qt/app.py:556 msgid "Ask a question" msgstr "" #: qt/app.py:559 msgid "Report a bug" msgstr "" #: qt/app.py:562 msgid "Translation" msgstr "" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Taasta" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Taasta asukohta …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:595 msgid "Up" msgstr "Üles" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Näita peidetud faile" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Võrdle hetktõmmiseid…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 msgid "&Restore" msgstr "Taa&sta" #: qt/app.py:682 msgid "&Help" msgstr "&Abi" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Kui sa sulged selle akna, siis Back In Time ei suuda peale hetktõmmise " "tegemist sinu arvuti tööd lõpetada." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Kas sa oled kindel, et tahad selle sulgeda?" #: qt/app.py:987 msgid "Working:" msgstr "Töötan:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "" #: qt/app.py:1044 msgid "Working" msgstr "Töötan" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "" #: qt/app.py:1076 msgid "Sent" msgstr "" #: qt/app.py:1077 msgid "Speed" msgstr "" #: qt/app.py:1078 msgid "ETA" msgstr "" #: qt/app.py:1140 msgid "Global" msgstr "Üleüldine" #: qt/app.py:1141 msgid "Root" msgstr "" #: qt/app.py:1142 msgid "Home" msgstr "Kodukataloog" #: qt/app.py:1170 msgid "Backup folders" msgstr "Tagavarakoopia kataloogid" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Hetktõmmise nimi" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Kas oled kindel, et soovid kustutada selle hetktõmmise?" msgstr[1] "Kas oled kindel, et soovid kustutada need hetktõmmised?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Taasta valitud failid ja kaustad algsesse asukohta ning kustuta failid ja " "kaustad, mis ei leidunud selles hetktõmmises. Kuna hetktõmmise tegemisel " "välistatud failid ja kaustad kuuluvad seetõttu kustutamisele, siis palun ole" " eriti ettevaatlik." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" msgstr[1] "" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Kas sa oled kindel, et soovid taastada selle faili või kausta?" msgstr[1] "Kas sa oled kindel, et soovid taastada need failid või kaustad?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" #: qt/app.py:1750 msgid "Snapshot" msgstr "Hetktõmmis" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Taasta {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Taasta {path} asukohta …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Krüptitud hetktõmmiste tegemise võimalus on hetkel suuresti muutumises ja " "EncFS saab lähiajal eemaldatud." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 msgid "System default" msgstr "" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:188 #, 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 "" "Tere\n" "Sa oled nüüdseks juba kasutanud Back In Time'i {language} keeles mitmeid kordi.\n" "Sinu poolt paigaldatud Back In Time'i versiooni tõlge {language} keelde on {perc} valmis. Sõltumata sinu tehnilistest teadmistest, võid sa panustada tõlkesse ja seeläbi ka Back In Time'i enesesse.\n" "Palun külasta {translation_platform_url} lehte kui sa soovid panustada. Edasiseks toeks ja küsimusteks, palun külasta {back_in_time_project_website}.\n" "Vabandage katkestuse pärast, ja seda sõnumit ei näidata uuesti. See dialoog on kättesaadav igal ajal läbi abimenüü.\n" "Sinu Back In Time meeskond" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Hetktõmmiste logivaade" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profiil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Hetktõmmised:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Kõik" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Muutused" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Veateated" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Teave" msgstr[1] "Teave" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profiil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Vaata Viimast Logi" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Töötan…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Hetktõmmised" #: qt/qttools.py:427 msgid "Today" msgstr "Täna" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Eile" #: qt/qttools.py:443 msgid "This week" msgstr "See nädal" #: qt/qttools.py:450 msgid "Last week" msgstr "Eelmine nädal" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "See EI OLE hetktõmmis, vaid sinu kohaliku failisüsteemi failide tegelik " "vaade" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Host:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Kasutaja:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Peamine profiil" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Muuda" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Lisa" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Eemalda" #: qt/settingsdialog.py:210 msgid "&General" msgstr "Ü&ldine" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Töörežiim:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Kuhu salvestame hetktõmmised" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Kaust" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH seadistused" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Asukoht:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Põhjalikum" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Hetktõmmiste täpne asukoht:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Välja lülitatud" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Igal käivitamisel/taaskäivitamisel" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Iga {n} minuti järel" msgstr[1] "Iga {n} minuti järel" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Kord tunnis" msgstr[1] "Iga {n} tunni järel" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Iga {n} tunni järel" msgstr[1] "Iga {n} tunni järel" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Iga päev" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Iga nädal" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Iga kuu" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Iga aasta" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Nädalapäev:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Tund:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Tund:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Iga:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Päev(a)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Nädal(at)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:555 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:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Kaasa" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Kaasa failid ja kataloogid" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Lisa fail" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Lisa kaust" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Jäta välja" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Jäta välja mustrid, failid või kataloogid" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Jäta välja fail:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "" #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Automaatselt eemalda" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Vanem kui:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Aasta(t)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Kui vabaruumi on vähem kui:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Kui vabaruumi on vähem kui:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Nutikas eemaldamine:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Säilita kõik hetktõmmised" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "päev(ad)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Säilita üks hetktõmmis iga päeva kohta" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Säilita üks hetktõmmis iga nädala kohta" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "nädal(ad)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Säilita üks hetktõmmis iga kuu kohta" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "kuu(d)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Säilita üks hetktõmmis iga aasta kohta kõikide aastate jooksul." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ära kustuta nimedega hetktõmmiseid." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Valikud" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Luba teated" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Kui arvuti on akutoitel, siis ära tee hetktõmmiseid" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Tee korraga vaid üks hetktõmmis" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Muude hetktõmmiste tegemine on seni blokeeritud, kuni hetkel pooleli olev " "hetktõmmis on tehtud. See on rakenduse üldine eelistus ja seega mõjutab " "selle kasutaja kõiki profiile. Aga sa pead ta aktiveerima kõikide kasutajate" " puhul." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Vigade puhul jätka tööd (säilita poolikud hetktõmmised)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" "Tee uus hetktõmmis hoolimata sellest, kas arvutis tekkis muutusi või mitte." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Logitase:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Pole" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Ek&sperdi valikud" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "kui teed hetktõmmist käsitsi" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, 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/settingsdialog.py:1196 msgid "default" msgstr "" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Uus profiil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Muuda profiili nime" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Oled kindel, et tahad kustuda profiili \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Soovitame tungivalt{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Jäta välja fail" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Jäta välja kataloog" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Kaasa fail" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Kaasa katloog" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Kas oled kindel, et soovid muuta hetktõmmiste kausta?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Vali hetktõmmiste kaust, kust soovid seadistuste faili importida. Asukoht " "võiks olla midagi sellist: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Hetktõmmiste võrdlemise valikud" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Käsk:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parameetrid:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Vaid siis, kui hetktõmmised erinevad" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Näita vaid hetktõmmiseid, mis võrduvad järgnevaga:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Mine" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Valikud" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Sa ei saa võrrelda hetktõmmist iseendaga." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Kas sa kindlasti soovid kustutada „{file}“ hetktõmmises nr {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" "Kas sa kindlasti soovid kustutada „{file}“ kokku {count}s hetktõmmises?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Kas soovid välistada tulevastest hetktõmmistest asukoha „{path}“?" #~ msgid "Profile" #~ msgstr "Profiil" backintime-1.5.2/common/po/eu.po000066400000000000000000001552461465446530500165350ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-05 20:13+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Abisua" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Profil nagusia" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "Lokalki zifratua" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH zifratua" #: common/config.py:309 msgid "Local" msgstr "Lokala" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH giltza pribatua" #: common/config.py:314 msgid "Local encrypted" msgstr "Lokalki zifratua" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Zifraketa" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH zifratua" #: common/config.py:327 msgid "Default" msgstr "Lehenetsia" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profila: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Babeskopien karpeta ez da baliozkoa!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Gutxienez karpeta bat hautatu behar duzu babeskopiarako!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Ezin da babeskopia karpeta sartu." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Babeskopia azpikarpeta ezin da sartu." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Aukera baliogabea. {path} ez da karpeta bat." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Ostalari/Erabiltzaile/Profila-ID ez da hutsik egon behar." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Ezin da hor idatzi: {path}\n" "Ziur zaude idazteko baimenik baduzula?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "'{path}'-(r)en helburuko fitxategi-sistemaren formatua FAT da eta ez du " "esteka gogorrik onartzen. Erabili Linux jatorriko fitxategi-sistema bat." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "'{path}'-(r)en helburuko fitxategi-sistema SMB-ez muntatutako partekatzea " "da. Egiaztatu urruneko SMB zerbitzariak esteka sinbolikoak onartzen dituela " "edo gaitu '{copyLinks}' '{expertOptions}'-en." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopiatu estekak (erreferentzia kendu esteka sinbolikoei)" #: common/config.py:497 msgid "Expert Options" msgstr "Aukera aurreratuak" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "'{path}'-(r)en helburuko fitxategi-sistema sshfs-ez muntatutako partekatzea " "da. sshfs-ek ez du esteka gogorrik onartzen. Erabili 'SSH' haren ordez." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Huts egin du crontab berria idazten." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron ez da exekutatzen crontab komandoa eskuragarri egon arren. " "Programatutako babeskopia-lanak ez dira exekutatuko. Baliteke Cron " "instalatuta egotea baina gaituta ez egotea. Saiatu \"systemctl enable cron\"" " komandoa edo kontsultatu zure GNU Linux banaketaren laguntza kanalak." #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Programatutako udev-a ez dabil {mode} moduarekin" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Ezin da UUID aurkitu \"{path}\"-erako" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Huts egin du ezarpenak gordetzen" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Huts egin du ezarpenak kargatzen" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\" profila badago honezkero." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Ezin da azken profila kendu." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "'{command}' ezin da muntatu" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Ez dira aurkitu zifratutako karpetaren ezarpenak." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Sortu karpeta zifratu berri bat?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Utzi" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Egiaztatu pasahitza mesedez" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Pasahitzak ez datoz bat." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Egin babeskopia" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Ezin da {mountprocess} desmuntatu {mountpoint}-tik." #: common/mount.py:696 #, fuzzy, 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/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "{mntpoint} muntatze-puntua ez dago hutsik." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Idatzi {mode} \"{profile}\" profilaren pasahitza:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "HUTS EGIN DU" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Leheneratu baimenak" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Eginda" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Atzeratu babeskopia bateriaz dabilen bitartean" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Ezin du aurkitu babeskopien karpeta.\n" "Karpeta unitate eramangarri batean badago konekta ezazu, mesedez." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Itxaroten segundo %s ." msgstr[1] "Itxaroten %s segundo." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Huts egin du {snapshot_id} babeskopia egiten." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Amaitzen" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Ezin da karpeta sortu" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Gorde ezarpenen fitxategia…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Baimenak gordetzen…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "'{snapshot_id}' jarraitu daitekeen soberakina aurkitu du." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Azken exekuziotik '{snapshot_id}' karpeta soberakina kentzen" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Ezin da karpeta ezabatu" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Babeskopia egiten" #: common/snapshots.py:1417 msgid "Success" msgstr "Ongi" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Transferentzia partziala errorea dela eta" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Transferentzia partziala desagertutako iturburu-fitxategiengatik (ikus 'man " "rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "\"rsync\" amaitu da irteera-kode honekin {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Ikus 'man rsync' xehetasun gehiagorako" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Rsync irteera kode negatiboak seinale zenbakiak dira, ikusi 'kill -l' eta " "'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Ez dago aldaketarik. Ez da babeskopia berririk egin behar" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Ezin jarri {new_path} izena honi {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Ezabatze adimenduna" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Babeskopia zaharrak ezabatzen" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Saiatu espazio minimoa mantentzen" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Saiatu gutxienez {perc} nodo libre mantentzen" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Orain" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Ezin da {sshfs} muntatu" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent ez da aurkitu. Ziurtatu instalatuta dagoela." #: common/sshtools.py:444 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." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "{cipher} zifraketak huts egin du {host}rentzat." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Urrutiko bide-izena badago baina ez da direktorio bat." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Urrutiko bide-izenak ez dauka idazteko baimenik." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Urrutiko bide-izena ez da exekutagarria." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Ezin da sortu urrutiko bide-izena." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Urruneko {host} ostalariak ez du {command} onartzen" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Begiratu 'man backintime' azalpenak ikusteko" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "{host} ostalariaren egiaztatzeko komandoek errore ezezaguna itzuli dute" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "{host} urrutiko ostalariak ez ditu esteka gogorrak onartzen" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Kopiatu \"{pubkey}\" ssh-gako publikoa \"{host}\" urrutiko ostalarira." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Sartu \"{user}\"-entzako pasahitza bat." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Honi buruz" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Egileak" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Itzulpenak" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Lizentzia" #: qt/app.py:169 msgid "Shortcuts" msgstr "Lasterbideak" #: qt/app.py:189 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:256 msgid "Add to Include" msgstr "Gehitu sartu beharrekoetan" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Gehitu kanpoan utzi beharrekoetan" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} lehen aldiz exekutatzen ari dela dirudi, ez baita konfiguraziorik" " aurkitzen." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Lehendik dagoen konfigurazio bat inportatu (babeskopia batetik edo beste " "ordenagailu batetik)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Ezin da aurkitu irudien karpeta." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "Unitate eramangarri batean badago konekta ezazu eta sakatu Ados." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Egin babeskopia" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Erabili denbora eta tamaina aldaketak fitxategi-aldaketak hautemateko." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Egin babeskopia (kontrol baturarekin)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Erabili kontrol-batura aldaketak detektatzeko." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pausatu babeskopia prozesua" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Laburtu babeskopia prozesua" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Gelditu babeskopia prozesua" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Freskatu babeskopien zerrenda" #: qt/app.py:508 msgid "Name snapshot" msgstr "Izendatu babeskopia" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Kendu babeskopia" #: qt/app.py:516 msgid "View snapshot log" msgstr "Ikusi babeskopiaren erregistroa" #: qt/app.py:520 msgid "View last log" msgstr "Ikusi azken erregistroa" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Kudeatu profilak…" #: qt/app.py:528 msgid "Shutdown" msgstr "Itzali" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Itzali sistema babeskopia bukatu ondoren." #: qt/app.py:532 msgid "Setup language…" msgstr "Konfiguratu hizkuntza…" #: qt/app.py:536 msgid "Exit" msgstr "Irten" #: qt/app.py:540 msgid "Help" msgstr "Laguntza" #: qt/app.py:544 msgid "Profiles config file" msgstr "Profilen konfigurazio fitxategia" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Webgunea" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Aldaketen egunkaria" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ" #: qt/app.py:556 msgid "Ask a question" msgstr "Zerbait galdetu" #: qt/app.py:559 msgid "Report a bug" msgstr "Akats baten berri eman" #: qt/app.py:562 msgid "Translation" msgstr "Itzulpena" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Itzulpenean parte hartzeari buruzko mezua erakusten du berriro." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Enkriptazio-trantsizioa (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "EncFS kentzeari buruzko mezua erakusten du berriro." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Berreskuratu" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Leheneratu hautatutako fitxategiak eta karpetak jatorrizko kokalekura." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Leheneratu hona …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Leheneratu hautatutako fitxategiak eta karpetak kokaleku berrira." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Leheneratu erakutsitako karpeta eta bere eduki guztia jatorrizko kokalekura." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Leheneratu erakutsitako karpeta eta bere eduki guztia kokaleku berrira." #: qt/app.py:595 msgid "Up" msgstr "Gora" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Erakutsi ezkututko fitxategiak" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Konparatu babeskopiak…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Babeskopia" #: qt/app.py:676 msgid "&Restore" msgstr "&Leheneratu" #: qt/app.py:682 msgid "&Help" msgstr "L&aguntza" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Leiho hau ixten baduzu Back In Time-k ezin izango du zure sistema itzali " "babeskopia bukatzean." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Ziur zaude itxi nahi duzula?" #: qt/app.py:987 msgid "Working:" msgstr "Lanean:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Egina, ez da babeskopia egin behar" #: qt/app.py:1044 msgid "Working" msgstr "Lanean" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Errorea" #: qt/app.py:1076 msgid "Sent" msgstr "Bidalita" #: qt/app.py:1077 msgid "Speed" msgstr "Abiadura" #: qt/app.py:1078 msgid "ETA" msgstr "ETA" #: qt/app.py:1140 msgid "Global" msgstr "Orokorra" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Hasiera" #: qt/app.py:1170 msgid "Backup folders" msgstr "Babeskopia-karpetak" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Babeskopiaren izena" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Seguru zaude babeskopia ezabatu nahi duzula?" msgstr[1] "Seguru zaude babeskopiak ezabatu nahi dituzula?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Egin babeskopia bukaerako '{suffix}'\n" "fitxategi lokalak gainidatzi edo ezabatu aurretik." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Berriagoak diren fitxategiak berrizendatuko dira '{suffix}' kokapenarekin " "leheneratu baino lehen. Ez badituzu gehiago behar ezabatu ditzakezu " "'{cmd}'-rekin" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Leheneratu soilik falta diren fitxategiak edo\n" "helburuko tokikoak baino berriagoak direnak.\n" "\"rsync --update\" aukera erabiliz." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Ezabatu jatorrizko karpetako fitxategirik berrienak." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Leheneratu hautatutako fitxategiak edo karpetak jatorrizko kokalekura eta ezabatu babeskopian ez dauden fitxategi edo karpetak. Argi ibili, honek babeskopia egitean baztertutako fitxategi\n" "eta karpetak ezabatuko ditu." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Seguru zaude fitxategi hau leheneratu\n" "nahi duzula '{path}' karpetan?" msgstr[1] "" "Seguru zaude fitxategi hauek leheneratu\n" "nahi dituzula '{path}' karpetan?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Ziur zaude fitxategi hau berreskuratu nahi duzula?" msgstr[1] "Ziur zaude fitxategi horiek berreskuratu nahi dituzula?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Ziur {path}-ko fitxategi berri guztiak kendu nahi dituzula?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Seguru zaude ezabatu nahi duzula jatorrizko karpetatik berriagoak diren " "fitxategi guztiak?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}KONTUZ{BOLDEND}: fitxategi-sistemaren erroko fitxategiak ezabatzeak " "sistema osoa hondatu dezake!" #: qt/app.py:1750 msgid "Snapshot" msgstr "Babeskopia" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Berreskuratu {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Leheneratu {path} hona…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Hizkuntza-ezarpenek Back In Time berrabiarazi ondoren bakarrik izango dute " "eragina." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "EncFSrako laguntza eten egingo da etorkizun hurbilean. Ez da gomendagarria " "modu hori erabiltzea profil baterako." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Zifratutako babeskopien laguntza jarraituaren ordezko erabakia oraindik zain" " dago, proiektuaren baliabideen eta laguntzaileen jarreraren arabera. " "Xehetasun gehiago eskuragarri daude {whitepaper} honetan." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "orri zuria" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Enkriptatutako irudi-profilen euskarria aldaketa nabarmenak jasaten ari " "dira, eta etorkizun hurbilean EncFS kenduko da." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Profil hau(ek) EncFS enkriptatzea erabiltzen dute:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Zifratutako babeskopien laguntza jarraituaren ordezko erabakia oraindik zain" " dago, proiektuaren baliabideen eta laguntzaileen erabilgarritasunaren " "arabera. Erabiltzaileak eztabaida honetan parte hartzera gonbidatuta daude. " "Hurrengo urratsei buruzko xehetasun eguneratuak eskuragarri daude liburu " "zuri honetan {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Mezu hau ez da berriro erakutsiko. Elkarrizketa hau edozein unetan " "eskuragarri dago laguntza menuaren bidez." #: qt/encfsmsgbox.py:87 #, fuzzy msgid "Your Back In Time Team" msgstr "Back In &Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Konfiguratu hizkuntza" #: qt/languagedialog.py:92 msgid "System default" msgstr "Sistemak lehenetsia" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Erabili sistema eragileen hizkuntza." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Itzulia: {percent}" #: qt/languagedialog.py:188 #, 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 "" "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/languagedialog.py:217 msgid "translation platform" msgstr "Itzulpenetarako plataforma" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Zure Itzulpena" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Azken erregistroaren ikuspegia" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Babeskopiaren erregistro ikuspegia" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profila:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Babeskopiak:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Iragazi:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Guztiak" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Aldaketak" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Akatsak" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informazioa" msgstr[1] "Informazioa" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync transferentzia akatsak (esperimentala)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Errorea, [I] Informazioa, [C] Aldaketa" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "dekodetu bide-izenak" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Galdera" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profila: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Ikusi azken erregistroa" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Hasi {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Lanean…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Bidalita:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Abiadura:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Babeskopiak" #: qt/qttools.py:427 msgid "Today" msgstr "Gaur" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Atzo" #: qt/qttools.py:443 msgid "This week" msgstr "Aste honetan" #: qt/qttools.py:450 msgid "Last week" msgstr "Joan den astean" #: qt/qttools.py:596 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/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Azken kontrola {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Erakutsi erregistro osoa" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH Proxya" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Ostalaria:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Ataka:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Erabiltzailea:" #: qt/settingsdialog.py:120 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 "" "Konektatu xede-ostalarira proxy honen bidez (jauzi-ostalari gisa ere " "ezaguna). Ikus \"-J\" \"ssh\" komandoaren dokumentazioan edo \"ProxyJump\" " "\"ssh_config\" man orrialdean xehetasunetarako." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Kudeatu profilak" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Editatu" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Gehitu" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Kendu" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Orokorra" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modua:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Non gorde babeskopiak" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Karpeta" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH ezarpenak" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Bide-izena:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Zifratu:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Gako pribatua:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Aukeratu lehendik dagoen gako pribatuaren fitxategi bat (normalean " "\"id_rsa\" izenekoa)" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Sortu SSH gako berri bat pasahitzik gabe (ez da onartzen gako pribatuaren " "fitxategi bat dagoeneko hautatuta badago)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Pasahitza" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Gorde pasahitza" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Cron-entzako cache-pasahitza (Segurtasun akatsa: root-ek pasahitza irakur " "dezake)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Aurreratua" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Babeskopiaren bide osoa:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Programaketa" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Ezgaituta" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Abiatze/berrabiaratze guztietan" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "minutu {n}etik behin" msgstr[1] "{n} minututik behin" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Orduro" msgstr[1] "{n} orduro" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "{n} orduero" msgstr[1] "{n} orduero" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Ordu pertsonalizatuak" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Egunero" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Behin eta berriz (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Unitatea konektatzean (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Astero" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Hilero" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Urtero" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Eguna:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Asteguna:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Ordua:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Orduak:" #: qt/settingsdialog.py:521 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." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Bakoitza:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Ordu" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Egun(ak)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "aste" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Hilabete" #: qt/settingsdialog.py:555 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 "" "Exekutatu Back In Time unitatea konektatu bezain pronto (X egunean behin bakarrik).\n" "Zure sudo pasahitza eskatuko zaizu." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Gaitu arazketa-mezuen erregistroa" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" "Arazketa-mailako mezuak idazten ditu sistemaren erregistroan \"--debug\" " "bidez." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Kontuz: diagnostikoetarako soilik erabili aldi baterako, irteera kopuru " "handia sortzen baitu." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Sartu" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "sartu fitxategiak eta karpetak" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Gehitu fitxategia" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Gehitu karpeta" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Kanpoan utzi" #: qt/settingsdialog.py:625 #, fuzzy, 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 "" "'SSH enkriptatutako' moduan, izartxo bakunak edo bikoitzak baino ez dute " "funtzionatzen (adibidez, {example2}). Beste komodin eta ereduei ez ikusi " "egingo zaie (adibidez, {example1}). Fitxategi-izenak ez dira aurreikusten " "modu honetan, EncFS-k enkriptatutakoaren ondorioz." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Baztertu txantiloi, fitxategi edo karpeta hauek" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Gehitu lehenetsia" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Utzi kanpoan hau baino handiagoak diren fitxategiak:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Utzi kanpoan {size_unit} baino handiagoak diren fitxategiak:" #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "'rsync osoko modua' desgaituta badago horrek fitxategi berriei soilik " "eragingo die zeren eta rsync-entzat hori transferentzia aukera bat da, ez " "baztertze aukera bat. Beraz babeskopia lehendik egina zituzten fitxategi " "handiak mantenduko dira irudian nahiz eta aldaketak jasan." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Ezabaketa automatikoa" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Hau baino zaharragoa:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "urtez" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Toki librea hau baino txikiagoa bada:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Baldin eta nodo libreak hauek baino gutxiago badira:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Ezabatze adimendua:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Exekutatu urrutiko ostalariaren atzeko planoan." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "ESPERIMENTALA" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Mantendu babeskopia guztiak" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "eguna(k)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Mantendu eguneko babeskopia bana azkenerako" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Mantendu asteko babeskopia bana azkenerako" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "astea(k)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Mantendu hilabeteko babeskopia bana azkenerako" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "hilabeta(k)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Mantendu babeskopia bat urteko urte guztietan." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ez ezabatu izena duten babeskopiak." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Aukerak" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Gaitu notifikazioak" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Ezgaitu babeskopiak egitea bateriaz funtzionatzean" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Energia egoera ez dago eskurtagarri sistemarentzat" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Egin babeskopia bat aldiko" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Beste babeskopiak blokeatuko dira oraingo babeskopia egiten den bitartean. " "Hau aukera globala da, beraz, erabiltzaile honen profil guztiei eragiten " "die. Baina beste erabiltzaileentzat aktibatu behar da ere." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Egin berreskurapenean ordeztutako fitxategien babeskopia" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Berriagoak diren fitxategiak berrizendatuko dira '{suffix}' kokapenarekin " "leheneratu baino lehen. Ez badituzu gehiago behar ezabatu ditzakezu " "'{cmd}'-rekin" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Jarraitu erroreak egon arren (mantendu babeskopia osatu gabea)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Erabili kontrol-batura aldaketak detektatzeko" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Egin babeskopia berria aldaketarik izan ote den kontuan izan gabe." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Erregistro maila:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Ezer Ez" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "A&ukera aurreratuak" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Kontuz: aldatu aukera hauek bakarrik zertan ari zaren badakizu." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Exekutatu 'rsync' '{cmd}'-rekin:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "programatutako ataza bezala" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "urrutiko ostalarian" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "eskuzko babeskopia egitean" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Mesedez, instalatu 'nocache' aukera hau gaitzeko)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "ordenagailuan bertan" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Birzuzendu stdout /dev/null-era cronjob-etan." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Cron-ek automatikoki bidaliko du mezu elektroniko bat cronjob-en irteera " "erantsia MTA instalatuta badago." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Birzuzendu stderr /dev/null-era cronjob-etan." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Cron-ek automatikoki bidaliko du mezu elektroniko bat cronjob-en akatsak " "erantsita MTA instalatuta badago." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Mugatu rsync-en banda-zabaleraren erabilera:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/seg" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Mantendu ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Mantendu atributu hedatuak (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopiatu esteka inseguruak (egin bakarrik esteka absolutuekin)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Mugatu fitxategi-sistema batera" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Aukerak kakotxen artean idatzi, esate baterako {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Itsatsi aukera gehigarriak rsync-eri" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Gehitu aurrizkia SSH komandoei" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" "Urruneko ostalariaren komando bakoitzaren aurretik exekutatzeko aurrizkia." #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Aldagaiak \\$FOO bidez alde egin behar dute. Honek ez dio eragiten rsync-i. " "Beraz rsync erabili dezan aurrizki bat gehitzeko erabili \"{example_value}\"" " {rsync_options_value}-rekin" #: qt/settingsdialog.py:1196 msgid "default" msgstr "lehenetsia" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Markatu urrutiko ostalaria linean badago" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Kontuz: desgaitzen baduzu eta urrutiko ostalaria ezin bada erabili, " "ustekabeko erroreak sor daitezke." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" "Probatu urrutiko ostalariak behar diren komando guztiak onartzen dituen." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Kontuz: desgaitzen baduzu eta urrutiko ostalariak behar diren komando " "guztiak onartzen ez baditu, ustekabeko erroreak sor daitezke ." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Leheneratu konfigurazioa" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Editutatu erabiltzailearen atzeradeia" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "EncFSrako euskarria eten egingo da etorkizun hurbilean. Zifratutako " "babeskopien laguntza jarraituaren ordezko erabakia oraindik zain dago, " "proiektuaren baliabideen eta laguntzaileen jarreraren arabera. Xehetasun " "gehiago eskuragarri daude {whitepaper} honetan." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Profil berria" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Berrizendatu profila" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Seguru zaude \"{name}\" profila ezabatu nahi duzula?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Oso gomendagarria{ENDBOLD}: (Gomendio guztiak dagoeneko sartuta.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Oso gomendagarria{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "Ordu pertsonalizazuak komaz banatutako ordu-zerrenda izan daiteke bakarrik " "(adib. 8,12,18,23) edo */3 hiru orduz behin babeskopia egiteko." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Ez duzu gako pribatuko fitxategirik aukeratu SSHrako." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "Nahi al duzu pasahitzik gabeko gako publiko/pribatu parea sortzea?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "\"{file}\" gako pribatuko fitxategirik ez dago." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Nahi al duzu kopiatzea zure SSH gako publikoa urrutiko ostalarira pasahitzik" " gabe sartu ahal izateko?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Ezin da egiaztatu \"{host}\" ostalariaren benetakotasuna." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} gakoaren hatz-marka hau da:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Egiaztatu hatz-marka! Nahi al duzu bera gehitzea zure 'known_hosts' " "fitxategira?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Baztertu txantiloia" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Baztertu fitxategia" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Baztertu karpeta" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Sartu fitxategia" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" esteka sinboliko bat da. Estekatutako objektuaren babeskopia ez da egingo are eta bera ere gehitu arte.\n" "Nahi duzu gehitu esteka sinbolikoen helburu-objektuak?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Sartu karpeta" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Ziur zaude aldatu nahi duzula babeskopien karpeta?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Huts egin du {path} kokalekuan SSH gako berria sortzen" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" "Desgaituta dago eredu hau ez delako funtzionala \"SSH enkriptatutako\" " "moduan." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(default: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "ezgaituta" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "gaituta" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Inportatu konfigurazioa" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Ez da ezarpenik topatu" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Inportatu" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Hautatu konfigurazio fitxategia inportatu behar den babes-irudien karpeta. " "Bidea honelakoa izan daiteke: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Karpeta kanpoko edo urruneko disko batean badago, eskuz muntatu behar da " "aldez aurretik." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Irudiak alderatzeko aukerak" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Agindua:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametroak:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Erabili %1 eta %2 bide-izenen parametroetarako" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Ezarri diff komando bat edo sakatu Utzi." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "\"{cmd}\" komandoa ezin da aurkitu sistema honetan. Saiatu beste zerbait edo" " sakatu Utzi." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Ez da parametrorik ezarri diff komandorako. \"{params}\" balio lehenetsia " "erabiltzen." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Babeskopia desberdinak soilik" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Zerrendatu bakarri berdinak diren babeskopiak hona: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Egiaztatze sakona (zehaztasun handiagoa, baina motela)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Ezabatu" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Hautatu denak" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Konparatu" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Joan hona" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Aukera" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Ezin duzu alderatu babeskopia bat bere buruarekin." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Ziur zaude \"{snapshot_id}\" babeskopiako \"{file}\" fitxategia ezabatu nahi" " duzula?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" "Ziur zaude {count} babeskopiako \"{file}\" fitxategia ezabatu nahi duzula?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "KONTUZ: Honek ez dauka atzera bueltarik." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Baztertu {path} hurrengo babeskopietan?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Ezin da aurkitu crontab. Seguru zaude crontab instalatuta dagoela? Crontab " #~ "ez baduzu babeskopia automatiko guztiak ezgaitu beharko zenuke." #~ msgid "Full snapshot path" #~ msgstr "Babeskopiaren bide osoa" #~ msgid "Info" #~ msgstr "Informazioa" #~ msgid "Mode" #~ msgstr "Modua" #~ msgid "Profile" #~ msgstr "Profila" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "'{profile}' profila: Sartu pasahitza honentzat {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "" #~ "Erabiltzalearen atzera-deiaren scriptaren shebang ez da exekutagarria." #~ msgid "WARNING" #~ msgstr "ABISUA" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs-ren 1.7.2 bertsioak eta honen aurrekoek badute akats bat --reverse " #~ "aukeran. Eguneratu encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "" #~ "Erabiltzailearen atzeradeia-scriptak ez dauka shebang (#!/bin/sh) lerrorik." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app}-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." backintime-1.5.2/common/po/fa.po000066400000000000000000001717731465446530500165150ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the Back In Time package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: Back In Time 1.3.4-dev\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-30 08:15+0000\n" "Last-Translator: Arash Pourhabibi \n" "Language-Team: Persian \n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.6.2\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "هشدار" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "نمایه اصلی" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "محلی (رمزگذاری EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (رمزنگاری EncFS)" #: common/config.py:309 msgid "Local" msgstr "محلی" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "کلید خصوصی SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "رمزگذاری محلی شده" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "رمزگذاری" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH رمزگذاری شده" #: common/config.py:327 msgid "Default" msgstr "پیش‌فرض" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "نمایه: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "پوشه برگرفت‌ها معتبر نیست!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "برای پشتیبان‌گیری باید حداقل یک پوشه را انتخاب کنید!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "پوشه پشتیبان را نمی‌توان اضافه کرد." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "زیر پوشه پشتیبان را نمی‌توان اضافه کرد." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "گزینه نادرست. {path} یک پوشه نیست." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "میزبان/کاربر/نشانه-رخ‌نما نباید خالی باشد." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "نمی‌توان نوشت در: {path}\n" "آیا اطمینان دارید که مجوز نوشتن را دارید؟" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "مقصد فایل سیستم برای {path} با قالبFAT فرمت شده است که لینک‌های سخت را " "پشتیبانی نمیکند. لطفاً از یک فایل سیستم بومی لینوکس استفاده کنید." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "فایل سیستم مقصد {path} یک share مانت شده SMB است. لطفاً مطمئن شوید سرور SMB " "ریموت، سیم‌لینک ها را پشتیبانی میکند یا {copyLinks} را در {expertOptions} " "فعال کنید." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "کپی کردن لینک ها(لینک‌های نمادین متفاوت)" #: common/config.py:497 msgid "Expert Options" msgstr "گزینه های حرفه‌ای" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "سیستم فایل مقصد برای {path} یک اشتراک نصب شده توسط sshfs است. Sshfs از لینک " "های سخت پشتیبانی نمی کند. لطفاً به جای آن از حالت \"SSH\" استفاده کنید." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "نوشتن کرونتب جدید شکست خورد." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron با وجود در دسترس بودن دستور crontab اجرا نمی شود. کارهای پشتیبان برنامه" " ریزی شده اجرا نمی شوند. Cron ممکن است نصب شده باشد اما فعال نباشد. دستور " "\"systemctl enable cron\" را امتحان کنید یا با پشتیبانی توزیع گنو/لینوکس خود" " مشورت کنید." #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "برنامه‌ریزی udev با حالت {mode} کار نمی‌کند" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "UUID برای \"{path}\" پیدا نشد" #: common/configfile.py:107 msgid "Failed to save config" msgstr "ذخیره پیکربندی شکست خورد" #: common/configfile.py:143 msgid "Failed to load config" msgstr "بارگذاری پیکربندی شکست خورد" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "نمایه \"{name}\" از قبل وجود دارد." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "آخرین نمایه را نمیتوان پاک کرد." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "نمی‌توان '{command}' را مانت کرد" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "پیکربندی برای پوشه رمزنگاری شده پیدا نشد." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "میخوای یک پوشه رمزنگاری شده جدید بسازی؟ یا نه؟" #: common/encfstools.py:151 msgid "Cancel" msgstr "لغو" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "لطفاً رمز عبور را تایید کنید." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "کلمه‌عبور مطابقت ندارد." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "گرفتن اسنپ‌شات" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "نمی‌توان {mountprocess} را از {mountpoint} جدا کرد." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "{command} پیدا نشد، لطفا آن را نصب کنید (مثلا با \"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "مونت‌پوینت ‮{mntpoint} خالی نیست." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "شکست خورد" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "بازگردانی دسترسی ها" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "تمام" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "به تعویق انداختن پشتیبان گیری در هنگام باتری" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "نمیتوان پوشه اسنپ‌شات را پیدا کرد.\n" "اگر در یک درایو جداشدنی قرار دارد، لطفاً آن را وصل کنید." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "برای %s ثانیه باید منتظر ماند." msgstr[1] "صبر برای %s ثانیه." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "گرفتن اسنپ‌شات {snapshot_id} شکست خورد." #: common/snapshots.py:937 msgid "Finalizing" msgstr "درحال نهایی کردن" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "نمی‌توان پوشه ساخت" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "درحال ذخیره فایل کانفیگ…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "در حال ذخیره دسترسی‌ها…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "{snapshot_id} رها شده‌ای پیدا شد که میتوان ادامه داد." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "حذف کردن پوشه {snapshot_id} رها شده‌ای از آخرین اجرا" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "نمیتوان پوشه را حذف کرد" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "در حال گرفتن اسنپ‌شات" #: common/snapshots.py:1417 msgid "Success" msgstr "موفق" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "انتقال جزئی به دلیل خطا" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "انتقال جزئی به دلیل فایل‌های منبع ناپدید شده (به \"man rsync\" مراجعه کنید)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' با کد خروج {exit_code} به پایان رسید" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "برای جزئیات بیشتر به \"man rsync\" مراجعه کنید" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "کدهای خروجی منفی rsync شماره سیگنال هستند، به \"kill -l\" و \"man kill\" " "مراجعه کنید" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "چیزی تغییر نیافت، اسنپ‌شات ضروری جدیدی نیست" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "نمی‌توان {new_path} را به {path} تغییر نام داد" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "حذف هوشمند" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "درحال پاک کردن اسنپ‌شات های قدیمی" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "درحال تلاش برای نگهداری حداقل فضای آزاد" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "در حال تلاش برای نگهداری حداقل {perc} آزاد inodeها" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "الان" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "نمی‌توان {sshfs} را مانت کرد" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent پیدا نشد. لطفا از نصب بودن آن مطمئن شوید." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "کلید خصوصی پوستهٔ امن باز نشد. رمز عبور اشتباه است یا این‌که برای کرون " "رمزعبور موجود نیست." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "متن رمز {cipher} برای {host} شکست خورد." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "مسیر ریموت وجود دارد اما یک دایرکتوری نیست." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "مسیر ریموت قابل نوشتن نیست." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "مسیر ریموت قایل اجرا نیست." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "نمی‌توان پوشه ریموت ساخت." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "مسیر ریموت {host} دستور {command} را پشتیبانی نمیکند" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "برای دستورالعمل های بیشتر به \"man backintime\" مراجعه کنید" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "بررسی دستورات روی میزبان {host} خطای ناشناخته ای را نشان داد" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "میزبان ریموت {host} از هاردلینک‌ها پشتیبانی نمی کند" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "کلید عمومی پوستهٔ امن \"{pubkey}\" را به هاست ریموت \"{host}\" کپی کنید" #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "لطفاً رمز عبور را برای \"{user}\" وارد کنید." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "درباره" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "نویسندگان" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "مترجم‌ها" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "پروانه" #: qt/app.py:169 msgid "Shortcuts" msgstr "میانبرها" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "این پوشه در\n" "اسنپ‌شات انتخاب شده فعلی وجود ندارد." #: qt/app.py:256 msgid "Add to Include" msgstr "اضافه کردن به شامل‌ها" #: qt/app.py:258 msgid "Add to Exclude" msgstr "اضافه کردن به استثناها" #: qt/app.py:343 #, fuzzy, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "به نظر می‌رسد که {app_name} برای اولین بار اجرا می‌شود زیرا هیچ پیکربندی‌ای " "یافت نشد." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "نمی‌توان پوشهٔ برگرفت‌ها را پیدا کرد." #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "اگر روی درایو قابل جابجایی است، لطفاً آن را وصل کنید و سپس باشه را فشار " "دهید." #: qt/app.py:481 msgid "Take a snapshot" msgstr "گرفتن اسنپ‌شات" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "از زمان و اندازه اصلاح برای تشخیص تغییر فایل استفاده کنید." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "گرفتن اسنپ‌شات (حالت چک‌سام)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "از چک‌سام برای شناسایی تغییرات استفاده کن." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "توقف پروسه اسنپ‌شات" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "ادامه پروسه اسنپ‌شات" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "لغو پروسه اسنپ‌شات" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "تازه‌سازی فهرست اسنپ‌شات" #: qt/app.py:508 msgid "Name snapshot" msgstr "نام‌بردن اسنپ‌شات" #: qt/app.py:512 msgid "Remove snapshot" msgstr "حذف اسنپ‌شات" #: qt/app.py:516 msgid "View snapshot log" msgstr "مشاهده لاگ اسنپ‌شات" #: qt/app.py:520 msgid "View last log" msgstr "مشاهده آخرین لاگ" #: qt/app.py:524 msgid "Manage profiles…" msgstr "مدیریت نمایه‌ها…" #: qt/app.py:528 msgid "Shutdown" msgstr "خاموش" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "بعد از تمام شدن اسنپ‌شات سیستم را خاموش کن." #: qt/app.py:532 msgid "Setup language…" msgstr "زبان راه‌اندازی…" #: qt/app.py:536 msgid "Exit" msgstr "خروج" #: qt/app.py:540 msgid "Help" msgstr "کمک" #: qt/app.py:544 msgid "Profiles config file" msgstr "فایل پیکربندی نمایه‌ها" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "وبسایت" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "لاگ تغییرات" #: qt/app.py:553 msgid "FAQ" msgstr "سوالات متداول" #: qt/app.py:556 msgid "Ask a question" msgstr "سوال بپرس" #: qt/app.py:559 msgid "Report a bug" msgstr "گزارش باگ" #: qt/app.py:562 msgid "Translation" msgstr "ترجمه" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "دوباره پیام درمورد مشارکت در ترجمه را نشان می‌دهد." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 #, fuzzy msgid "Shows the message about EncFS removal again." msgstr "دوباره پیام در مورد EncFS removal را نشان می‌دهد." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "بازگردانی" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "فایل ها یا پوشه های انتخاب شده را به مقصد اصلی بازگردان." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "بازگردانی به …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "فایل ها یا پوشه های انتخاب شده را به محل جدید بازگردان." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "پوشه نمایش داده شده فعلی و تمام محتویات آن را به مقصد اصلی بازگردانید." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "پوشه نمایش داده شده فعلی و تمام محتویات آن را به یک مقصد جدید بازیابی کنید." #: qt/app.py:595 msgid "Up" msgstr "بالا" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "نمایش فایل های مخفی" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "مقایسه اسنپ‌شات‌ها…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "&پشتیبان‌گیری" #: qt/app.py:676 msgid "&Restore" msgstr "&بازگردانی" #: qt/app.py:682 msgid "&Help" msgstr "&کمک" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "اگر شما این پنجره را ببندید، Back In Time دیگر قادر نیست که رایانه شما را بعد از تمام شدن اسنپ‌شات خاموش کند.\n" "می‌خواهید واقعاً ببندید؟" #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "آیا از بستن این مورد مطمئنید؟" #: qt/app.py:987 msgid "Working:" msgstr "در حال کار کردن:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "تامام، پشتیبان‌گیری لازم نیست" #: qt/app.py:1044 msgid "Working" msgstr "درحال کار" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "خطا" #: qt/app.py:1076 msgid "Sent" msgstr "فرستاده‌شده" #: qt/app.py:1077 msgid "Speed" msgstr "سرعت" #: qt/app.py:1078 msgid "ETA" msgstr "ETA" #: qt/app.py:1140 msgid "Global" msgstr "جهانی" #: qt/app.py:1141 msgid "Root" msgstr "ریشه" #: qt/app.py:1142 msgid "Home" msgstr "خانه" #: qt/app.py:1170 msgid "Backup folders" msgstr "پوشه های پشتیبانی" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "نام اسنپ‌شات" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "آیا از پاک کرد این اسنپ‌شات مطمئنید؟" msgstr[1] "آیا از پاک کردن این اسنپ‌شات‌ها مطمئنید؟" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "پشتیبان‌هایی با پسوند {suffix} را\n" "قبل از بازنوشتن یا حذف کردن عناصر محلی ایجاد کن." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "نسخه‌های جدیدتر از فایل‌ها با دنباله {suffix} قبل از بازگردانی تغییر نام داده خواهند شد.\n" "اگر دیگر به آن احتیاجی ندارید، میتوانید آن را با دستور زیر حذف کنید." #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "تنها عناصری که وجود ندارند\n" "یا جدید‌تر از معادلشان در مقسد هستند را بازیابی کن.\n" "با استفاده از قابلیت \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "عناصر جدیدتر را در پوشه اصلی حذف کنید." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "پرونده‌ها یا پوشه‌های انتخاب شده را به محل اصلی بازمی‌گرداند و\n" "پرونده‌ها و پوشه‌هایی که در رخ‌نما‌ها نیستند را حذف می‌کند.\n" "شدیداً مواظب باشید؛ زیرا این کار پرونده‌ها و پوشه‌هایی که هنگام رخ‌نما گرفتن مورد استثنا قرار گرفته شده‌اند را حذف خواهد کرد." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "آیا از بازگرداندن این مورد به شاخه جدید زیر مطمئنید\n" "{path}?" msgstr[1] "" "آیا از بازگرداندن این موارد به شاخه جدید زیر مطمئنید\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "آیا از بازگرداندن این مورد مطمئنید؟" msgstr[1] "آیا از بازگرداندن این موارد مطمئنید؟" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "آیا شما مطمئنید که میخواهید تمامی فایل های جدیدتر را در {path} حذف کنید؟" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "آیا شما مطمئنید که میخواهید تمامی فایل‌های جدیدتر را در پوشه اصلی خود حذف " "کنید؟" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}هشدار{BOLDEND}: حذف کردن پرونده‌ها در ریشهٔ سامانه‌پرونده می‌تواند کل " "سامانهٔ شما را خراب کند!" #: qt/app.py:1750 msgid "Snapshot" msgstr "اسنپ‌شات" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "بازگرداندن {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "بازگرداندن {path} به …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "تنظیمات زمان تنها بعد از خارج و وارد شدن از Back In Time تاثیر گذار هستند." #: qt/encfsmsgbox.py:42 #, fuzzy msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "پشتیبانی از EncFS در آینده پیش‌بینی شده پایان می‌یابد. علاوه بر این پیشنهاد " "نمی‌شود دیگر از این حالت برای یک پروفایل استفاده کنید." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "اوراق سفید" #: qt/encfsmsgbox.py:66 #, fuzzy msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "پشتیبانی از پروفایل‌های رخ‌نماهای رمزگذاری شده در حالت تغییرات زیادی است، و " "EncFS در آینده قابل پیش‌بینی حذف خواهد شد." #: qt/encfsmsgbox.py:75 #, fuzzy msgid "The following profile(s) use encryption with EncFS:" msgstr "پروفایل (های) زیر از رمزگذاری با EncFS استفاده می‌کنند:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 #, fuzzy msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "این پیام دیگر نشان داده نخواهد شد. این دیالوگ در هر زمانی از طریق منوی " "راهنما قابل دستیابی است." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "انتخاب زبان" #: qt/languagedialog.py:92 msgid "System default" msgstr "پیش‌فرض سیستم" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "استفاده از زبان سیستم عامل." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "ترجمه شده: {percent}" #: qt/languagedialog.py:188 #, 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 و در نتیجه خود Back In Time مشارکت کنید. لطفا {translation_platform_url} را در صورت علاقه‌مندی به مشارکت ببینید. برای کمک و مشارکت بیش‌تر، لطفا {back_in_time_project_website} را ببینید.\n" "از نگه‌داشتن شما عذرخواهی می‌کنیم. این پیام دوباره نمایش داده نمی‌شود. این پیام در هر زمان از طریق فهرست راهنمایی قابل دسترس است.\n" "تیم Back In Time برای شما." #: qt/languagedialog.py:217 msgid "translation platform" msgstr "سکوی ترجمه" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "ترجمه شما" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "آخرین نمای لاگ" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "نمای لاگ اسنپ‌شات" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "نمایه:" #: qt/logviewdialog.py:84 #, fuzzy msgid "Snapshots:" msgstr "اسنپ‌شات‌ها:" #: qt/logviewdialog.py:99 #, fuzzy msgid "Filter:" msgstr "فیلتر:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "همه" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "تغییرات" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "خطاها" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "اطلاعات" msgstr[1] "اطلاعات" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "خطا‌های انتقال rsync‌ (آزمایشی)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] خطا، [I] اطلاعات، [C] تغییرات" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "رمزگشایی مسیرها" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "سوال" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "نمایه: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "آخرین لاگ را ببین" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "اجرای {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "درحال کار…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "فرستاده‌شده:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "سرعت:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "اسنپ‌شات‌ها" #: qt/qttools.py:427 msgid "Today" msgstr "امروز" #: qt/qttools.py:434 msgid "Yesterday" msgstr "دیروز" #: qt/qttools.py:443 msgid "This week" msgstr "این هفته" #: qt/qttools.py:450 msgid "Last week" msgstr "آخرین هفته" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "این یک اسنپ‌شات نیست ولی یک نمای زنده از پرونده‌های محلی شماست" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "آخرین بررسی {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "لاگ را کامل نشان بده" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "میزبان(هاست):" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "پورت:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "کاربر:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "مدیریت نمایه‌ها" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "ویرایش" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "اضافه" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "حذف" #: qt/settingsdialog.py:210 msgid "&General" msgstr "کلی" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "حالت:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "کجا اسنپ‌شات‌ها ذخیره شود" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "پوشه" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "تنظیمات SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "مسیر:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "متن رمز:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "کلید خصوصی:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "یک فایل کلید خصوصی که وجود داره انتخاب کنید (به طور معمول \"id_rsa\" " "نامگذاری شده)" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "یک کلید SSH جدید بدون رمز عبور بساز (مجاز نیست اگر یک فایل کلید خصوصی درحال " "حاضر انتخاب شده باشد)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "رمز عبور" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "رمز عبور را در دسته کلید ذخیره کنید" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "رمز عبور کش برای کرون (مشکل امنیتی: ریشه می‌تواند رمز عبور را بخواند)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "پیشرفته" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "مسیر کامل اسنپ‌شات:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "برنامه‌ریزی" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "غیرفعال" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "در هر بوت/ریبوت" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "هر {n} دقیقه" msgstr[1] "هر {n} دقیقه" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "هر ساعت" msgstr[1] "هر {n} ساعت" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "هر {n} ساعت" msgstr[1] "هر {n} ساعت" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "ساعت‌های سفارشی" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "هر روز" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "مکرر (آناکرون)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "وقتی درایو متصل شد (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "هر هفته" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "هر ماه" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "هر سال" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "روز هفته:" #: qt/settingsdialog.py:495 #, fuzzy msgid "Hour:" msgstr "ساعت‌" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "ساعت‌:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Back In Time را مکرر اجرا کن. این زمانی کاربردی است که رایانه معمولاً روشن " "نیست." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "هر:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "ساعت(ها)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "روز" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "هفته" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "ماه" #: qt/settingsdialog.py:555 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 "" "Back In Time را تا وقتی که درایو متصل است اجرا کن (فقط یکبار در هر X روز)\n" "از شما رمز عبور sudo پرسیده خواهد شد." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "شامل" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "فایل‌ها و پوشه‌ها را شامل کن" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "اضافه کردن فایل" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "اضافه کردن پوشه" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "استثنا" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "الگوها، فایل‌ها یا پوشه‌ها را استثنا کن" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "اضافه کردن پیش‌فرض" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "استثنا قرار بده فایل های بزرگتر از:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "استثنا قرار بده فایل های بزرگتر از: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "پرونده‌های بزرگ‌تر از مقدار در %(prefix)s را استثنا قرار بده.\n" "در «حالت rsync کامل» غیرفعال، این کار فقط روی پرونده‌های جدید اثر می‌گذارد.\n" "به دلیل rsync، این یک گزینه منتقل‌کننده است، نه یک گزینه مستثنی‌کننده.\n" "به این خاطر، پرونده‌های بزرگی که قبلاً پشتیبان‌گیری شده بودند در اسنپ‌شات‌ها باقی می‌مانند\n" "حتی اگر تغییر کرده باشند." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "حذف خودکار" #: qt/settingsdialog.py:726 #, fuzzy msgid "Older than:" msgstr "قدیمی‌تر از" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "سال(ها)" #: qt/settingsdialog.py:748 #, fuzzy msgid "If free space is less than:" msgstr "اگر هست حافظه آزاد کمتر از" #: qt/settingsdialog.py:768 #, fuzzy msgid "If free inodes is less than:" msgstr "اگر هست آی‌نود های آزاد کمتر از" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "حذف هوشمند:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "در پس‌زمینه برروی میزبان راه‌دور اجرا کن." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "تجربی" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "تمامی اسنپ‌شات ها را تا آخر نگهدار" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "روز‌(ها)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "یک اسنپ‌شات را در روز تا آخر نگهدار" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "یک اسنپ‌شات را در هفته تا آخر نگهدار" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "هفته(ها)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "یک اسنپ‌شات را در ماه تا آخر نگهدار" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "ماه(ها)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "هر سال یک اسنپ‌شات برای تمام سال‌ها نگه دارید." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "اسنپ‌شات‌های نامگذاری شده را حذف نکن." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&گزینه‌ها" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "فعال کردن اعلان‌ها" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "غیرفعال کردن اسنپ‌شات‌ها در هنگام باتری" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "وضعیت باتری از طرف سیستم در دسترس نیست" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "فقط یک اسنپ‌شات در زمان اجرا کن" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "دیگر اسنپ‌شات‌ها تا وقتی که اسنپ‌شات فعلی تمام نشده است، مسدود می‌شوند.\n" "این یک گزینه جهانی است. به این خاطر برای تمامی نمایه‌های کاربر اثر می‌گذارد.\n" "ولیکن که همچنین شما نیاز دارید که این را برای همه کاربرها فعال کنید." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "پشتیبان‌گیری از فایل‌های تغییر مکان یافته در هنگام بازگردانی" #: qt/settingsdialog.py:879 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "نسخه‌های جدیدتر از فایل‌ها با دنباله {suffix} قبل از بازگردانی تغییر نام داده خواهند شد.\n" "اگر دیگر به آن احتیاجی ندارید، میتوانید آن را با {cmd} حذف کنید" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "ادامه دادن در هنگام خطا (اسنپ‌شات ناقص میمانند)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "از سرجمع برای شناسایی تغییرات استفاده کن" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "یک اسنپ‌شات جدید بگیر بدون در نظر گرفتن اینکه آیا تغییر داشته یا خیر." #: qt/settingsdialog.py:906 #, fuzzy msgid "Log Level:" msgstr "سطح لاگ" #: qt/settingsdialog.py:911 msgid "None" msgstr "هیچکدام" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "گ&زینه‌های حرفه‌ای" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "احتیاط: این گزینه ها را اگر میدانی که داری چه کار میکنی تغییر بده." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "'rsunc' را با '{cmd}' اجرا کن:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "به عنوان کار کرون" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "برروی هاست ریموت" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "وقتی که درحال گرفتن اسنپ‌شات دستی است" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(لطفاً 'nocache' را نصب کنید تا این گزینه فعال شود)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "برروی ماشین محلی" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "stdout را به /dev/null در کار‌های کرون منتقل کنید." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "stderr را به /dev/null در کار‌های کرون منتقل کنید." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "استفاده پهنای باند rsync را محدود کن" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/ثانیه" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "حفظ ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "حفظ ویژگی‌های گسترده (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "کپی پیوندهای ناامن (فقط با پیوندهای مطلق کار میکند)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "گزینه‌ها باید بین نویسه‌های نقل‌قول قرار بگیرند مثل {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "پیست گزینه های اضافه به rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "اضافه کردن پیشوند به دستورات SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "پیشوندی برای اجرا قبل از هر دستور برروی هاست ریموت.\n" "متغیر ها نیاز دارند که با \\$FOO فرار کنند.\n" "این rsync را لمس نمیکند. پس برای اضافه کردن پیشوند\n" "برای rsync استفاده کن از \"%(cbRsyncOptions)s\" با\n" "%(rsync_options_value)s\n" "\n" "%(default)s:%(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "پیش‌فرض" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "بررسی کن اگر هاست ریموت آنلاین هست" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "هشدار: اگر غیرفعال بود و هاست ریموت\n" "در دسترس نیست، این میتواند باعث\n" "خطاهایی عجیب غریب شود." #: qt/settingsdialog.py:1218 #, fuzzy msgid "Check if remote host supports all necessary commands." msgstr "" "بررسی کن که آیا میزبان راه‌دور از تمامی دستورات ضروری پشتیبانی می‌کند یا خیر" #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "هشدار: اگر غیرفعال بود و هاست ریموت\n" "تمام دستورات ضروری را پشتیبانی نمیکند،\n" "این میتواند باعث خطاهایی عجیب غریب شود." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "بازگردانی پیکربندی" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "ویرایش user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "نمایه جدید" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "تغییر نام نمایه" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "آیا شما واقعا مطمئن هستید که میخواهید نمایه \"{name}\" را پاک کنید؟" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "بسیار پیشنهاد شده است" #: qt/settingsdialog.py:1634 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‬ برای پشتیبان‌گیری دوره‌ای " "هر سه ساعت نوشته شود." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "شما برای SSH یک فایل کلید خصوصی انتخاب نکرده‌اید.\n" "آیا مایل به تمایلات هستید که یک جفت کلید خصوصی/عمومی بدون رمز عبور جدید بسازیم؟" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "فایل کلید خصوصی \"{file}\" وجود ندارد." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "آیا مایل به تمایلات هستید که کلید SSH عمومی خود را کپی کنید به\n" "هاست ریموت تا ورود بدون رمز عبور فعال شود؟" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "اعتباری از هاست {host} نمی‌توان ایجاد کرد.\n" "\n" "کلید اثر انگشت {keytype} هست:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "لطفاً این اثر انگشت را تایید کنید! آیا مایل به تمایلات هستید که این را به " "فایل 'known_hosts' خودتان اضافه کنید؟" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "استثنا کردن الگو" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "استثنا کردن فایل" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "استثنا کردن پوشه" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "شامل کردن فایل" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" یک پیوند نمادین است. هدف پیوندداده‌شده پشتیبان‌گیری نمی‌شود مگر این که شما شاملش کنید.\n" "آیا مایلید که هدف پیوندهای نمادین را نیز شامل کنید؟" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "شامل کردن پوشه" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "آیا مطمئن هستید که می خواهید پوشه اسنپ‌شات را تغییر دهید؟" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "ساختن کلید SSH جدید در {path} شکست خورد" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "پیش‌فرض" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "غیرفعال" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "فعال" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "هیچ پیکربندی‌ پیدا نشد" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "گزینه‌هایی در مورد مقایسه عکس‌های فوری" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "دستور" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters:" msgstr "پارامتر ها" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "از ۱٪ و ۲٪ برای پارامترهای مسیر استفاده کن" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "فقط اسنپ‌شات‌ها را مقایسه کن" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "فقط اسنپ‌شات‌های مساوی را لیست کن به: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "بررسی ژرفناک (بیشتر دقیق، ولی کند)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "پاک کردن" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "انتخاب همه" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "مقایسه" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "برو به" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "گزینه‌ها" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "شما نمی‌توانید یک اسنپ‌شات را با خودش مقایسه کنید." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "آیا شما واقعا می‌خواهید \"{file}\" را در اسنپ‌شات \"{snapshot_id}\" پاک کنید؟" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "آیا واقعا می‌خواهید \"{file}\" را در {count} اسنپ‌شات پاک کنید؟" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "این نمی‌تواند لغو شود!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "می‌خواهید که \"{path}\" را از اسنپ‌شات‌های آینده مستثنی کنید؟" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "نمی‌توان کرونتب را پیدا کرد.\n" #~ "آیا اطمینان دارید که کرون نصب شده است؟\n" #~ "اگر نه، شما بایستی همه پشتیبان های خودکار را غیرفعال کنید." #~ msgid "Full snapshot path" #~ msgstr "مسیر کامل اسنپ‌شات" #~ msgid "Mode" #~ msgstr "حالت" #~ msgid "Profile" #~ msgstr "پروفایل" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "پروفایل '{profile}': رمز عبور را وارد کنید برای {mode}:. " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang در اسکریپت user-callback قابل اجرا نیست." #~ msgid "WARNING" #~ msgstr "هشدار" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs نگارش ۱٫۷٫۲ و قبل از آن دارای یک اشکال با گزینه --reverse است. لطفا " #~ "encfs را به‌روز کنید." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "اسکریپت user-callback هیچ خط shebang ندارد (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} از EncFS برای رمزنگاری استفاده میکند. یک حسابرسی امنیتی اخیر، چندتا " #~ "مسیر‌ ممکن حمله برای این را افشا کرده است. لطفاً نگاهی به \"A NOTE ON " #~ "SECURITY\" در \"man backintime\" بیندازید." backintime-1.5.2/common/po/fi.po000066400000000000000000001460161465446530500165150ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Varoitus" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Pääprofiili" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Paikallinen (EncFS salattu)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (EncFS salattu)" #: common/config.py:309 msgid "Local" msgstr "Paikallinen" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "yksityinen SSH avain" #: common/config.py:314 msgid "Local encrypted" msgstr "Paikallinen salattu" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Salaus" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH salattu" #: common/config.py:327 msgid "Default" msgstr "Oletus" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profiili: ”{name}”" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Otoskansio ei ole kelvollinen!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Valitse varmuuskopioitavaksi vähintään yksi kansio!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Backup -kansiota ei voi sisällyttää." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Backup -alikansiota ei voi sisällyttää." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Väärä arvo. {path} ei ole kansio." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Kone/käyttäjätunnus/profiilitunnus ei voi olla tyhjä." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Tiedostoon ei voida kirjoittaa: {path}\n" "Onko siihen varmasti kirjoitusoikeus?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Kansion ”{path}” kohdetiedostojärjestelmä on FAT, joka ei tue kovia " "linkkejä. Käytä Linuxin natiivia tiedostojärjestelmää." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Kansion ”{path}” kohdetiedostojärjestelmä on liitetty SMB-jako. Varmista, " "että SMB-etäpalvelin tukee symlinkkejä tai ota käyttöö ”{copyLinks}” " "kohdassa ”{expertOptions}”." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopioi linkit (ratkaise symboliset linkit)" #: common/config.py:497 msgid "Expert Options" msgstr "Asiantuntija-asetukset" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Kansion ”{path}” kohdetiedostojärjestelmä on liitetty sshfs-jako. sshfs ei " "tue kovia linkkejä. Käytä tämän sijaan SSH-tilaa." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Uuden crontabin kirjoittaminen epäonnistui." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cronia ei ajeta vaikka crontab-komento on käytettävissä. Ajastettuja " "varmuuskopioita ei tehdä. Cron saattaa olla asennettu mutta ei käytössä. " "Kokeile komentoa \"systemctl enable cron\" tai käänny GNU Linux-jakelusi " "tukikanavien puoleen." #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Udev-aikataulu ei toimi {mode}-tilassa" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Kohteelle ”{path}” ei löytynyt UUID:tä" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Määrityksen tallentaminen epäonnistui" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Määrityksen lataaminen epäonnistui" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profiili ”{name}” on jo olemassa." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Tätä ei voi kumota." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Ei voitu liittää: ”{command}”" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Salatun kansion määritystä ei löytynyt." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Luodaanko uusi salattu kansio?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Peru" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Ole hyvä ja vahvista salasana." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Salasanat eivät täsmää." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Ota otos" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Ei voi vapauttaa {mountprocess} kohteesta {mountpoint}." #: common/mount.py:696 #, 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/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Liitospiste {mntpoint} ei ole tyhjä." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Syötä salasana {mode} profiilille \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "EPÄONNISTUI" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Palauta käyttöoikeudet" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Valmis" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Viivästetään varmuuskopiointia akkukäytön ajan" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Otoskansiota ei löydy.\n" "Jos otoskansio sijaitsee ulkoisella asemalla, liitä asema tietokoneeseen." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Odotetaan %s sekunti." msgstr[1] "Odotetaan %s sekuntia." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Otoksen {snapshot_id} luominen epäonnistui." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Viimeistellään" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Ei voitu luoda kansiota" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Tallennetaan määritystiedostoa…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Tallennetaan käyttöoikeuksia…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Löytyi kesken jäänyt ”{snapshot_id}”, jota voi jatkaa." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Poistetaan viime suorituksessa kesken jäänyt kansio ”{snapshot_id}”" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Ei voida poistaa kansiota" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Otetaan otosta" #: common/snapshots.py:1417 msgid "Success" msgstr "Onnistui" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Osittain siirretty johtuen virheestä" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Osittain siirretty johtuen puuttuvista lähdetiedostoista ( kts. 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' päättyi paluukoodilla {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Katso lisätietoja: 'man rsync'" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Mikään ei ole muuttunut: uusi otos ei ole tarpeen" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Ei voi uudelleennimetä {new_path} nimelle {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Älykäs poisto" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Poistetaan vanhoja otoksia" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Yritetään pitää vapaa vähimmäistila" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Yritetään pitää vähintään {perc} inodeja vapaana" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Nyt" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Ei voida liittää {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" "ssh-agent ohjelmaa ei löytynyt. Ole hyvä ja varmista että se on asennettu." #: common/sshtools.py:444 msgid "" "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/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Salausalgoritmi {cipher} epäonnistui kohteeseen {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Etäsijainti on olemassa muttei kansio." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Etäsijaintiin ei voi kirjoittaa." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Etäsijainti ei ole ohjelmatiedosto." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Etäsijaintia ei voitu luoda." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Etäkone {host} ei tue komentoa {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Lisätietoa: ”man backintime”" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Check-komennot palvelimella {host} antoivat tuntemattoman virheen" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Etäkone {host} ei tue kovia linkkejä" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Kopioi julkinen ssh-avain \"{pubkey}\" palvelimelle \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Ole hyvä ja syötä käyttäjän \"{user}\" salasana." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Tietoa" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Tekijät" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Käännökset" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Lisenssi" #: qt/app.py:169 msgid "Shortcuts" msgstr "Pikanäppäimet" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Tätä kansiota ei ole\n" "valitussa otoksessa." #: qt/app.py:256 msgid "Add to Include" msgstr "Lisää mukaan otettaviin" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Lisää pois jätettäviin" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} näyttää käynnistettävän ensimmäistä kertaa, koska asetuksia ei " "löytynyt." #: qt/app.py:348 #, fuzzy msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Tuo jo olemassa olevat asetukset (varmuuskopiosta tai toiselta " "tietokoneelta)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Otoskansiota ei löytynyt." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "Jos kansio on siirrettävällä asemalla, kytke se ja napsauta OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Ota otos" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Käytä muokkausajankohtaa ja kokoa tiedoston muokkauksen havaitsemiseen." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Ota otos tarkistussummin" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Havaitse muutokset tarkistussummista." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Keskeytä otosprosessi" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Jatka otosprosessia" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Pysäytä otosprosessi" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Virkistä otosluettelo" #: qt/app.py:508 msgid "Name snapshot" msgstr "Ota otos" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Poista otos" #: qt/app.py:516 msgid "View snapshot log" msgstr "Näytä otosloki" #: qt/app.py:520 msgid "View last log" msgstr "Näytä viimeisin loki" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Hallinnoi profiileja…" #: qt/app.py:528 msgid "Shutdown" msgstr "Sammuta" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Sammuta järjestelmän otoksen valmistuttua." #: qt/app.py:532 msgid "Setup language…" msgstr "Asennuskieli…" #: qt/app.py:536 msgid "Exit" msgstr "Lopeta" #: qt/app.py:540 msgid "Help" msgstr "Ohje" #: qt/app.py:544 msgid "Profiles config file" msgstr "Tallennetaan määritystiedostoa" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Sivusto" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Muutosloki" #: qt/app.py:553 msgid "FAQ" msgstr "UKK" #: qt/app.py:556 msgid "Ask a question" msgstr "Esitä kysymys" #: qt/app.py:559 msgid "Report a bug" msgstr "Ilmoita ohjelmavirheestä" #: qt/app.py:562 msgid "Translation" msgstr "Käännökset" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Näytä viesti kääntämiseen osallistumisesta uudelleen." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Näyttää viestin EncFS:n poistosta uudelleen." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Palauta" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Palauta valitut tiedostot tai kansiot alkuperäiseen paikkaansa." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Palauta kohteeseen…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Palauta valitut tiedostot tai kansiot uuteen paikkaan." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "Palauta näytetty kansio kaikkine sisältöineen alkuperäiseen paikkaan." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Palauta näytetty kansio kaikkine sisältöineen uuteen paikkaan." #: qt/app.py:595 msgid "Up" msgstr "Ylös" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Näytä piilotiedostot" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Vertaa otoksia…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Varmuuskopio" #: qt/app.py:676 msgid "&Restore" msgstr "&Palauta" #: qt/app.py:682 msgid "&Help" msgstr "&Ohje" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Jos suljet tämän ikkunan, Back In Time ei voi sammuttaa järjestelmää otoksen" " valmistuttua." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Haluatko varmasti sulkea sen?" #: qt/app.py:987 msgid "Working:" msgstr "Käsitellään:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Valmis, varmuuskopiota ei tarvittu" #: qt/app.py:1044 msgid "Working" msgstr "Käsitellään" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Virhe" #: qt/app.py:1076 msgid "Sent" msgstr "Lähetetty" #: qt/app.py:1077 msgid "Speed" msgstr "Nopeus" #: qt/app.py:1078 msgid "ETA" msgstr "Jäljellä" #: qt/app.py:1140 msgid "Global" msgstr "Yleisasetukset" #: qt/app.py:1141 msgid "Root" msgstr "Juuri" #: qt/app.py:1142 msgid "Home" msgstr "Koti" #: qt/app.py:1170 msgid "Backup folders" msgstr "Varmuuskopiokansiot" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Otoksen nimi" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Oletko varma että haluat poistaa tämän otoksen?" msgstr[1] "Oletko varma että haluat poistaa nämä otokset?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Varmuuskopioi paikalliset tiedostot ennen korvaamista\n" "tai poistamista ”{suffix}”-päätteelle." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Tiedostojen uudempien versioiden nimiin lisätään ”{suffix}” ennen " "palauttamista. Ellei niitä enää tarvita, ne voi poistaa tällä komennolla:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Palauta vain tiedostot, jotka puuttuvat tai\n" "ovat uudempia kuin kohteessa.\n" "Käytetään ”rsync --update” -asetusta." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Poista alkuperäiskansiosta uudemmat elementit." #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Palauta valitut tiedostot tai kansiot alkuperäiseen paikkaan ja poista " "tiedostot tai kansiot, joita otoksessa ei ole. Tämä poistaa tiedostot ja " "kansiot, jotka jätettiin otoksesta pois! Ole hyvin varovainen!" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Haluatko varmasti palauttaa tämän tiedoston uuteen kansioon\n" "{path}?" msgstr[1] "" "Haluatko varmasti palauttaa nämä tiedostot uuteen kansioon\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Haluatko varmasti palauttaa tämän tiedoston?" msgstr[1] "Haluatko varmasti palauttaa nämä tiedostot?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Haluatko varmasti poistaa kansion ”{path}” kaikki uudemmat tiedostot?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Haluatko varmasti poistaa alkuperäisen kansion kaikki uudemmat tiedostot?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}VAROITUS{BOLDEND}: tiedostojen poistaminen tiedostojärjestelmän " "juuresta voi rikkoa järjestelmän!!!" #: qt/app.py:1750 msgid "Snapshot" msgstr "Otokset" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Palauta {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Palauta {path} kohteeseen…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "Kielivalinta astuu voimaan uudelleenkäynnistyksen jälkeen." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Tuki EncFS:älle lopetetaan lähitulevaisuudessa. Sen käyttöä profiilille ei " "enään suositella." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Asennuskieli" #: qt/languagedialog.py:92 msgid "System default" msgstr "Järjestelmän oletus" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Käytä järjestelmän kielivalintaa." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Käännetty: {percent}" #: qt/languagedialog.py:188 #, 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 "" "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/languagedialog.py:217 msgid "translation platform" msgstr "Käännökset" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Käännökset" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Viimeisin lokinäkymä" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Otoslokinäkymä" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profiili:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Otokset:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Suodatin:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Kaikki" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Muutokset" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Virheet" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Tiedot" msgstr[1] "Tiedot" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Virhe, [I] Tietoa, [C] Muutokset" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "dekoodaa polut" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Kysymys" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profiili: ”{profile_name}”" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Näytä viimeisin loki" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Käynnistä {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Käsitellään…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Lähetetty:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Nopeus:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Otokset" #: qt/qttools.py:427 msgid "Today" msgstr "Tänään" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Eilen" #: qt/qttools.py:443 msgid "This week" msgstr "Tällä viikolla" #: qt/qttools.py:450 msgid "Last week" msgstr "Viime viikolla" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Tämä EI ole otos vaan elävä näkymä paikallisiin tiedostoihin" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Viimeksi tarkistettu {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Näytä täysi loki" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH Välipalvelin" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Konenimi:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Portti:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Käyttäjä:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Hallinnoi profiileja" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Muokkaa" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Lisää" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Poista" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Yleisasetukset" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Tila:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Mihin otokset tallennetaan" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Kansio" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH-asetukset" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Sijainti:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Salausmenetelmä:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Yksityinen avain:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Valitse jo olemassa oleva yksityinen avain (löytyy usein nimellä \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Luo uusi SSH avain ilman salasanaa (vain jos yksityistä avainta ei ole jo " "valittu)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Salasana" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Tallenna salasana avainrenkaaseen" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Tallenna salasana välimuistiin cronia varten (tietoturvaongelma: root voi " "lukea salasanan)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Lisäasetukset" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Otoksen täydellinen sijainti:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Aikataulu" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Ei käytössä" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Käynnistysten yhteydessä" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "{n} minuutin välein" msgstr[1] "{n} minuutin välein" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Tunnin välein" msgstr[1] "Joka {n} tunti" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "{n} tunnin välein" msgstr[1] "{n} tunnin välein" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Mukautetut tunnit" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Päivittäin" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Toistuva (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Asemaa kytkettäessä (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Viikoittain" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Kuukausittain" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Joka vuosi" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Päivä:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Viikonpäivä:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Tunti:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Tunnit:" #: qt/settingsdialog.py:521 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:528 msgid "Every:" msgstr "Joka:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Tunti/Tunnit" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Päivä(ä)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Viikko(a)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Kuukausi/Kuukaudet" #: qt/settingsdialog.py:555 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 "" "Suorita BackInTime heti, kun asema on kytketty (vain kerran X päivässä).\n" "Sinulta kysytään sudo-salasanaa." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Ota mukaan" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Ota mukaan tiedostoja ja kansioita" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Lisää tiedosto" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Lisää kansio" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Jätä pois" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Jätä pois kuvioita, tiedostoja ja kansioita" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Lisää oletukset" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Jätä pois tätä suuremmat tiedostot:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Jätä pois tätä suuremmat tiedostot: {size_unit}." #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Jätä pois tiedostot, jotka ovat suurempia kuin kohdan\n" "%(prefix)s arvo. Kun ”täysi rsync-tila” on poissa käytöstä, tällä\n" "on vaikutusta vain uusiin tiedostoihin, koska rsyncissä asetus vaikuttaa\n" "vain siirtoon, ei pois jättämiseen. Näin suuret tiedostot, jotka on\n" "varmuuskopioitu aikaisemmin säilyvät otoksessa, vaikka ne muuttuisivat." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Automaattinen &poisto" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Vanhemmat kuin:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Vuosi(a)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Kun vapaata tilaa on alle:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Kun vapaita inodeja on alle:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Älykäs poisto:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Suorita etäkoneessa taustalla." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "KOKEELLINEN" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Säilytä kaikki otokset" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "viime päivältä (/päiviltä)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Säilytä yksi otos kultakin päivältä" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Säilytä yksi otos kultakin viikolta" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "viime viikolta (/viikoilta)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Säilytä yksi otos kultakin kuulta" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "viime kuulta (/kuukausilta)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Säilytä yksi otos kultakin vuodelta." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Älä poista nimettyjä otoksia." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Valinnat" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Käytä ilmoituksia" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Älä tee otoksia akkukäytössä" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Järjestelmä ei tarjoa virranhallintatietoja" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Tee vain yksi otos kerrallaan" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Muut otokset estetään, kunnes nykyinen on valmis. Tämä on yleisasetus, joten" " se vaikuttaa käyttäjän kaikkiin profiileihin. Muille käyttäjille asetus " "täytyy kuitenkin ottaa käyttön erikseen." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Varmuuskopioi palautettaessa korvatut tiedostot" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Tiedostojen uudempien versioiden nimiin lisätään ”{suffix}” ennen palauttamista.\n" "Ellei niitä enää tarvita, ne voi poistaa komennolla \"{cmd}”" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Jatka virheistä huolimatta (säilytä keskeneräiset otokset)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Havaitse muutokset tarkistussummista" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Tee uusi otos, vaikkei muutoksia olisi." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Lokitaso:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Ei mitään" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "A&siantuntija-asetukset" #: qt/settingsdialog.py:936 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Muuta näitä asetuksia vain, jos tiedät, mitä teet." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Suorita rsync komennolla '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "cron-töissä" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "etäkoneilla" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "käyttäjän tehdessä itse otoksia" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Ole hyvä ja asenna 'nocache' aktivoidaksesi tämän asetuksen)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "paikallisella koneella" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Ohjaa cron-töiden vakiotulosvirta /dev/nulliin." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Ohjaa cron-töiden vakiovirhevirta /dev/nulliin." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Rajoita rsyncin kaistan käyttöä:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "kt/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Säilytä ACL:t" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Säilytä lisämääritteet (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopioi turvattomat linkit (toimii vain absoluuttisille linkeille)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Rajaa yhdelle tiedostojärjestelmälle" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Valitsinten arvot tulee merkitä lainausmerkkeihin, esim. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Liitä lisävalinnat rsyncille" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Lisää SSH-komentoihin etuliite" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Etuliite joka ajetaan ennen jokaista komentoa etäkoneella." #: qt/settingsdialog.py:1188 #, 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/settingsdialog.py:1196 msgid "default" msgstr "oletus" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Tarkista, onko etäkone verkossa" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Varoitus: ellei ole käytössä eikä etäkone ole käytettävissä, voi johtaa " "outoihin virheisiin." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Tarkista, tukeeko etäkone kaikkia vaadittuja komentoja." #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Varoitus: ellei ole käytössä eikä etäkone tue kaikkia vaadittuja komentoja, " "voi johtaa outoihin virheisiin." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Palauta määritys" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Muokkaa käyttäjäkutsua" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Uusi profiili" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Muuta profiilin nimeä" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Haluatko varmasti poistaa profiilin ”{name}”?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Erityisesti suositellaan{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "Mukautettujen tuntien on oltava pilkuin erotettu tuntiluettelo (esim. " "8,12,18,23) tai */3 varmuuskopioille kolmen tunnin välein." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Et valinnut SSH:lle yksityistä avainta.\n" "Haluatko luoda uuden salasanattoman julkisen ja yksityisen avaimen parin?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Yksityistä avaintiedostoa ”{file}” ei ole olemassa." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Haluatko kopioida julkisen SSH-avaimesi etäkoneelle\n" "salasanatonta kirjautumista varten?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Koneen ”{host}” autenttisuutta ei voida todentaa." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Tarkista tämä sormenjälki! Haluatko lisätä sen ”known_hosts”-tiedostoosi?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Jätä pois kuvio" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Jätä pois tiedosto" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Jätä pois kansio" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Ota mukaan tiedosto" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "”{path}” on symlinkki. Linkin kohdetta ei varmuuskopioida, ellei sitä oteta erikseen mukaan.\n" "Haluatko ottaa mukaan symlinkkien kohteet?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Ota mukaan kansio" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Haluatko varmasti muuttaa otoskansiota?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Ei voitu luoda uutta SSH-avainta kansioon {path}" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "oletus" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "ei käytössä" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "käytössä" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Komento:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametrit:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Käytä sijaintiparametreina %1 ja %2" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Luettele vain otokset, jotka ovat samoja kuin:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Syvätarkistus (tarkempi, mutta hitaampi)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Poista" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Valitse kaikki" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Siirry" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Valinnat" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Otosta ei voi verrata itseensä." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Haluatko poistaa tiedoston ”{file}” otoksesta ”{snapshot_id}”?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Haluatko varmasti poistaa tiedoston ”{file}” {count} otoksesta?" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "Tätä ei voi kumota!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Jätetäänkö ”{path}” pois myöhemmistä otoksista?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "crontab-tiedostoa ei löydy. Varmista, että cron on asennettu, ellei, poista " #~ "automaattinen varmuuskopiointi käytöstä." #~ msgid "Full snapshot path" #~ msgstr "Otoksen täydellinen sijainti" #~ msgid "Mode" #~ msgstr "Tila" #~ msgid "Profile" #~ msgstr "Profiili" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profiili ”{profile}”: Anna {mode}-salasana: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Käyttäjäkutsuskriptin #!-rivi ei viittaa ohjelmatiedostoon." #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs 1.7.2:n ja vanhempien versioiden --reverse-valitsimessa on " #~ "ohjelmavirhe. Päivitä encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "käyttäjäkutsuskriptistä puuttuu #!-rivi." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} käyttää salaukseen EncFS:ää. Tietoturvatarkistus paljasti siitä " #~ "äskettäin haavoittuvuuksia. Ks. ”man backintime”, ”A NOTE ON SECURITY”." backintime-1.5.2/common/po/fo.po000066400000000000000000001171361465446530500165240ustar00rootroot00000000000000# Faroese 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-06 08:07+0000\n" "Last-Translator: buhtz \n" "Language-Team: Faroese \n" "Language: fo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Ávaring" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Høvuðsumhvarv" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "Krypteraður" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH krypterað" #: common/config.py:309 msgid "Local" msgstr "Lokalt" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH privatur lykil" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "Krypteraður" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Kryptering" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH krypterað" #: common/config.py:327 msgid "Default" msgstr "" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Umhvarv: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Støðumyndaskjáttan er ólóglig!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Tú mást í minsta lagi velja eina skjáttu at trygdarrita!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} er ikki ein skjatta." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "" #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Kann ikki skriva til: {path}\n" "Hevur tú skrivi rættindi ?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "" #: common/config.py:497 msgid "Expert Options" msgstr "Framkomnir kostir" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "" #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "" #: common/configfile.py:107 msgid "Failed to save config" msgstr "" #: common/configfile.py:143 msgid "Failed to load config" msgstr "" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Umhvarv \"{name}\" finnst longu." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "" #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "" #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "" #: common/encfstools.py:151 msgid "Cancel" msgstr "" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Tak eina løtumynd" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "" #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Goymi loyvi" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Liðugt" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Bíði %s sekund." msgstr[1] "Bíði %s sekund." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "" #: common/snapshots.py:937 msgid "Finalizing" msgstr "" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Kann ikki stovna skjáttuna" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Goymi samansetingarfíluna …" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Goymi loyvi .…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Kaann ikki taka burtur skjáttuna" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Tak eina løtumynd" #: common/snapshots.py:1417 msgid "Success" msgstr "" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Tak gamlar støðumyndir burtir" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Royn at halda minstamark av tøkum plássi" #: common/snapshots.py:1929 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Royn at halda minstamark av tøkum plássi" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Nú" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Kann ikki stovna skjáttuna." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Um forritið" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "" #: qt/app.py:169 msgid "Shortcuts" msgstr "Snarvegir" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:256 msgid "Add to Include" msgstr "" #: qt/app.py:258 msgid "Add to Exclude" msgstr "" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Kann ikki stovna skjáttuna" #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" #: qt/app.py:481 msgid "Take a snapshot" msgstr "Tak eina løtumynd" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Hvar skullu støðumyndirnar goymast" #: qt/app.py:508 msgid "Name snapshot" msgstr "Tak eina løtumynd" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Tak støðumynd burtur" #: qt/app.py:516 msgid "View snapshot log" msgstr "Tak eina løtumynd" #: qt/app.py:520 msgid "View last log" msgstr "" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Høvuðsumhvarv…" #: qt/app.py:528 msgid "Shutdown" msgstr "" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:532 msgid "Setup language…" msgstr "" #: qt/app.py:536 msgid "Exit" msgstr "Far úr" #: qt/app.py:540 msgid "Help" msgstr "Hjálp" #: qt/app.py:544 msgid "Profiles config file" msgstr "Goymi samansetingarfíluna" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Heimasíða" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "" #: qt/app.py:553 msgid "FAQ" msgstr "" #: qt/app.py:556 msgid "Ask a question" msgstr "" #: qt/app.py:559 msgid "Report a bug" msgstr "" #: qt/app.py:562 msgid "Translation" msgstr "" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Endurstovna" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Endurstovna…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:595 msgid "Up" msgstr "Upp" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Vís fjaldar fílur" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Tak eina løtumynd…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 msgid "&Restore" msgstr "&Endurstovna" #: qt/app.py:682 msgid "&Help" msgstr "&Hjálp" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Er tú viss/ur í at taka støðumyndina burtur" #: qt/app.py:987 msgid "Working:" msgstr "Arbeiði:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Liðug, eingin trygdarriting neyðug" #: qt/app.py:1044 msgid "Working" msgstr "Arbeiði" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "" #: qt/app.py:1076 msgid "Sent" msgstr "" #: qt/app.py:1077 msgid "Speed" msgstr "" #: qt/app.py:1078 msgid "ETA" msgstr "" #: qt/app.py:1140 msgid "Global" msgstr "Heiltøkur" #: qt/app.py:1141 msgid "Root" msgstr "Rót" #: qt/app.py:1142 msgid "Home" msgstr "Heim" #: qt/app.py:1170 msgid "Backup folders" msgstr "Trygdarritingar-skjáttur" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Navn á støðumynd" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Er tú viss/ur í at taka støðumyndina burtur" msgstr[1] "Er tú viss/ur í at taka støðumyndina burtur" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" msgstr[1] "" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Er tú viss/ur í at taka støðumyndina burtur" msgstr[1] "Er tú viss/ur í at taka støðumyndina burtur" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" #: qt/app.py:1750 msgid "Snapshot" msgstr "Støðumyndir" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Endurstovna {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Endurstovna {path}…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 msgid "System default" msgstr "" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:188 #, python-brace-format, ignore-placeholder-compare 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 "" "Hey\n" "Tú hevur nú brúkt Back In Time á føroyskum nakrar ferðir.\n" "Umsetingin av tínari innløgdu útgávu av Back In Time til føroyskt er {perc} liðug. Tú er vælkomin til at geva eitt íkast til umsetingina og harvið eisini til sjálvt forritið Back In Time, óansæð tínar tøkniligu førleikar.\n" "Vitjað endiliga {translation_platform_url} um tú hevur hug at geva títt íkast. Um tú hevur spurningar ella brúk fyri hjálp, kanst tú vitja {back_in_time_project_website}.\n" "Hesi boðini verða ikki víst aftur, og vit biða um umbering fyri órónna. Boðini kunnu finnast aftur undir hjálparvalmyndini.\n" "Vinarliga, Back In Time toymi" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Umhvarv:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Støðumyndir:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtur:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Alt" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Broytingar" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Villur" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Upplýsingar" msgstr[1] "Upplýsingar" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Umhvarv: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Arbeiði…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Støðumyndir" #: qt/qttools.py:427 msgid "Today" msgstr "Í dag" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Í gjár" #: qt/qttools.py:443 msgid "This week" msgstr "Henda vikan" #: qt/qttools.py:450 msgid "Last week" msgstr "Fyrra vikan" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Vertur:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Brúkari:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Høvuðsumhvarv" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Ritstjórna" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Alment" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Hvar skullu støðumyndirnar goymast" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "SSH privatur lykil:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Framkomið" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Skrá" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Ógilda" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Við hvørjari byrjan/endurbyrjan" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Hvønn {n} minutt" msgstr[1] "Hvønn {n} minutt" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "" msgstr[1] "" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Hvønn {n} tími" msgstr[1] "Hvønn {n} tími" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Hvønn dag" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Hvørja viku" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Hvønn mánað" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Hvønn ár" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Tími:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Tími:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Hvønn:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dag(ar)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Vika(ur)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:555 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:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Tak við" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Legg fílu til" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Legg skjáttu til" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "Út&iloka" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "" #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Eldri enn:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Ár" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Um tað tøka plássið er minni enn:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Um tað tøka plássið er minni enn:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Dag(ar)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Vika(ur)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Tak støðumynd burtur." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Kostir" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Virkja kunningar" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "" #: qt/settingsdialog.py:911 msgid "None" msgstr "Einki" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "&Framkomnir kostir" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, 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/settingsdialog.py:1196 msgid "default" msgstr "" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nýtt umhvarv" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Nýnevn umhvarv" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Er tú viss/ur í at strika umhvarvið \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Tak fílu við" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Tak skjáttur við" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Er tú viss/ur í at broyta støðumyndaskjáttu ?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Stýriboð" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Far til" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Kostir" #: qt/snapshotsdialog.py:355 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:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Kann ikki finna crontab.\n" #~ "Er tú viss/ur í at cron er innlagt ?\n" #~ "Um so ikk er, eigur tú at ógilda allar sjálvvirknar trygdarritingar." #~ msgid "Profile" #~ msgstr "Umhvarv" backintime-1.5.2/common/po/fr.po000066400000000000000000001622661465446530500165330ustar00rootroot00000000000000# 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. # # msgid "" msgstr "" "Project-Id-Version: Back In Time 0.8.8\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-27 06:38+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Avertissement" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Profil principal" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Local (chiffré avec EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (chiffré avec EncFS)" #: common/config.py:309 msgid "Local" msgstr "Local" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Clé privée SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Local chiffré" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Chiffrement" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH chiffré" #: common/config.py:327 msgid "Default" msgstr "Par défaut" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil : \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Le dossier des instantanés n'est pas valide !" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Vous devez sélectionner au moins un dossier à sauvegarder !" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Le dossier de sauvegarde ne peut pas être inclus." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Le sous-dossier de sauvegarde ne peut pas être inclus." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Option invalide. {path} n'est pas un dossier." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Hôte/Utilisateur/ID du profil ne doivent pas être vides." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Impossible d'écrire dans : {path}\n" "Êtes-vous sûr(e) d'avoir les droits en écriture ?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "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 Linux natif." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "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/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Copier les liens (résoudre les liens symboliques)" #: common/config.py:497 msgid "Expert Options" msgstr "Options avancées" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Le système de fichiers de destination pour {path} est un partage sshfs. " "sshfs ne prend pas en charge les liens physiques. Veuillez utiliser le mode " "'SSH' à la place." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Échec d'écriture de la nouvelle crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron n'est pas lancé pas bien que la commande crontab soit disponible. Les " "tâches de sauvegarde planifiées ne seront pas exécutées. Il se pourrait que " "Cron soit installé mais pas activé. Essayez la commande \"systemctl enable " "cron\" ou consultez les canaux de support de votre distribution GNU Linux." #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "La planification udev ne fonctionne pas avec le mode {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Impossible de trouver l'UUID pour {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Échec de l'enregistrement de la configuration" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Échec du chargement de la configuration" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Le profil \"{name}\" existe déjà." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Le dernier profil ne peut pas être supprimé." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Impossible de monter '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Configuration du dossier chiffré introuvable." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Créer un nouveau dossier chiffré ?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Annuler" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Veuillez confirmer le mot de passe." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Le mot de passe ne correspond pas." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Prendre un instantané" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Impossible de démonter {mountprocess} depuis {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} non trouvé. Veuillez l'installer (par exemple via " "\"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Le point de montage {mntpoint} n'est pas vide." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Entrez le mot de passe pour le profil {mode} \"{profile}\" :" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "ÉCHEC" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Restaurer les permissions" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Terminé" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Différer la sauvegarde lors du fonctionnement sur batterie" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Impossible de trouver le dossier des instantanés.\n" "S'il se trouve sur un disque externe, veuillez le connecter." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "En attente %s seconde." msgstr[1] "En attente %s secondes." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Impossible d'effectuer l'instantané {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Finalisation" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Impossible de créer le dossier" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Sauvegarde en cours du fichier de configuration…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Sauvegarde en cours des permissions…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Instantané inachevé {snapshot_id} trouvé qui peut être poursuivi." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" "Suppression du répertoire résiduel {snapshot_id} restant de la dernière " "exécution" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Impossible de supprimer le répertoire" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Prise de l'instantané" #: common/snapshots.py:1417 msgid "Success" msgstr "Succès" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Transfert partiel en raison d'une erreur" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Transfert partiel dû à la disparition de fichiers source (voir 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' s'est terminé avec le code d'état {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Voir 'man rsync' pour plus de détails" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Les codes d'état négatifs de rsync sont des numéros de signal, voir 'kill " "-l' et 'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Aucun changement, un nouvel instantané n'est pas nécessaire" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Impossible de renommer {new_path} en {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Suppression intelligente" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Supprimer les anciens instantanés" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Essayer de conserver un minimum d'espace libre" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Essayer de conserver un minimum de {perc} d'inodes libres" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Maintenant" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Impossible de monter {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent introuvable. Assurez-vous qu'il soit installé." #: common/sshtools.py:444 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 non" " disponible pour cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Le chiffrement {cipher} a échoué pour {host}." #: common/sshtools.py:682 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/sshtools.py:687 msgid "Remote path is not writable." msgstr "Le chemin d'accès distant n'est pas accessible en écriture." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Le chemin d'accès distant n'est pas exécutable." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Impossible de créer le chemin distant." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "L'hôte distant {host} n'accepte pas {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Consultez 'man backintime' pour plus d'informations" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "La vérification des commandes sur l'hôte {host} a renvoyé une erreur " "inconnue" #: common/sshtools.py:1010 #, 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/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Copie de la clé SSH publique \"{pubkey}s\" sur l'hôte distant \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Veuillez fournir un mot de passe pour \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "À propos" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Auteurs" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Traductions" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licence" #: qt/app.py:169 msgid "Shortcuts" msgstr "Raccourcis" #: qt/app.py:189 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:256 msgid "Add to Include" msgstr "Ajouter aux inclusions" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Ajouter aux exclusions" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} semble s'exécuter pour la première fois car aucune configuration " "n'a été trouvée." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Importer une configuration existante (à partir d'un dossier de sauvegarde ou" " d'un autre ordinateur) ?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Impossible de trouver le répertoire des instantanés." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "S'il se trouve sur un disque externe, veuillez le connecter puis cliquer sur" " OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Prendre un instantané" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Utiliser la date de modification et la taille pour détecter les changements " "des fichiers." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Prendre un instantané (mode somme de contrôle)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "" "Utiliser les sommes de contrôle pour détecter les changements des fichiers." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Mettre en pause la prise de l'instantané" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Reprendre la prise de l'instantané" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Arrêter la prise de l'instantané" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Rafraîchir la liste des instantanés" #: qt/app.py:508 msgid "Name snapshot" msgstr "Nommer l'instantané" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Supprimer l'instantané" #: qt/app.py:516 msgid "View snapshot log" msgstr "Voir le journal des instantanés" #: qt/app.py:520 msgid "View last log" msgstr "Afficher le dernier journal" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Gérer les profils…" #: qt/app.py:528 msgid "Shutdown" msgstr "Arrêt" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Éteindre le système après la fin de l'instantané." #: qt/app.py:532 msgid "Setup language…" msgstr "Configurer la langue…" #: qt/app.py:536 msgid "Exit" msgstr "Quitter" #: qt/app.py:540 msgid "Help" msgstr "Aide" #: qt/app.py:544 msgid "Profiles config file" msgstr "Fichier de configuration des profils" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Site Web" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Notes de version" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ" #: qt/app.py:556 msgid "Ask a question" msgstr "Poser une question" #: qt/app.py:559 msgid "Report a bug" msgstr "Signaler un bogue" #: qt/app.py:562 msgid "Translation" msgstr "Traduction" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" "Affiche à nouveau le message au sujet de la participation à la traduction." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Transition de chiffrement (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Affiche à nouveau le message au sujet de la suppression d'EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Restaurer" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Restaurer les fichiers ou répertoires sélectionnés à leur emplacement " "d'origine." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Restaurer vers …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" "Restaurer les fichiers ou répertoires sélectionnés vers un nouvel " "emplacement." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Restaurer le répertoire actuellement affiché et tout son contenu à " "l'emplacement d'origine." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Restaurer le répertoire actuellement affiché et tout son contenu vers un " "nouvel emplacement." #: qt/app.py:595 msgid "Up" msgstr "Dossier parent" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Afficher les fichiers cachés" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Comparer des instantanés…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Sauvegarde" #: qt/app.py:676 msgid "&Restore" msgstr "&Restaurer" #: qt/app.py:682 msgid "&Help" msgstr "&Aide" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot 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é." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Voulez-vous vraiment la fermer ?" #: qt/app.py:987 msgid "Working:" msgstr "En cours :" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Terminé, il n'y a rien à sauvegarder" #: qt/app.py:1044 msgid "Working" msgstr "En cours" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Erreur" #: qt/app.py:1076 msgid "Sent" msgstr "Envoyé" #: qt/app.py:1077 msgid "Speed" msgstr "Vitesse" #: qt/app.py:1078 msgid "ETA" msgstr "Temps restant estimé" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Répertoire racine" #: qt/app.py:1142 msgid "Home" msgstr "Répertoire personnel" #: qt/app.py:1170 msgid "Backup folders" msgstr "Répertoires sauvegardés" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nom de l'instantané" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Êtes-vous sûr(e) de vouloir supprimer cet instantané ?" msgstr[1] "Êtes-vous sûr(e) de vouloir supprimer ces instantanés ?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Créer des copies de sauvegarde avec le suffixe '{suffix}' \n" "avant d'écraser ou de supprimer les fichiers locaux." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "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:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Restaurer seulement les fichiers qui n'existent pas\n" " ou qui sont plus récents que ceux de la destination.\n" "Utilise l'option \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Supprimer les fichiers les plus récents dans le dossier d'origine." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Restaure les fichiers ou répertoires sélectionnés vers leur emplacement " "d'origine et supprime les fichiers ou répertoires qui ne font pas partie de " "la sauvegarde. Attention : cette option va supprimer les fichiers et " "répertoires qui étaient exclus lors de la sauvegarde." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Voulez-vous vraiment restaurer ce fichier\n" "dans le nouveau répertoire {path} ?" msgstr[1] "" "Voulez-vous vraiment restaurer ces fichiers\n" "dans le nouveau répertoire {path} ?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Voulez-vous vraiment restaurer ce fichier ?" msgstr[1] "Voulez-vous vraiment restaurer ces fichiers ?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "Voulez-vous vraiment supprimer tous les fichiers les plus récents dans " "{path} ?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Êtes vous sûr(e) de vouloir supprimer tous les fichiers plus récents dans le" " répertoire d'origine ?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}Attention{BOLDEND} : Supprimer des fichiers à la racine du système de " "fichiers pourrait endommager l'ensemble de votre système." #: qt/app.py:1750 msgid "Snapshot" msgstr "Instantané" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Restaurer {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurer {path} vers…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Le changement de langue ne prend effet qu'après le redémarrage de Back In " "Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Le support pour EncFS sera arrêté dans un futur prévisible. Il n'est " "désormais plus recommandé d'utiliser ce mode pour un profil." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Le choix d'une solution de remplacement pour le support continu de " "sauvegardes chiffrées est toujours en cours, en fonction des ressources du " "projet et de la disponibilité des contributeurs. Plus de détails sont " "disponibles dans ce {whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "livre blanc" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Le support des profils d'instantanés chiffrés subit des changements " "significatifs, et EncFS sera supprimé dans un futur prévisible." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Les profils suivants sont chiffrés avec EncFS :" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Une décision concernant une solution de remplacement pour la poursuite de la" " prise en charge des sauvegardes chiffrées est en attente, en fonction des " "ressources du projet et de la disponibilité des contributeurs. Les " "utilisateurs sont invités à participer à la discussion. Les derniers détails" " sur les prochaines étapes sont disponibles dans ce {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Ce message ne sera plus affiché. Cette boîte de dialogue est disponible à " "tout moment via le menu d'aide." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Votre équipe Back In Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Configurer la langue" #: qt/languagedialog.py:92 msgid "System default" msgstr "Par défaut" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Utiliser la langue du système." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Traduit à : {percent}" #: qt/languagedialog.py:188 #, 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, merci de 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/languagedialog.py:217 msgid "translation platform" msgstr "plateforme de traduction" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Votre traduction" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Voir le dernier journal" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Voir le journal des instantanés" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil :" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Instantanés :" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtre :" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Tous" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Modifications" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Erreurs" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Information" msgstr[1] "Informations" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "Échecs de transfert rsync (expérimental)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Erreur, [I] Information, [C] Modification" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "décoder les chemins" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Question" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil : {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Voir la dernière entrée du journal" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Démarrer {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "En cours…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Envoyé :" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Vitesse :" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA :" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Instantanés" #: qt/qttools.py:427 msgid "Today" msgstr "Aujourd'hui" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Hier" #: qt/qttools.py:443 msgid "This week" msgstr "Cette semaine" #: qt/qttools.py:450 msgid "Last week" msgstr "La semaine dernière" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Ceci n'est PAS un instantané (sauvegarde) mais une vue de l'état actuel de " "vos fichiers locaux" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Dernière vérification {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Afficher le journal complet" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Proxy SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Hôte :" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port :" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Utilisateur :" #: qt/settingsdialog.py:120 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 "" "Connectez-vous à l'hôte cible via ce mandataire (proxy, aussi appelé jump " "host). Voir l'option \"-J\" dans la documentation de la commande ssh ou " "\"ProxyJump\" dans celle de \"ssh_config\" pour les détails." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Gérer les profils" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Modifier" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Ajouter" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Retirer" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Général" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Mode :" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Dossier pour les sauvegardes" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Dossier" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Réglages SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Chemin :" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Type de chiffrement :" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Clé privée :" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Choisissez un fichier de clé privée existant (normalement nommé \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Créer une nouvelle clé SSH sans mot de passe (non autorisé si un fichier de " "clé privée est déjà sélectionné)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Mot de passe" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Enregistrer le Mot de passe dans le trousseau" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Mot de passe en cache pour Cron (note de sécurité : root peut lire le mot de" " passe)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Avancées" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Chemin d'accès complet de l'instantané :" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Planification" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Désactivée" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "À chaque démarrage/re-démarrage" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Chaque minute" msgstr[1] "Toutes les {n} minutes" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Chaque heure" msgstr[1] "Toutes les {n} heures" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Chaque heure" msgstr[1] "Toutes les {n} heures" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Horaires personnalisées" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Tous les jours" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "À plusieurs reprises (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Quand le disque est connecté (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Chaque semaine" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Chaque mois" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Chaque année" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Jour :" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Jour de la semaine :" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Heure :" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Heures :" #: qt/settingsdialog.py:521 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." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Tous les :" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Heure(s)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Jour(s)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Semaine(s)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Mois" #: qt/settingsdialog.py:555 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 "" "Exécutez Back In Time dès que le lecteur est connecté (seulement une fois tous les X jours).\n" "Vous serez invité à saisir votre mot de passe super-utilisateur." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Activer l'enregistrement des messages de débogage" #: qt/settingsdialog.py:568 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:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Attention : n'utilisez cette fonction que temporairement pour des " "diagnostics, car elle génère une grande quantité de données." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Inclure" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Inclure les fichiers et répertoires" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Ajouter un fichier" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Ajouter un répertoire" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Exclure" #: qt/settingsdialog.py:625 #, 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} : En mode 'SSH chiffré', seuls les astérisques simples " "ou doubles sont fonctionnels (par exemple {example2}). Les autres types de " "jokers et de motifs seront ignorés (par exemple {example1}). Les noms de " "fichiers sont imprévisibles dans ce mode en raison du chiffrement par EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Exclure les motifs, fichiers ou répertoires" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Ajouter par défaut" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Exclure les fichiers plus gros que :" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Exclure les fichiers plus gros que la valeur en {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Avec 'Full rsync mode' désactivé, seuls les nouveaux fichiers seront " "affectés car du point de vue de rsync c'est une option de transfert et non " "d'exclusion. Les gros fichiers transférés précédemment resteront donc dans " "l'instantané même s'ils ont été modifiés." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Suppression &automatique" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Si antérieur à :" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Année(s)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Si l'espace libre est inférieur à :" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Si les inodes libres sont inférieurs à :" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Suppression intelligente :" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Exécuter en tâche de fond sur l'hôte distant." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPÉRIMENTAL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Garder tous les instantanés depuis les derniers" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "jour(s)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Garder un instantané par jour depuis les derniers" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Garder un instantané par semaine depuis les dernières" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "semaine(s)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Garder un instantané par mois depuis les derniers" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mois." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Garder un instantané par an tous les ans." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ne pas effacer les instantanés avec des noms." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Options" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Activer les notifications" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Désactiver les sauvegardes automatiques en mode batterie" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Impossible d'obtenir l'état de l'alimentation du système" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Exécuter un seul instantané à la fois" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Les autres instantanés seront bloqués jusqu'à ce que l'instantané en cours " "soit terminé. Ceci une option globale. Elle affectera donc tous les profils " "de cet utilisateur. Vous devez cependant l'activer pour tous les autres " "utilisateurs." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Sauvegarder les fichiers remplacés lors de la restauration" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Les versions plus récentes des fichiers seront renommées avec le préfixe " "'{suffix}' avant la restauration. Si vous n'en avez pas besoin, vous pouvez " "les supprimer avec '{cmd}'" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuer en cas d'erreur (conserver les instantanés incomplets)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Utiliser la somme de contrôle pour détecter les changements" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Prendre un instantané qu'il y ait eu des modifications ou non." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Niveau de journalisation :" #: qt/settingsdialog.py:911 msgid "None" msgstr "Aucun" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "O&ptions avancées" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Modifiez ces options uniquement si vous savez réellement ce que vous faites." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Exécuter 'rsync' avec '{cmd}' :" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "comme une tâche cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "sur un serveur distant" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "lors d'un instantané manuel" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Veuillez installer 'nocache' pour activer cette option)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "sur la machine locale" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Rediriger stdout vers /dev/null dans les tâches cron." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Cron enverra automatiquement un email avec les résultats des cronjobs en " "pièce jointe si un MTA est installé." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Rediriger stderr vers /dev/null dans les tâches cron." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Cron enverra automatiquement un email avec les erreurs attachées des " "cronjobs si un MTA est installé." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Limiter l'utilisation de bande passante par rsync :" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "Ko/sec" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Conserver les autorisations" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Conserver les attributs étendus (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Copier les liens non sécurisés (fonctionne seulement avec les liens absolus)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Limiter à un système de fichier" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" "Les arguments doivent être entourés de guillemets, par exemple : {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Passer des options supplémentaires à rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Ajouter un préfixe aux commandes SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Préfixe à exécuter avant chaque commande sur l'hôte distant." #: qt/settingsdialog.py:1188 #, 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 "" "Les variables doivent être échappées avec \\$FOO. Cela n'affecte pas rsync. " "Donc, pour ajouter un préfixe à rsync, utilisez \"{example_value}\" avec " "{rsync_options_value}." #: qt/settingsdialog.py:1196 msgid "default" msgstr "par défaut" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Vérifier si l'hôte distant est en ligne" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Attention : si désactivé et si l'hôte distant n'est pas disponible, cela " "pourrait conduire à d'étranges erreurs." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Vérifier si l'hôte distant accepte toutes les commandes nécessaires." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Attention : si désactivé et que l'hôte distant ne prend pas en charge toutes" " les commandes nécessaires, cela pourrait entraîner des erreurs étranges." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Restaurer la configuration" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Éditer le script de rappel (callback)" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "La prise en charge d'EncFS cessera dans un avenir prévisible. Une solution " "de remplacement permettant la poursuite de la prise en charge des " "sauvegardes chiffrées est toujours en attente de décision, dépendant des " "ressources du projet et de la disponibilité des contributeurs. De plus " "amples détails sont disponibles dans ce {whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nouveau profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Renommer le profil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Êtes-vous sûr(e) de vouloir supprimer le profil \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Fortement recommandé{ENDBOLD} : (Toutes les recommandations sont déjà " "incluses.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Fortement recommandé{ENDBOLD} : {files}" #: qt/settingsdialog.py:1634 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 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:1681 msgid "You did not choose a private key file for SSH." msgstr "Vous n'avez pas sélectionné de fichier de clé privée pour SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Voulez-vous générer une nouvelle paire de clés publique/privée sans mot de " "passe ?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Le fichier de clé privée {file} n'existe pas." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Voulez-vous copier votre clé publique SSH sur l'hôte distant pour activer " "l'identification sans mot de passe ?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "L'authenticité de l'hôte {host} ne peut être établie." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "L'empreinte de la clé {keytype} est :" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Veuillez vérifier cette empreinte. Souhaitez-vous l'ajouter à votre fichier " "'known_hosts' ?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Exclure selon un modèle" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Exclure un fichier" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Exclure un répertoire" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Inclure le fichier" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" est un lien symbolique. La cible du lien ne sera pas sauvegardée tant que vous ne l'aurez pas inclue également.\n" "Voulez-vous plutôt inclure la cible du lien ?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Inclure un répertoire" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Êtes-vous sûr(e) de vouloir changer le répertoire des instantanés ?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Échec de la création de la nouvelle clé SSH dans {path}." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "Désactivé car ce motif n'est pas fonctionnel en mode \"SSH chiffré\"." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(par défaut : {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "désactivé" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "activé" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importer la configuration" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Aucune configuration trouvée" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importer" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Sélectionnez le répertoire d'instantanés à partir duquel le fichier de " "configuration doit être importé. Le chemin d'accès peut ressembler à : " "{samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Si le dossier est situé sur un disque externe ou distant, il doit être monté" " manuellement au préalable." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Options de la comparaison d'instantanés" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Commande :" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Paramètres :" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Utiliser %1 et %2 pour les paramètres de chemin" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Veuillez définir une commande diff ou appuyez sur Annuler." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "La commande \"{cmd}\" est introuvable sur ce système. Veuillez essayer autre" " chose ou appuyer sur Annuler." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Aucun paramètre n'a été défini pour la commande diff. Utilisation de la " "valeur par défaut \"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Instantanés différents seulement" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Lister uniquement les instantanés égaux à :" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Vérification approfondie (plus précise, mais lente)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Supprimer" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Tout sélectionner" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Comparer" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Atteindre" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Options" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Vous ne pouvez pas comparer un instantané à lui même." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Voulez-vous vraiment supprimer {file} dans l'instantané {snapshot_id} ?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Voulez-vous vraiment supprimer {file} dans les {count} instantanés ?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "AVERTISSEMENT : Ceci ne peut être révoqué." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Exclure {path} des futurs instantanés ?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? 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." #~ msgid "Full snapshot path" #~ msgstr "Chemin complet de l'instantané" #~ msgid "Info" #~ msgstr "Information" #~ msgid "Mode" #~ msgstr "Mode" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil '{profile}' : Entrez le mot de passe pour {mode}  : " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Le shebang du script de rappel n'est pas exécutable." #~ msgid "WARNING" #~ msgstr "ATTENTION" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "Les versions 1.7.2 et précédentes de encfs ont un bug avec l'option " #~ "--reverse. Veuillez mettre à jour encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "Le script de rappel (callback) n'a pas de shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} utilise EncFS pour le chiffrement. Un audit récent de sécurité a " #~ "révélé plusieurs vecteurs d'attaque possibles le concernant. Veuillez lire " #~ "la section 'A NOTE ON SECURITY' dans la page du manuel ('man backintime')." backintime-1.5.2/common/po/gl.po000066400000000000000000001561461465446530500165260ustar00rootroot00000000000000# Galician 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: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-24 15:18+0000\n" "Last-Translator: mbouzada \n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Advertencia" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Local (EncFS cifrado)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (EncFS cifrado)" #: common/config.py:309 msgid "Local" msgstr "Local" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Chave privada SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Cifrado local" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Cifrado" #: common/config.py:320 msgid "SSH encrypted" msgstr "Cifrado SSH" #: common/config.py:327 msgid "Default" msgstr "Predeterminado" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: «{name}»" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "O cartafol das instantáneas non é válido!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Debe escoller polo menos un cartafol para a copia de seguranza!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Non é posíbel incluír o cartafol de copias de seguranza." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Non é posíbel incluír o subcartafol de copia de seguranza." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Оpción non válida. {path} non é un cartafol." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "O servidor/usuario/ID do perfil non poden estar baleiros." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Non é posíbel escribir en: {path}\n" "Está seguro de ter permiso de escritura?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "O sistema de ficheiros de destino de {path} está formatado con FAT, o cal " "non admite ligazóns fortes. Use un sistema de ficheiros nativo de GNU/Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "O sistema de ficheiros de destino {path} é unha comparticion montada de SMB." " Asegúrese de que o servidor de SMB remoto acepta ligazóns simbólicas ou " "active {copyLinks} en {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Copiar as ligazóns (desbotar as ligazóns simbólicas)" #: common/config.py:497 msgid "Expert Options" msgstr "Opcións avanzadas" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "O sistema de ficheiros de destino de {path} é unha compartición montada de " "sshfs. Sshfs non acepta ligazóns fortes. Use o modo «SSH»." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Produciuse un fallo escribindo no novo crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Non se esta a executar cron, mais existe a orde crontab. Non se crearán " "copias de seguranza. É posíbel que cron estea instalado mais inactivo. Probe" " a executar «systemctl enable cron» ou consultar a axuda da súa distribución" " GNU/Linux." #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Non foi posíbel instalar a regra de Udev para o perfil {profile_id}. O " "servizo DBus «{dbus_interface}» non estaba dispoñíbel" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "O programador udev non funciona en modo {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Non foi posíbel atopar o UUID de «{path}»" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Produciuse un erro ao gardar a configuración" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Produciuse un erro ao cargar a configuración" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "O perfil «{name}» xa existe." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Non é posíbel revogar o último perfil." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Non é posible montar «{command}»" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Non se atopou a configuración do cartafol cifrado." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Quere crear un novo cartafol cifrado?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Cancelar" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Confirme o contrasinal." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "O contrasinal non coincide." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Facer unha instantánea" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Non é posíbel desmontar {mountprocess} de {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "Non se atopou {command}. Instáleo, por exemplo con {installcommand}" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "O punto de montaxe {mntpoint} non está libre." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Introduza o contrasinal do perfil de {mode} ({profile}):" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "FALLOU" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Restaurar os permisos" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Feito" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Adiando a copia de seguranza mentres se usa a batería" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Non é posíbel atopar o cartafol das instantáneas.\n" "Se está nun dispositivo extraíbel débeo conectar." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Agardando %s segundo." msgstr[1] "Agardando %s segundos." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Produciuse un fallo ao facer a instantánea {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Rematando" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Non é posíbel crear o cartafol" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Gardando o ficheiro de configuración…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Gardando os permisos…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Atopáronse restos de {snapshot_id} cos que se pode continuar." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Retirando o cartafol {snapshot_id} sobrante da última execución" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Non é posíbel eliminar o cartafol" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Facer instantánea" #: common/snapshots.py:1417 msgid "Success" msgstr "Satisfactorio" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Transferencia incompleta por mor dun fallo" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Transferencia incompleta por desapareceren os ficheiros orixinais (véxase " "«man rsync»)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "«rsync» rematou co código {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Véxase «man rsync» para obter máis información" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Se rsync devolve un código negativo, iso é o código dun sinal. Véxanse as " "ordes «kill -l» e «man kill»" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Non se precisa unha instantánea nova porque non houbo cambios" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Non é posible renomear {new_path} a {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Remoción intelixente" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Eliminando instantáneas antigas" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Tentar preservar o espazo libre mínimo" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Tentando manter un mínimo de {perc} inodos libres" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Agora" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Non é posible montar {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Non se atopou ssh-agent. Asegúrese de que está instalado." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Non foi posíbel desbloquear a chave privada ssh. O contrasinal é incorrecto " "ou non está dispoñíbel para cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Fallou o cifrado {cipher} para {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "A ruta remota existe mais non é un cartafol." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "A ruta remota non permite a escritura." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "A ruta remota non é executábel." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Non foi posíbel crear a ruta remota." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "O servidor remoto {host} non admite {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Vexa «man backintime» para obter máis instrucións" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "As ordes de comprobación no servidor {host} devolveron un fallo descoñecido" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "O servidor remoto {host} non admite ligazóns duras" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Copiar a chave ssh pública «{pubkey}» ao servidor remoto «{host}»." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Introduza o contrasinal de «{user}»." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Sobre" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autores" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Traducións" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licenza" #: qt/app.py:169 msgid "Shortcuts" msgstr "Atallos" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Este cartafol non existe\n" "na actual instantánea seleccionada." #: qt/app.py:256 msgid "Add to Include" msgstr "Engadir á lista de inclusión" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Engadir á lista de exclusión" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "Semella que é a primeira vez que se executa {app_name} xa que non se atopou " "ningunha configuración." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Importar unha configuración existente (de copias de seguranza ou doutro " "computador)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Non é posíbel atopar o cartafol de instantáneas." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "Se está nunha unidade extraíble, conéctea e prema en Aceptar." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Facer unha instantánea" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Usar a data de modificación e o tamaño para atopar ficheiros modificados." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Facer unha instantánea (con suma de comprobación)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Usar a suma de comprobación para detectar cambios." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pausar o proceso de instantánea" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Continuar co proceso de instantánea" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Cancelar o proceso de instantánea" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Actualizar a lista de instantáneas" #: qt/app.py:508 msgid "Name snapshot" msgstr "Renomear a instantánea" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Retirar a instantánea" #: qt/app.py:516 msgid "View snapshot log" msgstr "Ver o rexistro de instantáneas" #: qt/app.py:520 msgid "View last log" msgstr "Ver o último rexistro" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Xestionar perfís…" #: qt/app.py:528 msgid "Shutdown" msgstr "Apagar" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Apagar o sistema ao rematar a instantánea." #: qt/app.py:532 msgid "Setup language…" msgstr "Configurar o idioma…" #: qt/app.py:536 msgid "Exit" msgstr "Saír" #: qt/app.py:540 msgid "Help" msgstr "Axuda" #: qt/app.py:544 msgid "Profiles config file" msgstr "Ficheiro de configuración dos perfís" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Sitio web" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Rexistro de cambios" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ (preguntas frecuentes)" #: qt/app.py:556 msgid "Ask a question" msgstr "Faga unha pregunta" #: qt/app.py:559 msgid "Report a bug" msgstr "Informar dun fallo" #: qt/app.py:562 msgid "Translation" msgstr "Tradución" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Amosa de novo a mensaxe sobre colaborar na tradución." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Transición do cifrado (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Amosa de novo a mensaxe sobre a retirada de EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Restaurar" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Restaurar os ficheiros ou cartafoles seleccionados no seu destino orixinal." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Restaurar en…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Restaurar os ficheiros ou cartafoles seleccionados nun novo destino." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "Restaurar o cartafol actual e todo o seu contido ao destino orixinal." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Restaurar o cartafol actual e todo o seu contido nun destino novo." #: qt/app.py:595 msgid "Up" msgstr "Subir" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Amosar os ficheiros agochados" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Comparar instantáneas…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Copia de seguranza" #: qt/app.py:676 msgid "&Restore" msgstr "&Restaurar" #: qt/app.py:682 msgid "&Help" msgstr "A&xuda" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Se pecha esta xanela Back In Time non poderá apagar o seu sistema cando " "remate a instantánea." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Confirma que quere pechala?" #: qt/app.py:987 msgid "Working:" msgstr "En proceso:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Rematado, non foi necesario facer unha copia de seguranza" #: qt/app.py:1044 msgid "Working" msgstr "En proceso" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Erro" #: qt/app.py:1076 msgid "Sent" msgstr "Enviada" #: qt/app.py:1077 msgid "Speed" msgstr "Velocidade" #: qt/app.py:1078 msgid "ETA" msgstr "Tempo restante estimado" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Raíz" #: qt/app.py:1142 msgid "Home" msgstr "Inicio" #: qt/app.py:1170 msgid "Backup folders" msgstr "Cartafoles de copia de seguranza" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nome da instantánea" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Confirma que quere eliminar esta instantánea?" msgstr[1] "Confirma que quere eliminar estas instantáneas?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Crear copias de seguranza cun {suffix} ao final antes\n" "de substituír ou retirar elementos locais." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "As versións máis recentes dos ficheiros renomearanse cun {suffix} ao final " "antes de restauralos. Se xa non os precisa, pode eliminalos coa seguinte " "orde:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Restaurar só os ficheiros que non existan ou\n" "que sexan máis recentes que os do destino.\n" "Empregando a opción «rsync --update»." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Retirar os ficheiros máis recentes do cartafol orixinal." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Restaurar os ficheiros ou cartafoles seleccionados ao destino orixinal e " "eliminar os ficheiros ou cartafoles que non estean na instantánea. Vaia " "amodo, xa que isto eliminará os ficheiros e cartafoles excluídos ao facer a " "instantánea." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Confirma que quere restaurar este elemento no novo cartafol\n" "{path}?" msgstr[1] "" "Confirma que quere restaurar estes elementos no novo cartafol\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Confirma que quere restaurar este elemento?" msgstr[1] "Confirma que quere restaurar estes elementos?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Confirma que quere eliminar os ficheiros máis recentes de {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Confirma que quere retirar todos os ficheiros recentes do cartafol orixinal?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}Advertencia{BOLDEND}: a eliminación de ficheiros na raíz do sistema de" " ficheiros pode estragar todo o sistema." #: qt/app.py:1750 msgid "Snapshot" msgstr "Instantánea" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Restaurar {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurar {path} en…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "Os axustes de idioma só teñen efecto após reiniciar Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "A compatibilidade con EncFS interromperase nun futuro próximo. Ademais, non " "se recomenda utilizar ese modo para un perfil." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Aínda está pendente unha decisión sobre a substitución para a " "compatibilidade continua das copias de seguranza cifradas, dependendo dos " "recursos do proxecto e da dispoñibilidade dos colaboradores. Hai máis " "detalles dispoñíbeis neste {whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "documento técnico" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "A compatibilidade con perfís de instantáneas cifradas está a experimentar " "cambios significativos e EncFS vai ser eliminado nun futuro próximo." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Os seguinte (ou seguintes) perfís usan cifrado con EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Aínda está pendente unha decisión sobre a substitución para a " "compatibilidade continua das copias de seguranza cifradas, dependendo dos " "recursos do proxecto e da dispoñibilidade dos colaboradores. Os usuarios " "están convidados a unirse a este debate. Os detalles actualizados sobre os " "seguintes pasos están dispoñíbeis neste {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Esta mensaxe non volverá ser amosada. Este diálogo está dispoñíbel en " "calquera momento a través do menú de axuda." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "O equipo de Back In Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Configurar o idioma" #: qt/languagedialog.py:92 msgid "System default" msgstr "Predeterminado do sistema" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Utilizar o idioma dos sistemas operativos." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Traducido: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Ola!\n" "Xa utilizou Back In Time en {language} varias veces.\n" "A tradución ao {language} da versión de Back In Time que ten instalada está completa só ao {perc}. Independentemente dos seus coñecementos técnicos, pode colaborar na tradución e, polo tanto, co propio Back In Time.\n" "Se quere colaborar, visite {translation_platform_url}. Para obter máis axuda ou resposta ás posíbeis preguntas, visite {back_in_time_project_website}.\n" "Descúlpenos a interrupción, esta mensaxe non volverá ser amosada. Pode abrir de novo este diálogo en calquera momento no menú de axuda.\n" "O equipo de Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "plataforma de tradución" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "A súa tradución" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Vista do último rexistro" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Vista do rexistro de instantáneas" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Perfil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Instantáneas:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtro:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Todo" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Cambios" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Erros" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Información" msgstr[1] "Informacións" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "Fallos nas transferencia de rsync (experimental)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Erro, [I] Información, [C] Cambio" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "descodificar rutas" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Pregunta" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Perfil: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Ver o último rexistro" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Iniciar {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "En proceso…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Enviada:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Velocidade:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "Tempo restante estimado:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Instantáneas" #: qt/qttools.py:427 msgid "Today" msgstr "Hoxe" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Onte" #: qt/qttools.py:443 msgid "This week" msgstr "Esta semana" #: qt/qttools.py:450 msgid "Last week" msgstr "A semana pasada" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Esta NON é unha instantánea senón unha vista actual dos ficheiros locais" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Última comprobación {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Amosar o rexistro completo" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Proxy SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Servidor:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Porto:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Usuario:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Xestionar perfís" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Editar" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Engadir" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Retirar" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Xeral" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modo:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Onde gardar as instantáneas" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Cartafol" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Axustes SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Ruta:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Cifrado:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Chave privada:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Escolla un ficheiro de chave privada existente (normalmente de nome " "«id_rsas»)" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Crear unha nova chave SSH sen contrasinal (non permitido se xa foi " "seleccionado un ficheiro de chave privada)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Contrasinal" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Gardar o contrasinal no chaveiro" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Caché do contrasinal para Cron (problema de seguranza: root pode ler o " "contrasinal)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Avanzado" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Ruta completa á instantánea:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Programar" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Desactivado" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "En todos os arranques" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Cada {n} minuto" msgstr[1] "Cada {n} minutos" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Cada hora" msgstr[1] "Cada {n} horas" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Cada {n} hora" msgstr[1] "Cada {n} horas" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Horas personalizadas" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Todos os días" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Repetidamente (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Cando a unidade se conecte (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Todas as semanas" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Todos os meses" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Todos os anos" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Día:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Día laborábel:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Hora:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Horas:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Executar Back In Time repetidamente. Útil se o computador non ten un " "funcionamento regular." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Cada:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Hora(s)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Día(s)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Semana(s)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Mes(es)" #: qt/settingsdialog.py:555 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 "" "Executar Back In Time cando o dispositivo estea conectado (só unha vez cada X días).\n" "Pediráselle o seu contrasinal para sudo." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Activar o rexistro de mensaxes de depuración" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" "Escribe mensaxes de nivel de depuración no rexistro do sistema mediante " "«--debug»." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Precaución: úseo só ocasionalmente para diagnósticos, xa que xera unha gran " "cantidade de resultados." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Incluír" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Incluír os ficheiros e os cartafoles" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Engadir ficheiro" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Engadir cartafol" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Excluír" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Excluír os patróns, ficheiros ou cartafoles" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Engadir predeterminado" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Excluír ficheiros maiores de:" #: qt/settingsdialog.py:698 #, 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:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Co «Modo resync completo» desactivado isto só afectará aos novos ficheiros " "porque para rsync é unha opción de transferencia, non de exclusión. Por iso," " os ficheiros grandes que se copiaron antes permanecerán nas copias, mesmo " "se tiveron cambios." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Remoción &automática" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Maior de:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Ano(s)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Se o espazo libre é menor de:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Se os inodos libres son menos do:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Eliminación intelixente:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Executar en segundo plano no servidor remoto." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Conservar todas as instantáneas dos últimos" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "día(s)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Conservar unha instantánea por día dos últimos" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Conservar unha instantánea por semana das últimas" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "semana(s)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Conservar unha instantánea por mes dos últimos" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mes(es)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Conservar unha instantánea por ano de todos os anos." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Non eliminar as instantáneas con nome." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opcións" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Activar as notificacións" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Desactivar as instantáneas cando estea coa batería" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "O estado da alimentación non está dispoñíbel no sistema" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Executar só unha instantánea á vez" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "As outras instantáneas bloquearanse ata que remate de facerse a actual. Esta" " é unha opción global. Afectará a todos os perfís deste usuario. Mais tamén " "debe activar isto para o resto de usuarios." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Copia de seguranza dos ficheiros substituídos na restauración" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "As versións máis recentes dos ficheiros renomearanse cun {suffix} ao final " "antes de restauralos. Se xa non os precisa, pode eliminalos con {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuar con erros (manter instantáneas incompletas)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Usar a suma de comprobación para detectar cambios" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Facer unha nova instantánea aínda que non houbese cambios." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Nivel do rexistro:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Ningún" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "O&pcións avanzadas" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Amodo: cambie estas opcións só se sabe ben o que está a facer." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Executar «rsync» con «{cmd}»:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "como un traballo de cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "nun servidor remoto" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "ao facer unha instantánea manual" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Instale «nocache» para activar esta opción)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "na máquina local" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirixir stdout a /dev/null en cronjobs." #: qt/settingsdialog.py:1005 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/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirixir stderr a /dev/null en cronjobs." #: qt/settingsdialog.py:1016 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:1024 msgid "Limit rsync bandwidth usage:" msgstr "Limitar o uso do largo de banda de rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Preservar ACL (listas de control de acceso)" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Preservar os atributos estendidos (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Copiar as ligazóns non seguras (funciona só con ligazóns absolutas)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Restrinxir a un sistema de ficheiros" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "As opcións deben levar comiñas p.e. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Pegar as opcións adicionais a rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Engadir prefixo ás ordes de SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefixo a executar antes de cada orde no servidor remoto." #: qt/settingsdialog.py:1188 #, 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 "" "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:1196 msgid "default" msgstr "predeterminado" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Comprobar se o servidor remoto está en liña" #: qt/settingsdialog.py:1214 msgid "" "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:1218 msgid "Check if remote host supports all necessary commands." msgstr "Comprobar se o servidor remoto acepta todas as ordes necesarias." #: qt/settingsdialog.py:1221 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/settingsdialog.py:1237 msgid "Restore Config" msgstr "Restaurar a configuración" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Editar a devolución de chamada do usuario" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "A compatibilidade con EncFS interromperase nun futuro próximo. Aínda está " "pendente unha decisión sobre a substitución para a compatibilidade continua " "das copias de seguranza cifradas, dependendo dos recursos do proxecto e da " "dispoñibilidade dos colaboradores. Hai máis detalles dispoñíbeis neste " "{whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Perfil novo" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Renomear o perfil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Confirma que quere eliminar o perfil «{name}»?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Moi recomendado{ENDBOLD}: (Todas as recomendacións xa incluídas.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Moi recomendado{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "As horas personalizadas só poden ser unha lista de horas separadas por comas" " (p. ex.: 8,12,18,23) ou */3 para copias periódicas cada 3 horas." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Non escolleu un ficheiro de chave privada para SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "Gustaríalle xerar un par de chaves pública/privada sen contrasinal?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Non existe o ficheiro coa chave privada «{file}»." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Quere copiar a súa chave SSH ao servidor remoto para activar o acceso sen " "contrasinal?" #: qt/settingsdialog.py:1878 #, 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:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "A pegada dixital da chave {keytype} é:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Verifique esta pegada dixital! Quere engadila ao seu ficheiro «known_hosts»?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Excluír o patrón" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Excluír o ficheiro" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Excluír o cartafol" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Incluír o ficheiro" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "«{path}» é unha ligazón simbólica. Non se fará unha copia de seguranza do destino ligado ata que tamén o inclúa.\n" "Quere incluír no seu lugar o destino da ligazón simbólica?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Incluír o cartafol" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Confirma que quere cambiar o cartafol de instantáneas?" #: qt/settingsdialog.py:2194 #, 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:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "Desactivouse porque este patrón non funciona no modo «SSH cifrado»." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(predeterminado: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "desactivado" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "activado" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importar a configuración" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Non se atopou ningunha configuración" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importar" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Seleccione o cartafol de instantáneas dende o que se debe importar o " "ficheiro de configuración. A ruta pode ter este aspecto: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Se o cartafol está situado nunha unidade externa ou remota, debe montarse " "previamente de xeito manual." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opcións para comparar instantáneas" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Orde:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parámetros:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Usar %1 e %2 como parámetros da ruta" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Defina unha orde diff ou prema en Cancelar." #: qt/snapshotsdialog.py:90 #, 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:98 #, 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:134 msgid "Differing snapshots only" msgstr "Só instantáneas con diferenzas" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Listar só as instantáneas iguais a:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Comprobación exhaustiva (máis precisa pero máis lenta)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Eliminar" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Seleccionar todo" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Comparar" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Ir a" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opcións" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Non é posíbel comparar unha instantánea con ela mesma." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Confirma que quere eliminar «{file}» na instantánea «{snapshot_id}»?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Confirma que quere eliminar «{file}» de {count} instantáneas?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "ADVERTENCIA: isto non se pode revogar." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Excluír {path} de instantáneas futuras?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Non se atopou o «crontab». Ten certeza de que «cron» está instalado? Se non " #~ "o está debería desactivar todas as copias automáticas." #~ msgid "Full snapshot path" #~ msgstr "Ruta completa á instantánea" #~ msgid "Mode" #~ msgstr "Modo" #~ msgid "Profile" #~ msgstr "Perfil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Perfil «{profile}»: Introduza o contrasinal de {mode}: " #~ msgid "WARNING" #~ msgstr "ADVERTENCIA" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "A versión de encfs 1.7.2 e anteriores teñen un fallo coa opción --reverse. " #~ "Actualice encfs." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} emprega o cifrado de EncFS. Unha auditoría recente de seguranza " #~ "mostrou varios posíbeis vectores de ataque para el. Bote unha ollada a «A " #~ "NOTE ON SECURITY» no manual do backintime." backintime-1.5.2/common/po/he.po000066400000000000000000001463661465446530500165230ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-02 06:55+0000\n" "Last-Translator: itay-goldraich \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "אזהרה" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "פרופיל ראשי" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "מקומי (בהצפנת EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (בהצפנת EncFS)" #: common/config.py:309 msgid "Local" msgstr "מקומי" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "מפתח SSH פרטי" #: common/config.py:314 msgid "Local encrypted" msgstr "בהצפנה מקומית" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "הצפנה" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH מוצפן" #: common/config.py:327 msgid "Default" msgstr "ברירת מחדל" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "פרופיל: „{name}”" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "תיקיית תמונות מצב אינה תקנית!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "יש לבחור בתיקייה אחת לפחות לגיבוי!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "אי אפשר לכלול את תיקיית הגיבוי." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "אי אפשר לכלול תת־תיקייה לגיבוי." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "אפשרות שגויה. {path} אינה תיקייה." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "מארח/משתמש/מזהה פרופיל לא יכולים להישאר ריקים." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "לא ניתן לכתוב אל: {path}\n" "האם אתה בטוח שיש ברשותך גישה לכתיבה ?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "מערכת הקבצים המיועדת ל־{path} מפורמטת ל־FAT שבה אין תמיכה בקישורים קשיחים. " "נא להשתמש במערכת קבצים טבעית של לינוקס." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "מערכת היעד עבור {path} היא שיתוף SMB מעוגן. נא לוודא ששרת ה־SMB המרוחק תומך " "בקישורים סמליים או להפעיל את {copyLinks} תחת {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "העתקת קישורים (ביטול הפניה של קישורים סמליים)" #: common/config.py:497 msgid "Expert Options" msgstr "אפשרויות מתקדמות" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "מערכת הקבצים המיועדת עבור {path} היא שיתוף בעיגון sshfs. ב־sshfs אין תמיכה " "בקישורים קשיחים. נא להשתמש במצב ‚SSH’ במקום." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "כתיבת crontab חדש נכשלה." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "תזמון udev לא עובד עם המצב {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "לא ניתן למצוא מזהה ייחודי ל־{path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "שמירת ההגדרות נכשלה" #: common/configfile.py:143 msgid "Failed to load config" msgstr "טעינת ההגדרות נכשלה" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "הפרופיל „{name}” כבר קיים." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "אי אפשר להסיר את הפרופיל האחרון." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "אי אפשר לעגן את ‚{command}’" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "לא נמצאו הגדרות לתיקייה מוצפנת." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "ליצור תיקייה מוצפנת חדשה?" #: common/encfstools.py:151 msgid "Cancel" msgstr "ביטול" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "נא לאשר את הסיסמה." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "הסיסמאות לא תואמות." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "בצע גיבוי" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "פקודה {command} לא נמצאה. אפשר להתקין את הפקודה (בעזרת \"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "" #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "הכנס סיסמא בשביל {mode} משתמש \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "נכשל" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "שחזור הרשאות" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "בוצע" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "הגיבוי מושהה בעת שימוש בסוללה" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "לא ניתן למצוא את תיקיית הגיבוי.\n" "אם הגיבוי נמצא בכונן נשלף, צריך לחבר אותו." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "בהמתנה של שנייה אחת." msgstr[1] "בהמתנה של %s שניות." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "שמירת תמונת המצב {snapshot_id} נכשלה." #: common/snapshots.py:937 msgid "Finalizing" msgstr "בהליכי סיום" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "לא ניתן ליצור את התיקייה" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "קובץ ההגדרות נשמר…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "הרשאות נשמרות…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "נמצאו שאריות {snapshot_id} שאפשר להמשיך." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "תיקיית השאריות {snapshot_id} מהריצה האחרונה נמחקת" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "לא ניתן להסיר תיקייה" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "תמונת מצב נשמרת" #: common/snapshots.py:1417 msgid "Success" msgstr "" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "שגיאה: בוצעה העברה חלקית בלבד" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "קרא את 'man rsync' לפרטים נוספים" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "דבר לא השתנה, לא צריך תמונת מצב חדשה" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "לא ניתן לשנות את השם {new_path} ל־{path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "הסרה חכמה" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "תמונות מצב ישנות נמחקות" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "מתבצע ניסיון לשמור על מקום פנוי מזערי" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "מתבצע ניסיון לשמור לפחות {perc} מה־inodes פנויים" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "עכשיו" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "אי אפשר לעגן את {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "לא הצלחנו למצוא ssh-agent. וודאו שהssh-agent מותקן כראוי." #: common/sshtools.py:444 #, fuzzy msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "שגיאה בפתיחת מפתח SSH פרטי. סיסמא שגויה ואו לא קיימת בcron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "מצפין {cipher} נכשל עבור המשתמש {host}." #: common/sshtools.py:682 #, fuzzy msgid "Remote path exists but is not a directory." msgstr "הנתיב המרוחק (קיים) הוא לא תיקייה." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "לא ניתן לכתוב לנתיב המרוחק." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "הנתיב המרוחק לא ניתן להפעלה." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "לא ניתן ליצור את הנתיב המרוחק." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "מארח מרוחק {host} לא תומך בפקודה {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "קרא את 'man backintime' להוראות נוספות" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "העתקת מפתח ה־SSH הציבורי „{pubkey}” למארח המרוחק „{host}”." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "נא למלא סיסמה למשתמש „{user}”." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "על אודות" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "יוצרים" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "תרגומים" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "רישיון" #: qt/app.py:169 msgid "Shortcuts" msgstr "קיצורים" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "התיקייה הזאת לא קיימת\n" "בתמונת המצב הנוכחית." #: qt/app.py:256 msgid "Add to Include" msgstr "הוספה להכללה" #: qt/app.py:258 msgid "Add to Exclude" msgstr "הוספה להחרגה" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "לא ניתן למצוא את תיקיית תמונות המצב." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "אם היא נמצאת על כונן נשלף יש לחבר אותו וללחוץ על אישור." #: qt/app.py:481 msgid "Take a snapshot" msgstr "שמירת תמונת מצב" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "שמירת תמונת מצב (עם סיכומי ביקורת)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "להשתמש בסיכומי ביקורת לאיתור שינויים." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "השהיית שמירת תמונת מצב" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "המשך שמירת תמונת מצב" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "עצירת שמירת תמונת מצב" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "רענון רשימת תמונות המצב" #: qt/app.py:508 msgid "Name snapshot" msgstr "מתן שם לתמונת מצב" #: qt/app.py:512 msgid "Remove snapshot" msgstr "הסרת תמונת מצב" #: qt/app.py:516 msgid "View snapshot log" msgstr "הצגת יומן תמונות מצב" #: qt/app.py:520 msgid "View last log" msgstr "הצגת היומן האחרון" #: qt/app.py:524 msgid "Manage profiles…" msgstr "ניהול פרופילים…" #: qt/app.py:528 msgid "Shutdown" msgstr "כיבוי" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "לכבות את המערכת אחרי ששמירת תמונת המצב הסתיימה." #: qt/app.py:532 msgid "Setup language…" msgstr "" #: qt/app.py:536 msgid "Exit" msgstr "יציאה" #: qt/app.py:540 msgid "Help" msgstr "עזרה" #: qt/app.py:544 msgid "Profiles config file" msgstr "קובץ הגדרות פרופילים" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "אתר" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "יומן שינויים" #: qt/app.py:553 msgid "FAQ" msgstr "שאלות ותשובות" #: qt/app.py:556 msgid "Ask a question" msgstr "פרסום שאלה" #: qt/app.py:559 msgid "Report a bug" msgstr "דיווח על תקלה" #: qt/app.py:562 msgid "Translation" msgstr "תרגום" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "שחזור" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "שחזור הקבצים או התיקיות הנבחרים ליעד המקורי." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "שחזור אל…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "שחזור הקבצים או התיקיות הנבחרים ליעד חדש." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "שחזור התיקייה שמופיעה ואת כל התוכן שלה ליעד המקורי." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "שחזור התיקייה שמופיע ואת כל התוכן שלה ליעד חדש." #: qt/app.py:595 msgid "Up" msgstr "למעלה" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "הצגת קבצים נסתרים" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "השוואת תמונות מצב…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 msgid "&Restore" msgstr "&שחזור" #: qt/app.py:682 msgid "&Help" msgstr "ע&זרה" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "סגירת החלון תגרום לכך של־Back In Time לא תהיה אפשרות לכבות את המערכת שלך עם " "סיום שמירת תמונת המצב." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "לסגור אותו?" #: qt/app.py:987 msgid "Working:" msgstr "בעבודה:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "הסתיים, לא נדרש גיבוי" #: qt/app.py:1044 msgid "Working" msgstr "בעבודה" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "שגיאה" #: qt/app.py:1076 msgid "Sent" msgstr "נשלח" #: qt/app.py:1077 msgid "Speed" msgstr "מהירות" #: qt/app.py:1078 msgid "ETA" msgstr "זמן משוערך" #: qt/app.py:1140 msgid "Global" msgstr "כללי" #: qt/app.py:1141 msgid "Root" msgstr "תיקיית העל" #: qt/app.py:1142 msgid "Home" msgstr "תיקיית הבית" #: qt/app.py:1170 msgid "Backup folders" msgstr "תיקיות הגיבוי" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "שם תמונת המצב" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "להסיר את תמונת המצב?" msgstr[1] "להסיר את תמונות המצב?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "ליצור עותקי גיבוי עם {suffix} כסיומת.\n" "בטרם שכתוב או הסרה של רכיבים מקומיים." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "לשחזר רק קבצים שלא נמצאים או\n" "שהם חדשים יותר מאלו ביעד.\n" "באמצעות האפשרות „rsync --update”." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "הסרת רכיבים חדשים יותר בתיקייה המקורית." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "לשחזר את הרכיב הזה לתיקייה החדשה\n" "{path}?" msgstr[1] "" "לשחזר את הרכיבים האלה לתיקייה החדשה\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "לשחזר את הרכיב הזה?" msgstr[1] "לשחזר את הרכיבים האלה?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "להסיר את כל הקבצים החדשים יותר תחת {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "להסיר את כל הקבצים החדשים בתיקיית המקור שלך?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}אזהרה{BOLDEND}: מחיקת קבצים בשורש מערכת הקבצים יכולה לשבש את כל המערכת" " שלך." #: qt/app.py:1750 msgid "Snapshot" msgstr "תמונת מצב" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "שחזור {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "לשחזר את {path} אל…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 msgid "System default" msgstr "ברירת מחדל המערכת" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:188 #, 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/languagedialog.py:217 msgid "translation platform" msgstr "פלטפורמת תרגום" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "התרגום שלך" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "תצוגת היומן האחרון" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "תצוגת יומן תמונות מצב" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "פרופיל:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "תמונות מצב:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "סינון:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "הכול" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "שינויים" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "שגיאות" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "פרטים" msgstr[1] "פרטים" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] שגיאה, [I] מידע, [C] שינוי" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "פענוח נתיבים" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "שאלה" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "פרופיל: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "הצגת היומן האחרון" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "הפעלת {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "בעבודה…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "נשלחו:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "מהירות:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "גיבויים" #: qt/qttools.py:427 msgid "Today" msgstr "היום" #: qt/qttools.py:434 msgid "Yesterday" msgstr "אתמול" #: qt/qttools.py:443 msgid "This week" msgstr "השבוע" #: qt/qttools.py:450 msgid "Last week" msgstr "שבוע שעבר" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "זה לא גיבוי אלא מבט עכשוי על הקבצים המקומיים שלך" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "בדיקה אחרונה {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "הצגת היומן המלא" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "מארח:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "פתחה:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "משתמש:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "ניהול פרופילים" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "עריכה" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "הוספה" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "הסרה" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&כללי" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "מצב:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "איפה לשמור גיבויים" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "תיקייה" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "הגדרות SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "נתיב:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "צופן:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "מפתח פרטי:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "נא לבחור קובץ מפתח פרטי קיים (בדרך כלל נקרא „id_rsa”)" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "יצירת מפתח SSH חדש ללא סיסמה (אסור אם כבר נבחר מפתח פרטי)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "סיסמה" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "שמירת הסיסמה לצרור המפתחות" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "שמירת סיסמה ל־Cron במטמון (סיבות אבטחה: משתמש על - root יכול לקרוא סיסמה)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "הגדרות מתקדמות" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "נתיב תמונת מצב מלא:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "תזמון" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "מושבת" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "בכל טעינה/הפעלה מחדש של מערכת" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "כל דקה" msgstr[1] "כל {n} דקות" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "כל שעה" msgstr[1] "כל {n} שעות" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "כל שעה" msgstr[1] "כל {n} שעות" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "שעות מותאמות אישית" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "כל יום" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "במחזוריות (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "כאשר כונן מתחבר (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "כל שבוע" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "כל חודש" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "כל שנה" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "יום בשבוע:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "שעה:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "שעות:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "הרצת Back In Time באופן מחזורי. שימוש אם המחשב לא דולק קבוע." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "כל:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "שעה/ות" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "יום/ימים" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "שבוע/ות" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "חודש/ים" #: qt/settingsdialog.py:555 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:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "לכלול" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "לכלול קבצים ותיקיות" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "הוספת קובץ" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "הוספת תיקייה" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "לה&חריג" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "החרגת תבניות, קבצים או תיקיות" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "הוספת ברירת מחדל" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "החרגת קבצים שגדולים מאשר:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "החרגת קבצים שגדולים מהערך {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "ה&סרה אוטומטית" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "לפני:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "שנה/ים" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "אם המקום הפנוי קטן מאשר:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "אם כמות ה־inodes הפנויה קטנה מאשר:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "הסרה חכמה:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "הרצה ברקע במארח המרוחק." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "נסיוני" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "לשמור את כל תמונות המצב האחרונות במשך" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "יום/ימים." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "לשמור תמונת מצב ביום למשך" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "לשמור תמונת מצב בשבוע למשך" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "שבוע/שבועות." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "לשמור תמונת מצב בחודש למשך" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "חודש/חודשים." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "לשמור תמונת מצב אחת מכל שנה." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "לא להסיר תמונות מצב עם שם." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&אפשרויות" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "אפשר הודעות" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "השבת גיבויים כאשר על בטרייה" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "מצב הכוח אינו זמין מהמערכת" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "להריץ שמירה של תמונת מצב אחת כל פעם" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "גיבוי הקבצים המוחלפים בשחזור" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "להמשיך כשיש שגיאות (לשמור על תמונות מצב חלקיות)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "להשתמש בסכומי בדיקה לאיתור שינויים" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "לשמור תמונת מצב גם אם לא נערכו שינויים." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "רמת פירוט יומן:" #: qt/settingsdialog.py:911 msgid "None" msgstr "ללא" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "אפשרויות &מומחים" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "אזהרה: עדיף לשנות את ההגדרות האלה רק אם ברור לך לחלוטין מה תהיה התוצאה." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "הרצת ‚rsync’ עם ‚{cmd}’:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "כמשימת cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "במארח המרוחק" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "בעת שמירת תמונת מצב ידנית" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(נא להתקין ‚nocache’ כדי להפעיל את האפשרות הזאת)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "במכונה המקומית" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "הפניית stdout ל־‎/dev/null במשימות ה־cron." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "הפניית stderr ל־‎/dev/null במשימות ה־cron." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "הגבלת ניצולת רוחב הפס של rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "ק״ב/שנייה" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "שימור הרשאות ובעלות" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "שימור מאפיינים מורחבים (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "העתקת קישורים בלתי מהימנים (עובד רק עם קישורים מוחלטים)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "חובה לשים אפשרויות במירכאות, למשל: ‎{example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "הדבקת אפשרויות נוספות ל־rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "הוספת קידומת לפקודות SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, 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/settingsdialog.py:1196 msgid "default" msgstr "ברירת מחדל" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "לבדוק אם המארח המרוחק מחובר" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "לבדוק האם המארח המרוחק תומך בכל הפקודות הנחוצות." #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "שחזור הגדרות" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "עריכת משוב משתמש" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "פרופיל חדש" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "שינוי שם לפרופיל" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "למחוק את הפרופיל „{name}”?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}מומלץ בחום{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "לייצר צמד מפתחות ציבורי/פרטי ללא סיסמה?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "קובץ המפתח הפרטי „{file}” לא קיים." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "להעתיק את מפתח ה־SSH הציבורי שלך למארח המרוחק כדי לאפשר כניסה ללא סיסמה?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "אי אפשר לאמת את האמינות של {host}." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "נא לאמת את טביעת האצבע הזאת. להוסיף אותה לקובץ ה־‚known_hosts’ שלך?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "הכללת תבנית" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "הכללת קובץ" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "הכללת תיקיה" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "לכלול קובץ" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "לכלול תיקיה" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "להחליף את תיקיית תמונות המצב?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "יצירת מפתח SSH חדש תחת {path} נכשלה." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(ברירת מחדל: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "מושבת" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "מופעל" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "ייבוא הגדרות" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "לא נמצאו הגדרות" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "ייבוא" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "נא לבחור את תיקיית תמונות המצב ממנה ייובא קובץ ההגדרות. הנתיב יכול להיראות " "כך: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "אם התיקייה נמצאת בכונן חיצוני או מרוחק, יש לעגן אותה ידנית תחילה." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "אפשרויות להשוואת תמונות מצב" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "פקודה:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "משתנים:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "להשתמש ב־%1 וב־%2 כמשתני נתיב" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "נא להגדיר פקודת diff או לבטל." #: qt/snapshotsdialog.py:90 #, 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:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "לא מוגדרים משתנים לפקודת diff (הבדלים). נעשה שימוש בערך ברירת המחדל " "„{params}”." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "תמונות מצב עם שינויים בלבד" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "להציג רק תמונות מצב שזהות ל־:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "בדיקה עמוקה (מדויקת יותר, אך אטית)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "מחיקה" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "בחירה בהכול" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "השוואה" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "מעבר אל" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "אפשרויות" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "לא ניתן להשוות תמונת מצב לעצמה." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "למחוק את {file} בתמונת המצב {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "למחוק את {file} ב־{count} תמונות מצב?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "אזהרה: אי אפשר לשלול את זה." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "להחריג את {path} מתמונות מצב עתידיות?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "לא ניתן למצוא crontab.\n" #~ "האם cron מותקן?\n" #~ "אם לא יש לבטל את כל הגיבויים האוטומטיים." #~ msgid "Full snapshot path" #~ msgstr "נתיב מלא לתמונת מצב" #~ msgid "Mode" #~ msgstr "מצב" #~ msgid "Profile" #~ msgstr "פרופיל" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "פרופיל ‚{profile}’: נא למלא סיסמה עבור {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "שורת הפיקוד - Shebang בסקריפט משוב משתמש אינה ניתנת להפעלה." #~ msgid "WARNING" #~ msgstr "אזהרה" #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "לסקריפט משוב משתמש אין שורת פיקוד - shebang‏ (‎#/bin/sh)." backintime-1.5.2/common/po/hr.po000066400000000000000000001213461465446530500165270ustar00rootroot00000000000000# Croatian 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \n" "Language-Team: Croatian \n" "Language: hr\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.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Upozorenje" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Glavni profil" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "" #: common/config.py:309 msgid "Local" msgstr "" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "" #: common/config.py:314 msgid "Local encrypted" msgstr "" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "" #: common/config.py:320 msgid "SSH encrypted" msgstr "" #: common/config.py:327 msgid "Default" msgstr "" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Mapa snimki nije valjana !" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Morate odabrati barem jednu mapu za rezervu !" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:447 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} nije mapa." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "" #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nije moguće zapisati u: {path}\n" "Provjerite imate li pristup zapisivanju ?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "" #: common/config.py:497 msgid "Expert Options" msgstr "Stručne opcije" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "" #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "" #: common/configfile.py:107 msgid "Failed to save config" msgstr "" #: common/configfile.py:143 msgid "Failed to load config" msgstr "" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" već postoji." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "" #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "" #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "" #: common/encfstools.py:151 msgid "Cancel" msgstr "" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Molim Vas da potvrdite lozinku." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Uzmi snimku" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "" #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "NEUSPJEŠNO" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Spremi dopuštenje" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Završeno" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Nije moguće pronaći mapu snimki.\n" "Ako se nalazi na izmjenjivom disku, molimo, spojite ga." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Čekanje %s sekunda." msgstr[1] "Čekanje %s sekundi." msgstr[2] "Čekanje %s sekundi." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Neuspjelo uzimanje snimke {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Završavanje" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Nije moguće napraviti mapu" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Spremi datoteku postavki …" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Spremi dopuštenje …" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Nije moguće ukloniti mapu" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Uzmi snimku" #: common/snapshots.py:1417 msgid "Success" msgstr "" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Smart uklanjanje" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Ukloni stare snimke" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Pokušaj sačuvati min slobodno mjesta" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Pokušaj sačuvati min {perc} slobodno mjesta" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Sada" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Nije moguće napraviti mapu." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Molim Vas unesite lozinku za korisnika \"{user}\"" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "O programu" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "" #: qt/app.py:169 msgid "Shortcuts" msgstr "Prečaci" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:256 msgid "Add to Include" msgstr "" #: qt/app.py:258 msgid "Add to Exclude" msgstr "" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Nije moguće napraviti mapu" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Nije moguće pronaći mapu snimki.\n" "Ako je na izmjenjivom disku, molimo, spojite ga i pritisnite OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Uzmi snimku" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Osvježi popis snimki" #: qt/app.py:508 msgid "Name snapshot" msgstr "Uzmi snimku" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Ukloni Snimku" #: qt/app.py:516 msgid "View snapshot log" msgstr "Prikaži Zapis Snimke" #: qt/app.py:520 msgid "View last log" msgstr "Prikaži Zadnji Zapis" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Glavni profil…" #: qt/app.py:528 msgid "Shutdown" msgstr "" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:532 msgid "Setup language…" msgstr "" #: qt/app.py:536 msgid "Exit" msgstr "Izlaz" #: qt/app.py:540 msgid "Help" msgstr "Pomoć" #: qt/app.py:544 msgid "Profiles config file" msgstr "Spremi datoteku postavki" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Web stranica" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "" #: qt/app.py:553 msgid "FAQ" msgstr "" #: qt/app.py:556 msgid "Ask a question" msgstr "" #: qt/app.py:559 msgid "Report a bug" msgstr "" #: qt/app.py:562 msgid "Translation" msgstr "" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Povrati" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Povrati…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:595 msgid "Up" msgstr "Gore" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Prikaži skrivene datoteke" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Uzmi snimku…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 msgid "&Restore" msgstr "&Povrati" #: qt/app.py:682 msgid "&Help" msgstr "Po&moć" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Jeste li sigurni da želite ukloniti snimku" #: qt/app.py:987 msgid "Working:" msgstr "Radim:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Završeno, nije potrebna rezerva" #: qt/app.py:1044 msgid "Working" msgstr "Radim" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "" #: qt/app.py:1076 msgid "Sent" msgstr "" #: qt/app.py:1077 msgid "Speed" msgstr "" #: qt/app.py:1078 msgid "ETA" msgstr "" #: qt/app.py:1140 msgid "Global" msgstr "Globalno" #: qt/app.py:1141 msgid "Root" msgstr "Korijen" #: qt/app.py:1142 msgid "Home" msgstr "Početak" #: qt/app.py:1170 msgid "Backup folders" msgstr "Mape rezerve" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Ime Snimke" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Jeste li sigurni da želite ukloniti snimku" msgstr[1] "Jeste li sigurni da želite ukloniti snimku" msgstr[2] "Jeste li sigurni da želite ukloniti snimku" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "Neuspjelo uzimanje snimke {snapshot_id} !!!" msgstr[1] "Neuspjelo uzimanje snimke {snapshot_id} !!!" msgstr[2] "Neuspjelo uzimanje snimke {snapshot_id} !!!" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Jeste li sigurni da želite ukloniti snimku" msgstr[1] "Jeste li sigurni da želite ukloniti snimku" msgstr[2] "Jeste li sigurni da želite ukloniti snimku" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" #: qt/app.py:1750 msgid "Snapshot" msgstr "Snimke" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Povrati {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Povrati {path}…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 msgid "System default" msgstr "" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:188 #, 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 "" "Pozdrav\n" "Već ste više puta upotrebljavali Back In Time na sljedećem jeziku: {language}.\n" "Prijevod vaše inačice programa Back In Time na {language} dovršen je {perc}. Bez obzira na tehničke sposobnosti koje imate, možete se pridružiti projektu prevođenja te tako doprinositi programu Back In Time.\n" "Ako želite sudjelovati, posjetite {translation_platform_url}. Za dodatna pitanja i pomoć posjetite {back_in_time_project_website}.\n" "Ispričavamo se što smo Vas prekinuli, ova se poruka više neće prikazivati. Ubuduće je možete pronaći na izborniku za pomoć.\n" "Tim programa Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Snimke:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Sve" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Promjene" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Grješke" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Informacije" msgstr[1] "Informacije" msgstr[2] "Informacije" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Grješka, [I] Informacije, [C] Promjeni" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Prikaži Zadnji Zapis" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Radim…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Snimke" #: qt/qttools.py:427 msgid "Today" msgstr "Danas" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Jučer" #: qt/qttools.py:443 msgid "This week" msgstr "Ovaj tjedan" #: qt/qttools.py:450 msgid "Last week" msgstr "Prošli tjedan" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Domaćin:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Korisnik:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Glavni profil" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Uredi" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Općenito" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Gdje sačuvati snimke" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Napredno" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Raspored" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Onemogućeno" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Pri svakom paljenju/podizanju" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Svakih {n} minuta" msgstr[1] "Svakih {n} minuta" msgstr[2] "Svakih {n} minuta" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "" msgstr[1] "" msgstr[2] "" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Svakih {n} sati" msgstr[1] "Svakih {n} sati" msgstr[2] "Svakih {n} sati" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Svaki dan" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Svaki tjedan" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Svaki mjesec" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Svake godine" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Sat:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Sat:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Svakih:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dan(a)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Tjedan(a)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:555 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:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Uključi" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Ukljući mape i datoteke" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Dodaj datoteku" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Dodaj mapu" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Isključi" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Iskljući sljedove, datoteke ili mape" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Isključi datoteku:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "" #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Auto-ukloni" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Starije od:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Godina" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Ako je slobodan prostor manji od:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Ako je slobodan prostor manji od:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Smart uklanjanje:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Dan(a)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Tjedan(a)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Nemoj uklanjati imenovane snimke." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "Op&cije" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Omogući obavijesti" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Onesposobi snimke tijekom rada na bateriji" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Stanje energije nije dostupno od sustava" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Nastavi ne grješkama (zadrži nepotpune snimke)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Razina Zapisa:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nijedno" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "&Stručne opcije" #: qt/settingsdialog.py:936 #, fuzzy msgid "" "Caution: 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:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Sačuvaj ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, 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/settingsdialog.py:1196 msgid "default" msgstr "" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Novi profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Preimenuj profil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Jeste li sigurni da želite obrisati profil \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Visoko preporučeno" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Isključi slijed" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Isključi datoteku" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Isključi mapu" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Uključi datoteku" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Uključi mapu" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Jeste li sigurni da želite promjeniti mapu snimki?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Naredba" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametri:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Dubinska provjera (preciznije, ali sporije)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Idi Na" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opcije" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Nije moguće usporediti snimku s njom samom." #: qt/snapshotsdialog.py:398 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Neuspjelo uzimanje snimke {snapshot_id} !!!" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Nije moguće pronaći crontab.\n" #~ "Provjerite jeli cron instaliran ?\n" #~ "Ako nije, onesposobite sve automatske rezerve." #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil '{profile}': Unesite zaporku za {mode}: " backintime-1.5.2/common/po/hu.po000066400000000000000000001552251465446530500165350ustar00rootroot00000000000000# 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. # # FIRST AUTHOR , 2009. # Kósa Lajos , 2013. msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Figyelmeztetés" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Fő profil" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "titkosított" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH-val titkosított" #: common/config.py:309 msgid "Local" msgstr "Helyi" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH személyes kulcs" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "titkosított" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Titkosítás" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH-val titkosított" #: common/config.py:327 msgid "Default" msgstr "Alapértelmezett" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: „{name}”" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "A pillanatképek mappája nem érvényes!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Legalább egy mappát ki kell jelölnie a biztonsági mentéshez!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "A biztonsági mentés mappáját nem lehet felvenni." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "A biztonsági mentés almappáját nem lehet felvenni." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Érvénytelen kapcsoló. A(z) {path} nem mappa." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "A gép-, felhasználó- vagy profilazonosító nem lehet üres." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nem lehet írni ide: {path}\n" "Biztosan van írási joga?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native 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 Linux fájlrendszert." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. 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 a {expertOptions}" " alatt." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Hivatkozások másolása (szimbolikus hivatkozások megszüntetése)" #: common/config.py:497 msgid "Expert Options" msgstr "Szakértői beállítások" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. 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/config.py:1658 msgid "Failed to write new crontab." msgstr "Nem sikerült az új crontab írása." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Az udev ütemezése nem működik a(z) {mode} móddal" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Nem található a(z) „{path}” UUID-ja" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Nem sikerült elmenteni a beállításokat" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Nem sikerült betölteni a beállításokat" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "A(z) „{name}” profil már létezik." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Az utolsó profilt nem lehet eltávolítani." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Nem lehet csatolni a(z) „{command}” parancsot" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "A titkosított mappa beállítása nem található." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Létrehoz egy új titkosított mappát?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Mégse" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Erősítse meg a jelszót." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "A jelszó nem egyezik." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Pillanatkép készítése" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" "Nem lehet leválasztani a(z) {mountprocess} folyamatot a(z) {mountpoint} " "csatolási pontról." #: common/mount.py:696 #, 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 ezt: {installcommand}" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "A(z) {mntpoint} csatolási pont nem üres." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Írja be a jelszót a(z) {mode} \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "SIKERTELEN" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Jogosultságok helyreállítása" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Kész" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Biztonsági mentés elhalasztása akkumulátorról való működésnél" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Nem található a pillanatképek mappája.\n" "Ha cserélhető meghajtón van, akkor helyezze be." #: common/snapshots.py:839 #, 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:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Nem sikerült a(z) {snapshot_id} pillanatkép készítése." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Véglegesítés" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Nem lehet mappát létrehozni" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Beállítófájl mentése…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Jogosultságok mentése…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "A(z) {snapshot_id} maradvány található, amely folytatható." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "A(z) {snapshot_id} maradványmappa eltávolítása az utolsó futásból" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Nem lehet eltávolítani a mappát" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Pillanatkép készítése" #: common/snapshots.py:1417 msgid "Success" msgstr "Sikeres" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Részleges átvitel egy hiba miatt" #: common/snapshots.py:1421 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/snapshots.py:1425 #, 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/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Nézze meg a „man rsync” kézikönyvet a további részletekért" #: common/snapshots.py:1445 msgid "" "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/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Semmi sem változott, nincs szükség új pillanatképre" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Nem lehet átnevezni a(z) {new_path} útvonalat erre: {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Intelligens eltávolítás" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Régi pillanatképek eltávolítása" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Kísérlet a legkisebb szabad hely megtartására" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Kísérlet legalább {perc} szabad inode megtartására" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Most" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Nem lehet csatolni: {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" "Az „ssh-agent” nem található. Győződjön meg arról, hogy telepítve van-e." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "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/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "A(z) {cipher} titkosító meghiúsult a(z) {host} kiszolgálónál." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "A távoli útvonal létezik, de nem könyvtár." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "A távoli útvonal nem írható." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "A távoli útvonal nem futtatható." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Nem sikerült létrehozni a távoli útvonalat." #: common/sshtools.py:981 #, 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/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Nézze meg a „man backintime” parancsot a további utasításokért" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "A parancsok ellenőrzése a(z) {host} kiszolgálón ismeretlen hibát adott " "vissza" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" "A(z) {host} távoli kiszolgáló nem támogatja a rögzített hivatkozásokat" #: common/sshtools.py:1164 #, fuzzy, 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/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Adja meg „{user}” felhasználó jelszavát" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Névjegy" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Szerzők" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Fordítások" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licenc" #: qt/app.py:169 msgid "Shortcuts" msgstr "Gyorsbillentyűk" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Ez a mappa nem létezik a jelenleg\n" "kiválasztott pillanatképben." #: qt/app.py:256 msgid "Add to Include" msgstr "Hozzáadás a felvettekhez" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Hozzáadás a kizártakhoz" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "{app_name} először lett elindítva, ezért nem található konfiguráció." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "Betölti a beállításokat (egy mentésből vagy egy másik gépről)?" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Nem lehet mappát létrehozni" #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Ha cserélhető meghajtón van, akkor helyezze be, és nyomja meg az OK gombot." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Pillanatkép készítése" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "A módosítás ideje és a méret használata a fájlváltoztatások felismeréséhez." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Pillanatkép készítése (ellenőrzőösszeg mód)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Ellenőrzőösszegek használata a fájlváltoztatások felismeréséhez." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pillanatkép folyamatának szüneteltetése" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Pillanatkép folyamatának folytatása" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Pillanatkép folyamatának leállítása" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Pillanatképek listájának frissítése" #: qt/app.py:508 msgid "Name snapshot" msgstr "Pillanatkép elnevezése" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Pillanatkép eltávolítása" #: qt/app.py:516 msgid "View snapshot log" msgstr "Pillanatkép naplójának megtekintése" #: qt/app.py:520 msgid "View last log" msgstr "Utolsó napló megtekintése" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Profilok kezelése…" #: qt/app.py:528 msgid "Shutdown" msgstr "Leállítás" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "A rendszer leállítása a pillanatkép befejezése után." #: qt/app.py:532 msgid "Setup language…" msgstr "Nyelv beállítása…" #: qt/app.py:536 msgid "Exit" msgstr "Kilépés" #: qt/app.py:540 msgid "Help" msgstr "Súgó" #: qt/app.py:544 msgid "Profiles config file" msgstr "Profilok beállítófájlja" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Webhely" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Változásnapló" #: qt/app.py:553 msgid "FAQ" msgstr "GYIK" #: qt/app.py:556 msgid "Ask a question" msgstr "Kérdés feltevése" #: qt/app.py:559 msgid "Report a bug" msgstr "Hiba jelentése" #: qt/app.py:562 msgid "Translation" msgstr "Fordítás" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "A fordításban segédkezők megtekintése." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Ismét megjeleníti az EncFS eltávolításának üzenetét." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Helyreállítás" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "A kijelölt fájlok vagy mappák helyreállítása az eredeti célhelyre." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Helyreállítás ide…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "A kijelölt fájlok vagy mappák helyreállítása egy új célhelyre." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "A jelenleg megjelenített mappa és annak teljes tartalmának helyreállítása az" " eredeti célhelyre." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "A jelenleg megjelenített mappa és annak teljes tartalmának helyreállítása " "egy új célhelyre." #: qt/app.py:595 msgid "Up" msgstr "Fel" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Rejtett fájlok megjelenítése" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Pillanatképek összehasonlítása…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Vissza az időbe" #: qt/app.py:665 msgid "&Backup" msgstr "&Biztonsági mentés" #: qt/app.py:676 msgid "&Restore" msgstr "&Helyreállítás" #: qt/app.py:682 msgid "&Help" msgstr "&Súgó" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Ha bezárja ezt az ablakot, akkor a Back In Time nem lesz képes leállítani a " "rendszert a pillanatkép befejezése után." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Valóban be szeretné zárni?" #: qt/app.py:987 msgid "Working:" msgstr "Munkavégzés:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Kész, nincs szükség biztonsági mentésre" #: qt/app.py:1044 msgid "Working" msgstr "Munkavégzés" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Hiba" #: qt/app.py:1076 msgid "Sent" msgstr "Elküldve" #: qt/app.py:1077 msgid "Speed" msgstr "Sebesség" #: qt/app.py:1078 msgid "ETA" msgstr "Becsült hátralévő idő" #: qt/app.py:1140 msgid "Global" msgstr "Globális" #: qt/app.py:1141 msgid "Root" msgstr "Gyökér" #: qt/app.py:1142 msgid "Home" msgstr "Saját mappa" #: qt/app.py:1170 msgid "Backup folders" msgstr "Biztonsági mentés mappái" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Pillanatkép neve" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Biztosan el szeretné távolítani ezt a pillanatképet?" msgstr[1] "Biztosan el szeretné távolítani ezeket a pillanatképeket?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Biztonsági mentés másolatok létrehozása {suffix}\n" "végződéssel a helyi elemek felülírása vagy eltávolítása előtt." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "A fájlok újabb verziói átnevezésre kerülnek {suffix} végződéssel a " "helyreállítás előtt. Ha már nincs rájuk többé szüksége, akkor az alábbi " "paranccsal eltávolíthatja azokat:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Csak azon elemek helyreállítása, amelyek nem\n" "léteznek, vagy újabbak a célhelyen lévőknél.\n" "Az „rsync --update” kapcsoló használata." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Újabb elemek eltávolítása az eredeti mappából." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "A kijelölt fájlok vagy mappák helyreállítása az eredeti célhelyre, valamint " "a pillanatképben nem szereplő fájlok vagy mappák törlése. Legyen rendkívül " "óvatos, mert ez törölni fogja azokat a fájlokat és mappákat, amelyek a " "pillanatkép készítésekor ki lettek zárva." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Valóban helyre szeretné állítani ezt az elemet az új\n" "{path} mappába?" msgstr[1] "" "Valóban helyre szeretné állítani ezeket az elemeket az új\n" "{path} mappába?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Valóban helyre szeretné állítani ezt az elemet?" msgstr[1] "Valóban helyre szeretné állítani ezeket az elemeket?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "Biztosan el szeretné távolítani a(z) {path} mappában lévő összes újabb " "fájlt?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Biztosan el szeretné távolítani az eredeti mappában lévő összes újabb fájlt?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your 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:1750 msgid "Snapshot" msgstr "Pillanatkép" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "{path} helyreállítása" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "{path} helyreállítása ide…" #: qt/app.py:1948 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/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Az EncFS támogatása a közel jövőben meg fog szünni. Nem javasolt a profilhoz" " való használata." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "Leírás" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Az alábbi profil(ok) használ(nak) EncFS titkosítást:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Nyelv beállítása" #: qt/languagedialog.py:92 msgid "System default" msgstr "Rendszer alapértelmezettje" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Az operációs rendszer nyelvének használata." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Lefordítva: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Ü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/languagedialog.py:217 msgid "translation platform" msgstr "fordítási platform" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Az Ön fordítása" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Utolsó napló megtekintése" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Pillanatkép naplójának megtekintése" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Pillanatképek:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Szűrő:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Összes" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Változtatások" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Hibák" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Információk" msgstr[1] "Információk" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync átviteli hibák (kísérleti)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] hiba, [I] információ, [C] változtatás" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "útvonalak visszafejtése" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Kérdés" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: „{profile_name}”" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Utolsó napló megtekintése" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "{appname} indítása" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Munkavégzés…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Elküldve:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Sebesség:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Pillanatképek" #: qt/qttools.py:427 msgid "Today" msgstr "Ma" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Tegnap" #: qt/qttools.py:443 msgid "This week" msgstr "Ez a hét" #: qt/qttools.py:450 msgid "Last week" msgstr "Múlt hét" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Ez NEM pillanatkép, hanem a helyi fájlok élő nézete" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Utolsó ellenőrzés {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Teljes napló megjelenítése" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Kiszolgáló:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Felhasználó:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Profilok kezelése" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Szerkesztés" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Hozzáadás" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Eltávolítás" #: qt/settingsdialog.py:210 msgid "&General" msgstr "Á<alános" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Mód:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Hová mentse a pillanatképeket" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Mappa" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH-beállítások" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Útvonal:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Titkosító:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Személyes kulcs:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Meglévő személyes kulcsfájl kiválasztása (általában „id_rsa” néven)" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Új SSH-kulcs létrehozása jelszó nélkül (nem engedélyezett, ha egy személyes " "kulcsfájl már ki van választva)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Jelszó" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Jelszó mentése a kulcstartóba" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Jelszó gyorsítótárazása a Cron számára (biztonsági probléma: a rendszergazda" " elolvashatja a jelszót)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Speciális" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Teljes pillanatkép útvonala:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Ütemezés" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Letiltva" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Minden indításkor vagy újraindításkor" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "{n} percenként" msgstr[1] "{n} percenként" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Óránként" msgstr[1] "Óránként" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "{n} óránként" msgstr[1] "{n} óránként" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Egyéni órák" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Naponta" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Ismételten (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Ha meghajtó kerül csatlakoztatásra (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Hetente" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Havonta" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Évente" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Hét napja:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Óra:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Óra:" #: qt/settingsdialog.py:521 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:528 msgid "Every:" msgstr "Minden:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Óra" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Nap" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Hét" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Hónap" #: qt/settingsdialog.py:555 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 "" "A Back In Time futtatása, amint a meghajtó csatlakoztatásra kerül (X naponta egyszer).\n" "Kérni fogja a sudo jelszót." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Felvétel" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Fájlok és mappák felvétele" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Fájl hozzáadása" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Mappa hozzáadása" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Kizárás" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Minták, fájlok vagy mappák kizárása" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Alapértelmezett hozzáadása" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Ennél nagyobb fájlok kizárása:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Ennél nagyobb fájlok kizárása: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "A(z) %(prefix)s előtagban lévő értéknél nagyobb fájlok kizárása.\n" "A letiltott „Teljes rsync mód” használatával ennek csak az új fájlokra\n" "van hatása, mert az rsync esetén ez egy átviteli kapcsoló, nem kizáró\n" "kapcsoló. Tehát a korábban biztonsági mentett nagy fájlok megmaradnak\n" "a pillanatképekben, akkor is ha megváltoztak." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Automatikus eltávolítás" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Régebbi mint:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Év" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Ha a szabad hely kevesebb mint:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Ha a szabad inode-ok száma kevesebb mint:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Intelligens eltávolítás:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Futtatás a háttérben a távoli kiszolgálón." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "KÍSÉRLETI" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Az összes pillanatkép megtartása a legutóbbi" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "napról." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Napi egy pillanatkép megtartása a legutóbbi" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Heti egy pillanatkép megtartása a legutóbbi" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "hétről." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Havi egy pillanatkép megtartása a legutóbbi" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "hónapról." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Évi egy pillanatkép megtartása minden évről." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ne távolítsa el az elnevezett pillanatképeket." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Beállítások" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Értesítések engedélyezése" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Pillanatképek letiltása akkumulátorról való működésnél" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Az energiaellátás állapota nem érhető el a rendszerről" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Egyszerre csak egy pillanatkép futtatása" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "A többi pillanatkép tiltva lesz, amíg az aktuális pillanatkép elkészül.\n" "Ez egy globális beállítás. Tehát a felhasználó összes profiljára hatással lesz.\n" "De ezt az összes többi felhasználónál is be kell kapcsolnia." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Lecserélt fájlok biztonsági mentése a helyreállításkor" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "A fájlok újabb verziói átnevezésre kerülnek {suffix} végződéssel a " "helyreállítás előtt. Ha már nincs rájuk többé szüksége, akkor a(z) {cmd} " "paranccsal eltávolíthatja azokat." #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Folytatás hibák esetén (befejezetlen pillanatképek megtartása)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Ellenőrzőösszeg használata a változtatások felismeréséhez" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" "Új pillanatkép készítése, függetlenül attól, hogy voltak-e változtatások " "vagy sem." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Naplózási szint:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nincs" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "S&zakértői beállítások" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Vigyázat! Csak akkor változtassa meg ezeket a beállításokat, ha valóban " "tudja, hogy mit csinál." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Az „rsync” futtatása a(z) „{cmd}” paranccsal:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "cron-feladatként" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "távoli kiszolgálón" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "kézi pillanatkép készítésekor" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Telepítse a „nocache” programot ezen beállítás engedélyezéséhez)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "helyi gépen" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" "A szabványos kimenet átirányítása a /dev/null helyre a cron-feladatokban." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" "A szabványos hibakimenet átirányítása a /dev/null helyre a cron-" "feladatokban." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "Az rsync sávszélesség-használatának korlátozása" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/mp" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "ACL megőrzése" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Kiterjesztett attribútumok (xattr) megőrzése" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Nem biztonságos hivatkozások másolása (csak abszolút hivatkozásokkal " "működik)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "A kapcsolókat idézőjelek közé kell tenni, például: {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "További kapcsolók beillesztése az rsync-be" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Előtag hozzáadása az SSH-parancsokhoz" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Minden parancs előtt futtatandó előtag a távoli kiszolgálón.\n" "A változókat el kell fedni a \\$FOO visszafelé mutató perjelekkel.\n" "Ez nem érinti az rsync-et. Tehát az rsync számára történő\n" "előtag-hozzáadáshoz használjon „%(cbRsyncOptions)s” kapcsolókat\n" "%(rsync_options_value)s értékkel\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "alapértelmezett" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Annak ellenőrzése, hogy a távoli kiszolgáló elérhető-e" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Figyelmeztetés: ha le van tiltva és a távoli\n" "kiszolgáló nem érhető el, akkor ez néhány\n" "furcsa hibát eredményezhet." #: qt/settingsdialog.py:1218 #, fuzzy msgid "Check if remote host supports all necessary commands." msgstr "" "Annak ellenőrzése, hogy a távoli kiszolgáló támogatja-e az összes szükséges " "parancsot" #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Figyelmeztetés: ha le van tiltva és a távoli\n" "kiszolgáló nem támogatja az összes szükséges parancsot,\n" "akkor ez néhány furcsa hibát eredményezhet." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Beállítások helyreállítása" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Felhasználó-visszahívás szerkesztése" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Új profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Profil átnevezése" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Biztosan törölni szeretné a(z) „{name}” profilt?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Erősen ajánlott" #: qt/settingsdialog.py:1634 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 "" "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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Nem választott személyes kulcsfájlt az SSH-hoz.\n" "Szeretne egy új, jelszómentes nyilvános és személyes kulcspárt előállítani?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "A(z) „{file}” személyes kulcsfájl nem létezik." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Szeretné a nyilvános SSH-kulcsát a távoli kiszolgálóra\n" "másolni a jelszómentes bejelentkezés engedélyezéséhez?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "A(z) {host} kiszolgáló hitelességét nem lehet megállapítani.\n" "\n" "A(z) {keytype} kulcs ujjlenyomata:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Ellenőrizze ezt az ujjlenyomatot! Szeretné hozzáadni a „known_hosts” " "fájlhoz?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Minta kizárása" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Fájl kizárása" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Mappa kizárása" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Fájl felvétele" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "A(z) „{path}” útvonal egy szimbolikus hivatkozás. A hivatkozott cél nem kerül biztonsági mentésre, amíg azt is fel nem veszi.\n" "Szeretné a szimbolikus hivatkozás célját felvenni helyette?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Mappa felvétele" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Biztosan meg szeretné változtatni a pillanatképek mappáját?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Nem sikerült létrehozni új SSH-kulcsot a(z) {path} mappában" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "alapértelmezett" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "letiltva" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "engedélyezve" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Nem találhatók beállítások" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Pillanatképek összehasonlítására vonatkozó beállítások" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Parancs" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Paraméterek:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Használjon %1 és %2 helykitöltőket az útvonal paramétereihez" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Csak eltérő pillanatképek" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Csak ezzel azonos pillanatképek felsorolása: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Alapos ellenőrzés (pontosabb, de lassú)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Törlés" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Összes kijelölése" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Összehasonlítás" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Ugrás ide" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Beállítások" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Nem lehet összehasonlítani egy pillanatképet önmagával." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Valóban törölni szeretné a(z) „{file}” fájlt a(z) „{snapshot_id}” " "pillanatképből?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Valóban törölni szeretné a(z) „{file}” fájlt {count} pillanatképből?" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "Nem lehet visszavonni!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Kizárja a(z) „{path}” útvonalat a jövőbeli pillanatképekből?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "A crontab nem található.\n" #~ "Biztos benne, hogy a cron telepítve van?\n" #~ "Ha nincs, akkor le kell tiltania az automatikus biztonsági mentéseket." #~ msgid "Full snapshot path" #~ msgstr "Teljes pillanatkép útvonala" #~ msgid "Mode" #~ msgstr "Mód" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "„{profile}” profil: adja meg a jelszót a(z) {mode} módhoz: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "" #~ "A felhasználó-visszahívási parancsfájlban lévő parancsértelmezőt megadó sor " #~ "nem futtatható." #~ msgid "WARNING" #~ msgstr "FIGYELMEZTETÉS" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "Az encfs 1.7.2-es és korábbi verzióinál a --reverse kapcsoló hibásan " #~ "működik. Frissítse az encfs programot." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "" #~ "A felhasználó-visszahívási parancsfájlnak nincs parancsértelmezőt megadó " #~ "(#!/bin/sh) sora." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "A(z) {app} EncFS-t használ a titkosításhoz. Egy nemrég végzett biztonsági " #~ "ellenőrzés számos lehetséges támadási vektort fedezett fel erre vonatkozóan." #~ " Nézze meg az „A NOTE ON SECURITY” fejezetet a „man backintime” " #~ "kézikönyvben." backintime-1.5.2/common/po/id.po000066400000000000000000001535331465446530500165150ustar00rootroot00000000000000# Indonesian 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-08 06:59+0000\n" "Last-Translator: buhtz \n" "Language-Team: Indonesian \n" "Language: id\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.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Peringatan" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Profil utama" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Lokal (terenkripsi EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (terenkripsi EncFS)" #: common/config.py:309 msgid "Local" msgstr "Lokal" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Kunci privat SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Terenkripsi secara lokal" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Enkripsi" #: common/config.py:320 msgid "SSH encrypted" msgstr "Terenkripsi secara SSH" #: common/config.py:327 msgid "Default" msgstr "Baku" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Folder snapshot tidak valid!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Anda harus memilih setidaknya satu folder untuk backup!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Folder backup tidak boleh disertakan." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Sub folder backup tidak bisa disertakan." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opsi tidak valid. {path} bukan sebuah folder." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID tidak boleh kosong." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Tak bisa menulis ke: {path}\n" "Apakah Anda yakin memiliki hak untuk menulis?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Sistem berkas tujuan untuk {path} diformat menggunakan FAT yang tidak " "mendukung hard-link. Mohon menggunakan sistem berkas Linux yang native." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Sistem berkas tujuan untuk {path} adalah share yang dipasang dengan SMB. " "Pastikan SMB server remote mendukung symlink atau aktifkan {copyLinks} pada " "{expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Salin link (dereference link simbolik)" #: common/config.py:497 msgid "Expert Options" msgstr "Opsi Tingkat Lanjut" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Sistem berkas tujuan untuk {path} adalah share yang dipasang dengan sshfs. " "sshfs tidak mendukung hard-link. Harap gunakan mode 'SSH' sebagai pengganti." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Gagal menulis crontab baru." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron tidak berjalan walaupun perintah crontab tersedia. Tugas backup " "terjadwal tidak akan berjalan. Cron mungkin terpasang tapi tidak " "difungsikan. Cobalah perintah \"systemctl enable cron\" atau konsultasikan " "ke kanal dukungan dari distribusi GNU Linux Anda." #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Gagal memasang peraturan Udev untuk profil {profile_id}. Layanan DBus " "'{dbus_interface}' tidak tersedia" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Penjadwalan udev tidak dapat digunakan dengan mode {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Tidak bisa menemukan UUID untuk {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Gagal menyimpan konfigurasi" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Gagal memuat konfigurasi" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" sudah ada." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Profil terakhir tidak dapat dihapus." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Tidak bisa mengait '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Konfigurasi untuk folder yang terenkripsi tidak ditemukan." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Buat folder terenkripsi yang baru?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Batal" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Mohon konfirmasi kata sandi." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Kata sandi tidak cocok." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Ambil snapshot" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Tidak dapat melepas kait {mountprocess} dari {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} tidak ditemukan. Silakan pasang (misalnya melalui " "\"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Titik kait {mntpoint} tidak kosong." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Masukkan kata sandi untuk profil {mode} \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "GAGAL" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Pulihkan izin" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Selesai" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Tunda backup saat menggunakan baterai" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Tidak dapat menemukan folder snapshot.\n" "Jika ada di hardisk eksternal atau perangkat lainnya silakan ditancapkan." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Menunggu %s detik." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Gagal untuk mengambil snapshot {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Menyelesaikan" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Tidak bisa membuat folder" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Menyimpan berkas konfigurasi…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Menyimpan izin…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Ditemukan sisa snapshot {snapshot_id} yang dapat dilanjutkan." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Membuang sisa folder snapshot {snapshot_id} dari proses sebelumnya" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Tidak bisa membuang folder" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Mengambil snapshot" #: common/snapshots.py:1417 msgid "Success" msgstr "Sukses" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Transfer sebagian karena kesalahan" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Transfer sebagian karena berkas sumber yang menghilang (lihat 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' berakhir dengan kode keluar {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Lihat 'man rsync' untuk lebih jelasnya" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Kode keluar rsync negatif adalah nomor sinyal, lihat 'kill -l' dan 'man " "kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Tidak ada yang berubah, tidak perlu membuat snapshot baru" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Tidak bisa mengubah nama {new_path} ke {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Pembuangan pintar" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Membuang snapshot lama" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Mencoba menjaga ruang kosong minimum" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Mencoba menjaga minimum {perc} inode bebas" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Sekarang" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Tidak bisa mengait {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent tidak ditemukan. Pastikan sudah terpasang." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Tidak dapat membuka kunci kunci privat SSH. Kata sandi yang salah atau tidak" " tersedia untuk cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cipher {cipher} gagal untuk {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Path jauh ada tapi sayangnya bukan sebuah direktori." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Path jauh tidak dapat ditulisi." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Path jauh tidak dapat dieksekusi." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Tidak bisa membuat path jauh." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Host {host} jauh tidak mendukung {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Lihat 'man backintime' untuk instruksi lebih lanjut" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Pemeriksaan perintah pada host {host} mengembalikan kesalahan yang tak " "dikenal" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Host {host} jauh tidak mendukung hardlink" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Salin ssh-key publik \"{pubkey}\" ke host jarak jauh \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Mohon masukkan kata sandi untuk \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Tentang" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Penulis" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Terjemahan" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Lisensi" #: qt/app.py:169 msgid "Shortcuts" msgstr "Pintasan" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Folder ini tidak ada\n" "dalam snapshot yang sedang dipilih." #: qt/app.py:256 msgid "Add to Include" msgstr "Tambahkan untuk Disertakan" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Tambahkan pada Perkecualian" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} tampaknya dijalankan untuk pertama kali karena tidak ditemukan " "konfigurasi." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Impor konfigurasi yang ada (dari folder target cadangan atau komputer lain)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Tidak bisa menemukan folder snapshot." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Jika folder tersebut ada pada drive lepasan harap tancapkan lalu tekan OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Ambil snapshot" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Gunakan waktu & ukuran modifikasi untuk deteksi perubahan berkas." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Ambil snapshot (mode checksum)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Gunakan checksum untuk deteksi perubahan berkas." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Istirahatkan proses snapshot" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Lanjutkan proses snapshot" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Hentikan proses snapshot" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Segarkan daftar snapshot" #: qt/app.py:508 msgid "Name snapshot" msgstr "Namai snapshot" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Buang snapshot" #: qt/app.py:516 msgid "View snapshot log" msgstr "Lihat catatan log snapshot" #: qt/app.py:520 msgid "View last log" msgstr "Lihat catatan log terakhir" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Kelola profil…" #: qt/app.py:528 msgid "Shutdown" msgstr "Matikan" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Matikan sistem setelah snapshot selesai." #: qt/app.py:532 msgid "Setup language…" msgstr "Siapkan bahasa…" #: qt/app.py:536 msgid "Exit" msgstr "Keluar" #: qt/app.py:540 msgid "Help" msgstr "Bantuan" #: qt/app.py:544 msgid "Profiles config file" msgstr "Berkas konfigurasi profil" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Situs Web" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Perubahan" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ" #: qt/app.py:556 msgid "Ask a question" msgstr "Ajukan sebuah pertanyaan" #: qt/app.py:559 msgid "Report a bug" msgstr "Laporkan sebuah bug" #: qt/app.py:562 msgid "Translation" msgstr "Terjemahan" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Menampilkan pesan tentang partisipasi dalam penerjemahan lagi." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Transisi Enkripsi (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Menampilkan pesan tentang penghapusan EncFS lagi." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Pulihkan" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Pulihkan berkas atau folder yang dipilih ke tujuan asal." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Pulihkan ke …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Pulihkan berkas atau folder yang dipilih ke sebuah tujuan baru." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Pulihkan folder yang saat ini ditampilkan dan semua isinya ke tujuan awal." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Pulihkan folder yang saat ini ditampilkan dan semua isinya ke tujuan baru." #: qt/app.py:595 msgid "Up" msgstr "Naik" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Tampilkan berkas tersembunyi" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Bandingkan snapshot…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Backup" #: qt/app.py:676 msgid "&Restore" msgstr "&Pulihkan" #: qt/app.py:682 msgid "&Help" msgstr "B&antuan" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Jika Anda menutup jendela ini, Back In Time tidak akan mampu mematikan " "sistem Anda ketika snapshot selesai dibuat." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Anda yakin ingin menutupnya?" #: qt/app.py:987 msgid "Working:" msgstr "Sedang Bekerja:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Selesai, backup tidak diperlukan" #: qt/app.py:1044 msgid "Working" msgstr "Sedang Bekerja" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Kesalahan" #: qt/app.py:1076 msgid "Sent" msgstr "Terkirim" #: qt/app.py:1077 msgid "Speed" msgstr "Kecepatan" #: qt/app.py:1078 msgid "ETA" msgstr "ETA" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Home" #: qt/app.py:1170 msgid "Backup folders" msgstr "Folder backup" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nama Snapshot" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Anda yakin hendak membuang snapshot ini?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Buat salinan cadangan dengan {suffix} di belakang\n" "sebelum menimpa atau menghapus elemen lokal." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Versi lebih baru dari berkas akan diubah nama dengan tambahan {suffix} di " "belakang sebelum memulihkan. Jika Anda tidak memerlukannya lagi Anda dapat " "menghapusnya dengan perintah berikut:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Hanya pulihkan berkas yang tidak ada atau\n" "lebih baru dari yang ada di tujuan.\n" "Gunakan opsi \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Buang elemen yang lebih baru di folder asal." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Memulihkan berkas atau folder yang dipilih ke tujuan asal dan menghapus " "berkas/folder yang tidak ada dalam snapshot. Mesti sangat berhati-hati " "karena ini akan menghapus berkas/folder yang dikecualikan pada saat " "pengambilan snapshot." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Anda yakin ingin memulihkan elemen ini ke dalam folder baru\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Anda yakin ingin memulihkan elemen ini?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Anda yakin ingin membuang semua berkas yang lebih baru dalam {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Anda yakin ingin membuang semua berkas yang lebih baru dalam folder asal?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}Peringatan{BOLDEND}: Menghapus berkas-berkas dalam sistem berkas root " "dapat merusak seluruh sistem Anda." #: qt/app.py:1750 msgid "Snapshot" msgstr "Snapshot" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Pulihkan {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Pulihkan {path} ke …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "Pengaturan bahasa hanya berlaku setelah memulai ulang Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Dukungan untuk EncFS akan dihentikan di masa mendatang. Tidak disarankan " "lagi untuk memakai mode itu bagi suatu profil." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Suatu keputusan tentang pengganti bagi dukungan lanjutan dari backup " "terenkripsi masih tertunda, bergantung pada ketersediaan kontributor dan " "sumber daya proyek. Rincian lebih jauh tersedia di {whitepaper} ini." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "dokumen" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Dukungan bagi profil snapshot terenkripsi sedang mengalami perubahan " "signifikan, dan EncFS akan dihapus di masa mendatang." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Profil berikut memakai enkripsi dengan EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Suatu keputusan tentang penggantian bagi dukungan lanjutan dari backup " "terenkripsi masih tertunda, bergantung pada ketersediaan kontributor dan " "sumber daya proyek. Pengguna diundang untuk bergabung ke diskusi ini. " "Rincian yang diperbarui tentang langkah-langkah selanjutnya tersedia dalam " "{whitepaper} ini." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Pesan ini tidak akan ditampilkan lagi. Dialog ini tersedia kapan pun melalui" " menu bantuan." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Tim Back In Time Anda" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Siapkan bahasa" #: qt/languagedialog.py:92 msgid "System default" msgstr "Baku sistem" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Gunakan bahasa sistem operasi." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Diterjemahkan: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Halo\n" "Kini Anda sudah menggunakan Back In Time dalam bahasa {language} beberapa kali.\n" "Penerjemahan ke dalam {language} dari versi Back In Time yang terpasang telah {perc} selesai. Terlepas dari tingkat keahlian teknismu, Anda bisa berkontribusi dalam penerjemahan maupun Back In Time itu sendiri.\n" "Bila ingin terlibat, silakan kunjungi {translation_platform_url}. Untuk bantuan dan pertanyaan lebih lanjut, silakan kunjungi {back_in_time_project_website}.\n" "Maaf atas gangguannya, dan pesan ini tidak akan ditampilkan lagi. Dialog ini tersedia kapan saja melalui menu bantuan.\n" "Tim Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "platform penerjemahan" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Terjemahan Anda" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Tampilan Log Terakhir" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Tampilan Log Snapshot" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Snapshot:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Semua" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Perubahan" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Kesalahan" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informasi" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "kegagalan transfer rsync (eksperimen)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[K] Kesalahan, [I] Informasi, [U] Ubah" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "dekode path" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Pertanyaan" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Lihat Catatan Log Terakhir" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Mulai {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Sedang Bekerja…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Terkirim:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Kecepatan:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Snapshot" #: qt/qttools.py:427 msgid "Today" msgstr "Hari ini" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Kemarin" #: qt/qttools.py:443 msgid "This week" msgstr "Minggu ini" #: qt/qttools.py:450 msgid "Last week" msgstr "Minggu lalu" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Ini BUKAN sebuah snapshot melainkan tampilan langsung dari berkas-berkas " "lokal Anda" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Pemeriksaan terakhir {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Tampilkan log lengkap" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Proksi SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Host:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Pengguna:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Kelola profil" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Sunting" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Tambah" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Buang" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Umum" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Mode:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Lokasi penyimpanan snapshot" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Folder" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Pengaturan SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Path:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Cipher:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Kunci Privat:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Pilih berkas kunci privat yang sudah ada (umumnya dinamai \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Buat kunci SSH baru tanpa kata sandi (tidak diizinkan jika kunci privat " "sudah dipilih)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Kata Sandi" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Simpan Kata Sandi ke Ring Kunci" #: qt/settingsdialog.py:378 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/settingsdialog.py:390 msgid "Advanced" msgstr "Tingkat Lanjut" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Path snapshot lengkap:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Jadwal" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Dinonaktifkan" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Pada setiap boot/reboot" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Setiap {n} menit" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Setiap {n} jam" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Setiap {n} jam" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Waktu Khusus" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Setiap Hari" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Berulang (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Ketika drive tersambung (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Setiap minggu" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Setiap bulan" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Setiap tahun" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Hari:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Hari kerja:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Jam:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Jam:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Jalankan Back In Time secara berulang. Ini berguna bila komputer tidak " "dijalankan secara berkala." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Setiap:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Jam" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Hari" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Minggu" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Bulan" #: qt/settingsdialog.py:555 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 "" "Jalankan Run Back In Time segera setelah drive terhubung (hanya sekali setiap X hari).\n" "Anda akan ditanya untuk kata sandi sudo Anda." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Fungsikan pencatatan log dari pesan-pesan debug" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "Menulis pesan-pesan level debug ke dalam log sistem melalui \"--debug\"." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Waspada: Hanya gunakan ini sementara untuk diagnostik, karena itu " "menghasilkan keluaran yang sangat banyak." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Sertakan" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Menyertakan berkas dan folder" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Tambah berkas" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Tambah folder" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "K&ecualikan" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Abaikan pola, berkas, atau folder" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Tambahkan baku" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Abaikan berkas yang lebih dari:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Abaikan berkas yang lebih dari {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Dengan 'Mode rsync lengkap' dimatikan, ini hanya akan mempengaruhi berkas-" "berkas baru karena untuk rsync ini merupakan pilihan transfer, bukan opsi " "pengabaian. Maka berkas-berkas besar yang telah dicadangkan sebelumnya akan " "tetap berada dalam snapshot-snapshot bahkan setelah mereka berubah." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Bu&ang Otomatis" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Lebih lama dari:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Tahun" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Jika ruang kosong kurang dari:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Jika inode bebas kurang dari:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Penghapusan pintar:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Jalankan di latar belakang pada host jarak jauh." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTAL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Simpan semua snapshot selama" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "hari terakhir." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Simpan satu snapshot tiap hari selama" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Simpan satu snapshot tiap minggu selama" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "minggu terakhir." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Simpan satu snapshot tiap bulan selama" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "bulan terakhir." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Simpan satu snapshot tiap tahun untuk semua tahun." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Jangan buang snapshot yang telah dinamai." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opsi" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Fungsikan pemberitahuan" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Matikan snapshot ketika menggunakan baterai" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Status daya tidak tersedia dari sistem" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Jalankan hanya satu snapshot pada suatu saat" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Snapshot-snapshot lain akan diblok hingga snapshot yang sekarang berjalan " "selesai. Ini adalah pilihan global. Jadi ini akan mempengaruhi semua profil " "untuk pengguna ini. Tetapi Anda harus mengaktifkan ini untuk pengguna " "lainnya juga." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Backup menggantikan berkas-berkas saat pemulihan" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Versi lebih baru dari berkas akan diubah nama dengan tambahan {suffix} di " "belakang sebelum memulihkan. Jika Anda tidak memerlukannya lagi Anda dapat " "menghapusnya dengan {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" "Lanjukan saat ada kesalahan (simpan snapshot-snapshot yang tidak lengkap)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Gunakan checksum untuk mendeteksi perubahan" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Ambil snapshot baru walaupun ada perubahan atau tidak." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Level Log:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nihil" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Opsi Tingkat &Lanjut" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Hati-hati: Ubah opsi ini hanya jika Anda benar-benar tahu apa yang Anda " "lakukan." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Jalankan 'rsync' dengan '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "sebagai cron job" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "pada host jarak jauh" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "ketika mengambil snapshot manual" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Mohon pasang 'nocache' untuk mengaktifkan opsi ini)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "pada mesin lokal" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Arahkan ulang stdout ke /dev/null dalam cronjobs." #: qt/settingsdialog.py:1005 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:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Arahkan ulang stderr ke /dev/null dalam cronjobs." #: qt/settingsdialog.py:1016 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:1024 msgid "Limit rsync bandwidth usage:" msgstr "Batasi penggunaan bandwidth rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/detik" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Pertahankan ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Pertahankan atribut tambahan (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Salin tautan yang tidak aman (hanya bisa untuk tautan absolut)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Batasi ke satu sistem berkas" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opsi harus diapit tanda kutip mis.: {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Tempelkan opsi tambahan ke rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Tambahkan prefiks ke perintah-perintah SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefiks yang dijalankan sebelum setiap perintah pada host jarak jauh." #: qt/settingsdialog.py:1188 #, 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 "" "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:1196 msgid "default" msgstr "baku" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Periksa apakah host jarak jauh sedang daring" #: qt/settingsdialog.py:1214 msgid "" "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:1218 msgid "Check if remote host supports all necessary commands." msgstr "" "Periksa apakah host jarak jauh mendukung semua perintah yang diperlukan." #: qt/settingsdialog.py:1221 msgid "" "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:1237 msgid "Restore Config" msgstr "Pulihkan Konfigurasi" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Sunting user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "Dukungan bagi EncFS akan dihentikan di masa mendatang. Suatu keputusan " "tentang penggantian untuk dukungan lanjutan dari backup terenkripsi masih " "tertunda, bergantung kepada ketersediaan kontributor dan sumber daya proyek." " Rincian lebih banyak tersedia di {whitepaper} ini." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Profil baru" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Ubah nama profil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Anda yakin ingin menghapus profil \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "{BOLD}Sangat disarankan{ENDBOLD}: (Semua saran telah disertakan.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Sangat disarankan{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "Jam ubahan hanya dapat berupa daftar jam yang dipisah koma (contoh: " "8,12,18,23) atau */3 untuk backup terjadwal setiap 3 jam." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Anda tidak memilih suatu berkas kunci privat bagi SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Apakah Anda ingin membuat pasangan baru kunci publik/privat tanpa kata " "sandi?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Berkas kunci privat \"{file}\" tidak ada." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Apakah Anda ingin menyalin kunci publik SSH Anda ke host jarak jauh untuk " "mengaktifkan login tanpa kata sandi?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Keaslian host \"{host}\" tidak dapat diyakinkan." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "Sidik jari kunci {keytype} adalah:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Mohon memeriksa sidik jari ini. Apakah Anda ingin menambahkannya ke dalam " "berkas 'known_hosts' Anda?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Abaikan pola" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Abaikan berkas" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Abaikan folder" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Sertakan berkas" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" adalah sebuah symlink. Tujuan yang ditaut tidak akan dicadangkan hingga Anda juga menyertakannya.\n" "Apakah Anda ingin menyertakan tujuan yang di-symlink sebagai penggnti?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Sertakan folder" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Anda yakin ingin mengubah folder snapshot?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Gagal membuat kunci SSH baru dalam {path}." #: qt/settingsdialog.py:2310 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:2369 msgid "(default: {})" msgstr "(baku: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "dinonaktifkan" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "difungsikan" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Impor konfigurasi" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Konfigurasi tidak ditemukan" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Impor" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Pilih folder snapshot tempat asal berkas konfigurasi mesti diimpor. Path-nya" " mungkin seperti ini: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Bila folder terletak pada drive eksternal atau jarak jauh, itu mesti dikait " "secara manual sebelumnya." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opsi tentang membandingkan snapshot" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Perintah:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parameter:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Gunakan %1 dan %2 untuk parameter path" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Harap atur suatu perintah diff atau tekan Batal." #: qt/snapshotsdialog.py:90 #, 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:98 #, 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:134 msgid "Differing snapshots only" msgstr "Hanya snapshot yang berbeda" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Hanya cantumkan daftar snapshot yang sama dengan:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Pemeriksaan mendalam (lebih akurat, tetapi lambat)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Hapus" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Pilih Semua" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Bandingkan" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Pergi Ke" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opsi" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Anda tidak bisa membandingkan sebuah snapshot dengan dirinya sendiri." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Apakah Anda yakin ingin menghapus \"{file}\" dalam snapshot " "\"{snapshot_id}\"?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Apakah Anda yakin ingin menghapus \"{file}\" dalam {count} snapshot?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "PERINGATAN: Ini tidak dapat dicabut." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Jangan ikutkan \"{path}\" dari snapshot mendatang?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Tidak dapat menemukan crontab. Apakah Anda yakin cron sudah terpasang? Jika " #~ "tidak, Anda harus mematikan semua backup otomatis." #~ msgid "Full snapshot path" #~ msgstr "Path snapshot lengkap" #~ msgid "Mode" #~ msgstr "Mode" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil '{profile}': Masukkan kata sandi untuk {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang dalam skrip user-callback tidak dapat dijalankan." #~ msgid "WARNING" #~ msgstr "PERINGATAN" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs versi 1.7.2 dan sebelumnya mempunyai bug ketika menggunakan opsi " #~ "--reverse. Silakan perbaharui encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "skrip user-callback tidak memiliki baris shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} menggunakan EncFS untuk enkripsi. Pemeriksaan keamanan baru-baru ini " #~ "menemukan beberapa kemungkinan vektor serangan untuk ini. Mohon melihat " #~ "\"CATATAN TENTANG KEAMANAN\" dalam \"man backintime\"." backintime-1.5.2/common/po/is.po000066400000000000000000001352731465446530500165350ustar00rootroot00000000000000# Sveinn í Felli , 2023. msgid "" msgstr "" "Project-Id-Version: Icelandic (Back In Time)\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-05 09:28+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Aðvörun" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Aðalsnið" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "Staðvært dulritað" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH dulritað" #: common/config.py:309 msgid "Local" msgstr "Staðvært" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH-einkalykill" #: common/config.py:314 msgid "Local encrypted" msgstr "Staðvært dulritað" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Dulritun" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH dulritað" #: common/config.py:327 msgid "Default" msgstr "Sjálfgefið" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Snið: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Mappa fyrir skyndiafrit er ekki gild!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Þú verður að velja a.m.k. eina möppu til að öryggisafrita!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Aftrits mappa getur ekki verið innifalin." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Aftrits undirmappa getur ekki verið innifalin." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ógildur valkostur: {path} er ekki mappa." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID getur ekki verið tómt." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Get ekki skrifað í: {path}\n" "Ertu viss um að þú hafir skrifaðgang þarna ?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Markskráakerfi fyrir {path} er í FAT formi sem virkar ekki með hörðum-" "tenglum. Notaðu frekar upprunalegt Linux skráakerfi." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. 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/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Afrita tengla (afbyggja tákntengi - dereference symbolic links)" #: common/config.py:497 msgid "Expert Options" msgstr "Ítarlegri valkostir" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. 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/config.py:1658 msgid "Failed to write new crontab." msgstr "Mistókst að skrifa nýtt crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Áætlað udev virkar ekki með hamnum {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Gat ekki fundið UUID fyrir {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Mistókst að vista stillingaskrá" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Mistókst að hlaða inn stillingaskrá" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Sniðið \"{name}\" er þegar til staðar." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Ekki er hægt að fjarlægja síðasta sniðið." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Get ekki tengt '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Stillingar fyrir dulritaða möppu fannst ekki." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Búa til nýja dulritaða möppu?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Hætta við" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Staðfestu lykilorðið." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Lykilorðin samsvara ekki." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Taka skyndiafrit" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Get ekki aftengt {mountprocess} úr {mountpoint}." #: common/mount.py:696 #, fuzzy, 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/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Tengipunktur {mntpoint} er ekki laus." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Settu inn lykilorð fyrir {mode}-notkunarsniðið \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "MISTÓKST" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Endurheimta heimildir" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Búið" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Fresta afritun þegar rafhlöður eru í notkun" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" #: common/snapshots.py:839 #, 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:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Mistókst að taka skyndiafritið {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Geng frá" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Get ekki búið til möppu" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Vista stillingaskrá…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Vista aðgangsheimildir…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Get ekki fjarlægt möppu" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Taka skyndiafrit" #: common/snapshots.py:1417 msgid "Success" msgstr "Tókst" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Skoðaðu 'man rsync' til að sjá frekari upplýsingar" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Gat ekki endurnefnt {new_path} sem {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Snjöll fjarlæging" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Fjarlægja gömul skyndiafrit" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Reyna að halda í lágmarksmagn af lausu plássi" #: common/snapshots.py:1929 #, 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/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Núna" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Get ekki tengt {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" "ssh-agent fannst ekki. Gakktu úr skugga um að það sé uppsett á vélinni." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Fjartengd slóð er til, en er ekki mappa." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Fjartengd slóð er ekki skrifanleg." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Fjartengd slóð er ekki keyranleg." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Tókst ekki að útbúa fjartengda slóð." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Fjartengda vélin {host} styður ekki {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Fjartengda vélin {host} styður ekki harðtengi (hardlinks)" #: common/sshtools.py:1164 #, 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/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Settu inn lykilorð fyrir \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Um forritið" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Höfundar" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Þýðingar" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Notkunarleyfi" #: qt/app.py:169 msgid "Shortcuts" msgstr "Flýtilyklar" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:256 msgid "Add to Include" msgstr "Bæta við meðfylgjandi" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Bæta við útilokun" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Get ekki fundið möppu fyrir skyndiafrit." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" #: qt/app.py:481 msgid "Take a snapshot" msgstr "Taka skyndiafrit" #: qt/app.py:483 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:486 msgid "Take a snapshot (checksum mode)" msgstr "Taka skyndiafrit (í gátsummuham)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Nota gátsummu til að skynja breytingar á skrám." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Gera hlé á vinnslu skyndiafrits" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Halda áfram með vinnslu skyndiafrits" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Stöðva vinnslu skyndiafrits" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Endurlesa lista yfir skyndiafrit" #: qt/app.py:508 msgid "Name snapshot" msgstr "Nefndu skyndiafrit" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Fjarlægja skyndiafrit" #: qt/app.py:516 msgid "View snapshot log" msgstr "Skoða atvikaskrá skyndiafrita" #: qt/app.py:520 msgid "View last log" msgstr "Skoða síðustu atvikaskrá" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Sýsla með notkunarsnið…" #: qt/app.py:528 msgid "Shutdown" msgstr "Slökkva" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Slökkva á kerfi þegar skyndiafritun lýkur." #: qt/app.py:532 msgid "Setup language…" msgstr "Setja upp tungumál…" #: qt/app.py:536 msgid "Exit" msgstr "Hætta" #: qt/app.py:540 msgid "Help" msgstr "Hjálp" #: qt/app.py:544 msgid "Profiles config file" msgstr "Stillingaskrá notkunarsniða" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Vefsvæði" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Breytingasaga" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ / Algengar spurningar" #: qt/app.py:556 msgid "Ask a question" msgstr "Spyrja spurninga" #: qt/app.py:559 msgid "Report a bug" msgstr "Tilkynna um villu" #: qt/app.py:562 msgid "Translation" msgstr "Þýðing" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Endurheimta" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Endurheimta valdar skrár eða möppur á upprunalega staðsetningu." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Endurheimta í …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Endurheimta valdar skrár eða möppur á nýja staðsetningu." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Endurheimta sýnda möppu og allt innihald hennar á upprunalega staðsetningu." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Endurheimta sýnda möppu og allt innihald hennar á nýja staðsetningu." #: qt/app.py:595 msgid "Up" msgstr "Upp" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Sýna faldar skrár" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Bera saman skyndiafrit…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time öryggisafritun" #: qt/app.py:665 msgid "&Backup" msgstr "&Öryggisafrit" #: qt/app.py:676 msgid "&Restore" msgstr "Endu&rheimta" #: qt/app.py:682 msgid "&Help" msgstr "&Hjálp" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Ef þú lokar þessum glugga, mun Back In Time ekki geta slökkt á kerfinu þegar" " skyndiafritun lýkur." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Viltu örugglega loka þessu?" #: qt/app.py:987 msgid "Working:" msgstr "Að vinna:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Búið, engin öryggisafritun nauðsynleg" #: qt/app.py:1044 msgid "Working" msgstr "Að vinna" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Villa" #: qt/app.py:1076 msgid "Sent" msgstr "Sent" #: qt/app.py:1077 msgid "Speed" msgstr "Hraði" #: qt/app.py:1078 msgid "ETA" msgstr "Áætluð lok" #: qt/app.py:1140 msgid "Global" msgstr "Víðvært" #: qt/app.py:1141 msgid "Root" msgstr "Kerfisstjóri (root)" #: qt/app.py:1142 msgid "Home" msgstr "Einkamappa (Home)" #: qt/app.py:1170 msgid "Backup folders" msgstr "Öryggisafritunarmöppur" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Heiti skyndiafrits" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Ertu viss um að þú viljir fjarlægja þetta skyndiafrit?" msgstr[1] "Ertu viss um að þú viljir fjarlægja þessi skyndiafrit?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Fjarlægja nýjar skrár í upprunalegri möppu." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Ertu viss um að þú viljir endurheimta þetta atriði í nýju möppuna\n" "{path}?" msgstr[1] "" "Ertu viss um að þú viljir endurheimta þessi atriði í nýju möppuna\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to 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/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Ertu viss um að þú viljir fjarlægja allar nýrri skrár í {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Ertu viss um að þú viljir fjarlægja allar nýrri skrár í upprunalegri möppu?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" #: qt/app.py:1750 msgid "Snapshot" msgstr "Skyndiafrit" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Endurheimta {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Endurheimta {path} í …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 #, fuzzy msgid "Your Back In Time Team" msgstr "Back In &Time öryggisafritun" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Setja upp tungumál" #: qt/languagedialog.py:92 msgid "System default" msgstr "Sjálfgefið í kerfinu" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Nota tungumál stýrikerfis." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Þýtt: {percent}" #: qt/languagedialog.py:188 #, 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 "" "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/languagedialog.py:217 msgid "translation platform" msgstr "þýðingakerfið" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Þýðing þín" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Síðasta virka sýn atvikaskráningar" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Sýn á atvikaskrá skyndiafrita" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Notkunarsnið:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Skyndiafrit:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Sía:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Allt" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Breytingar" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Villur" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Upplýsingar" msgstr[1] "Upplýsingar" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Villa, [I] Upplýsingar, [C] Breyta" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "afkóða slóðir" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Spurning" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Snið: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Skoða síðasta annál" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Ræsa {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Í vinnslu…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Sent:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Hraði:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "Áætluð lok:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Skyndiafrit" #: qt/qttools.py:427 msgid "Today" msgstr "Í dag" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Í gær" #: qt/qttools.py:443 msgid "This week" msgstr "Í þessari viku" #: qt/qttools.py:450 msgid "Last week" msgstr "Í síðustu viku" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Síðasta athugun {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Birta fullan annál" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH-milliþjónn" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Miðlari:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Gátt:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Notandi:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Sýsla með notkunarsnið" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Breyta" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Bæta við" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Fjarlægja" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Almennt" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Hamur:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Hvar á að vista skyndiafrit" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Mappa" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH stillingar" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Slóð:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Dulritunaraðferð (cipher):" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Einkalykill:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Lykilorð" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Vista lykilorð í lyklakippu" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Nánar" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Heildarslóð skyndiafrita:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Vinnuáætlun" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Óvirkt" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Við hverja ræsingu/endurræsingu" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Á {n} mínútu fresti" msgstr[1] "Á {n} mínútna fresti" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Á klukkustundar fresti" msgstr[1] "Á {n} klukkustunda fresti" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "{n} klukkustundar fresti" msgstr[1] "Á {n} klukkustunda fresti" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Sérsniðnar klukkustundir" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Hvern dag" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Endurtekið (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Þegar drif er tengt (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Vikulega" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Mánaðarlega" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Árlega" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Dagur:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Vikudagur:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Klukkustund:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Klukkustundir:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Hverjar:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "klukkustund(ir)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "dag(a)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "viku(r)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "mánuð(ir)" #: qt/settingsdialog.py:555 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:564 msgid "Enable logging of debug messages" msgstr "Virkja atvikaskráningu villuleitarskilaboða" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "Ta&ka með" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Hafa með skrár og möppur" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Bæta við skrá" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Bæta við möppu" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Undanskilja" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Undanskilja mynstur, skrár eða möppur" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Bæta við sjálfgefnu" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Hunsa skrár stærri en:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Hunsa skrár stærri en gildi í {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Sjálfvirk fj&arlæging" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Eldri en:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "ár" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Ef laust pláss er minna en:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Ef lausir i-hnútar (inodes) eru færri en:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Snjöll fjarlæging:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Keyra í bakgrunni á fjartengdri vél." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "Á TILRAUNASTIGI" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Geyma öll skyndiafrit síðustu" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dag(a)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Geyma eitt skyndiafrit á dag síðustu" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Geyma eitt skyndiafrit á viku síðustu" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "viku(r)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Geyma eitt skyndiafrit á mánuði síðustu" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mánuði(r)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Geyma eitt skyndiafrit á ári." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ekki fjarlægja nefnd skyndiafrit." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "Valk&ostir" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Virkja tilkynningar" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Koma í veg fyrir skyndiafritun þegar rafhlaða er í notkun" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Staða rafhlöðu er ekki tiltæk frá kerfinu" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Keyra aðeins eitt skyndiafrit í einu" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Öryggisafrit skipti út skrám við endurheimtingu" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Halda áfram við villur (geyma ókláruð skyndiafrit)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Nota gátsummu til að skynja breytingar" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Taka skyndiafrit sama hvort nokkuð hafi breyst eða ekki." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Stig atvikaskráningar:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Ekkert" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Ítarlegri &valkostir" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Varlega: Ekki breyta þessum stillingum nema að þú vitir hvað þú ert að gera." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Keyra 'rsync' með '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "sem cron-verk" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "á fjartengdri vél" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "við að taka handvirkt skyndiafrit" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Settu upp 'nocache' til að geta notað þennan valkost)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "á staðværri vél" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Endurbeina stdout í /dev/null í cron-verkum." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Endurbeina stderr í /dev/null í cron-verkum." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Takmarka notkun rsync á bandbreidd:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/sek" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Varðveita ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Varðveita ítarleg eigindi (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Afrita ótrygga tengla (virkar aðeins með algilda tengla)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Takmarka við eitt skráakerfi" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Líma viðbótarvalkosti inn í rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Bæta forskeyti við SSH-skipanir" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, 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/settingsdialog.py:1196 msgid "default" msgstr "sjálfgefið" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Athuga hvort fjartengd vél er á netinu" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Athuga hvort fjartengd vél styðji allar nauðsynlegar skipanir." #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "Endurheimta stillingaskrá" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nýtt snið" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Endurnefna snið" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Ertu viss um að þú viljir eyða sniðinu \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Sérstaklega mælt með{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "Þú valdir enga skrá með einkalykli fyrir SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Einkalykilsskráin \"{file}\" er ekki til." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Ekki tókst að sannreyna auðkenni hýsilvélarinnar {host}." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "Fingrafar {keytype} dulritunarlykils er:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Yfirfarðu þetta fingrafar. Myndirðu vilja bæta því í 'known_hosts' skrána " "þina?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Útilokunarmynstur" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Undanskilja skrá" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Undanskilja möppu" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Hafa með skrá" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Hafa með möppu" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Ertu viss um að þú viljir skipta um möppu undir skyndiafrit?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Mistókst að búa til nýjan SSH-lykil í {path}." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(sjálfgefið: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "óvirkt" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "virkt" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Flytja inn uppsetningu" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Engin stillingaskrá fannst" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Flytja inn" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Valkostir við samanburð skyndiafrita" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Skipun:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Viðföng:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Notaðu %1 og %2 fyrir viðföng slóðar" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Veldu aðra diff-skipun eða ýttu á að hætta við." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "Skipunin \"{cmd}\" finnst ekki á þessu kerfi. Prófaðu eitthvað annað eða " "ýttu á að hætta við." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Taka skyndiafrit mismunar" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Telja aðeins upp skyndiafrit sem jafngilda: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Ítarleg yfirferð (nákvæmari, en hægvirkt)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Eyða" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Velja allt" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Bera saman" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Fara í" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Valkostir" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Þú getur ekki borið skyndiafrit saman við sjálft sig." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Ertu viss um að þú viljir eyða {file} í skyndiafritinu {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Ertu viss um að þú viljir eyða {file} í {count} skyndiafritum?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "AÐVÖRUN: Þetta er ekki hægt að afturkalla." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Útiloka {path} framvegis frá skyndiafritun?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Finn ekki crontab. Ertu viss um að cron sé uppsett? Ef ekki, ættirðu að gera" #~ " alla sjálfvirka öryggisafritun óvirka." #~ msgid "Full snapshot path" #~ msgstr "Heildarslóð skyndiafrita" #~ msgid "Mode" #~ msgstr "Hamur" #~ msgid "Profile" #~ msgstr "Snið" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Snið '{profile}': Settu inn lykilorð fyrir {mode}: " #~ msgid "WARNING" #~ msgstr "AÐVÖRUN" backintime-1.5.2/common/po/it.po000066400000000000000000001531551465446530500165350ustar00rootroot00000000000000# Italian 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 07:07+0000\n" "Last-Translator: buhtz \n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Attenzione" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Profilo principale" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "Locale cifrato" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH con cifratura" #: common/config.py:309 msgid "Local" msgstr "Locale" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Chiave privata SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Locale cifrato" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Cifratura" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH con cifratura" #: common/config.py:327 msgid "Default" msgstr "Predefinito" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profilo: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Cartella delle istantanee non valida!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Scegliere almeno una cartella per il backup!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "La cartella di backup non può essere inclusa." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "La sotto-cartella di backup non può essere inclusa." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opzione non valida. {path} non è una cartella." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/Utente/Profilo non possono essere vuoti." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Impossibile scrivere su: {path}\n" "Sei sicuro di avere i permessi in scrittura?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Il filesystem di destinazione per '{path}' è formattato con FAT che non " "supporta hard-link. Si prega di usare un filesystem Linux nativo." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Il filesystem di destinazione per '{path}' è una condivisione montata su " "SMB. Per favore assicurati che il server SMB remoto supporti i collegamenti " "simbolici o attiva '{copyLinks}' in '{expertOptions}'." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Copia collegamenti (segue i collegamenti simbolici)" #: common/config.py:497 msgid "Expert Options" msgstr "Opzioni per esperti" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Il filesystem di destinazione per '{path}' è una condivisione montata su " "sshfs. Sshfs non supporta gli hard-link. Utilizza invece la modalità 'SSH'." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Impossibile scrivere nuovi crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Impossibile installare la regola di Udev per il profilo {profile_id}. Il " "servizio DBus '{dbus_interface}' non era disponibile" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "La pianificazione udev non funziona con la modalità {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Impossibile trovare UUID per \"{path}\"" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Salvataggio della configurazione fallito" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Impossibile caricare la configurazione" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Il profilo \"{name}\" esiste già." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "L'ultimo profilo non può essere rimosso." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Non posso montare '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Configurazione per cartella cifrata non trovata." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Creare una nuova cartella cifrata?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Annulla" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Per favore conferma la password." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "La password non corrisponde." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Prendi istantanea" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Impossibile smontare {mountprocess} da {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} non trovato. Si prega di installarlo (usando ad es. " "{installcommand}\"" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Il punto di montaggio {mntpoint} non è vuoto." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Inserire la password per il profilo {mode} \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "FALLITO" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Ripristina permessi" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Fatto" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Backup posticipato durante funzionamento a batteria" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Impossibile trovare la cartella delle istantanee.\n" "Se si tratta di un disco rimovibile si prega di collegarlo." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Aspettando %s secondo." msgstr[1] "Aspettando %s secondi." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Creazione dell'istantanea {snapshot_id} fallita." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Finalizzazione" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Impossibile creare la cartella" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Salvataggio file di configurazione…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Salvataggio permessi…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Trovate rimanenze di '{snapshot_id}' che può essere continuata." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" "Rimozione della cartella '{snapshot_id}' rimanente dall'ultima esecuzione" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Impossibile eliminare la cartella" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Generazione dell'istantanea" #: common/snapshots.py:1417 msgid "Success" msgstr "Successo" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Trasferimento parziale a causa di un errore" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Trasferimento parziale perché i file di origine sono spariti (vedi 'man " "rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' terminato con codice di uscita {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Vedi 'man rsync' per ulteriori dettagli" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "I codici di ritorno negativi di rsync sono numeri di segnale, vedi 'kill -l'" " e 'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Nulla è cambiato, nessuna nuova istantanea necessaria" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Non posso rinominare {new_path} in {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Rimozione intelligente" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Rimozione vecchie istantanee" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Tentativo di mantenere lo spazio libero minimo" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Tentativo di mantenere almeno il {perc} di inode liberi" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Adesso" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Non riesco a montare {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Non trovo ssh-agent. Accertati che sia installato." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Non ho potuto sbloccare la chiave privata ssh. Password errata o non " "disponibile per cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cifratura {cipher} fallita per {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Il percorso remoto esiste ma non è una cartella." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Il percorso remoto non è scrivibile." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Il percorso remoto non è eseguibile." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Non ho potuto creare il percorso remoto." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "L'host remoto {host} non supporta {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Guarda 'man backintime' per ulteriori istruzioni" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Il controllo dei comandi sull'host {host} ha restituito un errore " "sconosciuto" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "L'host remoto {host} non supporta gli hard link" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Copia la chiave ssh pubblica \"{pubkey}\" sull'host remoto \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Inserire la password per \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Informazioni" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autori" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Traduzioni" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licenza" #: qt/app.py:169 msgid "Shortcuts" msgstr "Collegamenti" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Questa cartella non esiste\n" "nell'istantanea selezionata al momento." #: qt/app.py:256 msgid "Add to Include" msgstr "Aggiungi alle inclusioni" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Aggiungi alle esclusioni" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} sembra essere stato avviato per la prima volta in quanto non è " "stata trovata una configurazione." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Importare una configurazione esistente (da una cartella di destinazione del " "backup o da un altro computer)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Impossibile trovare la cartella delle istantanee." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "Se si trova su un disco rimovibile, collegarlo e premere OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Prendi istantanea" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Usa la dimensione e l'ora di modifica dei file per riconoscere i file " "cambiati." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Prendi istantanea (usando i checksums)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Usa il checksum per rilevare i cambiamenti." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Metti in pausa il processo di istantanea" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Riprendi il processo di istantanea" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Interrompi il processo di istantanea" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Aggiorna lista istantanee" #: qt/app.py:508 msgid "Name snapshot" msgstr "Nome Istantanea" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Rimuovi istantanea" #: qt/app.py:516 msgid "View snapshot log" msgstr "Visualizza registro istantanea" #: qt/app.py:520 msgid "View last log" msgstr "Visualizza ultimo registro" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Gestione profili…" #: qt/app.py:528 msgid "Shutdown" msgstr "Spegnimento" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Arresta il sistema quando l'istantanea è terminata." #: qt/app.py:532 msgid "Setup language…" msgstr "Imposta lingua…" #: qt/app.py:536 msgid "Exit" msgstr "Esci" #: qt/app.py:540 msgid "Help" msgstr "Aiuto" #: qt/app.py:544 msgid "Profiles config file" msgstr "File di configurazione profili" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Sito web" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Registro dei cambiamenti" #: qt/app.py:553 msgid "FAQ" msgstr "Domande frequenti" #: qt/app.py:556 msgid "Ask a question" msgstr "Fai una domanda" #: qt/app.py:559 msgid "Report a bug" msgstr "Segnala un bug" #: qt/app.py:562 msgid "Translation" msgstr "Traduzione" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Ripristina" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Ripristina i file o le cartelle selezionati nella destinazione originale." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Ripristina su…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" "Ripristina i file o le cartelle selezionati in una nuova destinazione." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Ripristina la cartella attualmente mostrata e tutto il suo contenuto nella " "destinazione originale." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Ripristina la cartella attualmente mostrata e tutto il suo contenuto in una " "nuova destinazione." #: qt/app.py:595 msgid "Up" msgstr "Su" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Mostra i file nascosti" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Confronta istantanee…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Backup" #: qt/app.py:676 msgid "&Restore" msgstr "&Ripristina" #: qt/app.py:682 msgid "&Help" msgstr "&Aiuto" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Se chiudi questa finestra Back In Time non sarà in grado di arrestare il " "sistema al termine dell'istantanea." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Vuoi veramente chiuderla?" #: qt/app.py:987 msgid "Working:" msgstr "Lavoro in corso:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Fatto, nessun backup necessario" #: qt/app.py:1044 msgid "Working" msgstr "Lavoro in corso" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Errore" #: qt/app.py:1076 msgid "Sent" msgstr "Inviati" #: qt/app.py:1077 msgid "Speed" msgstr "Velocità" #: qt/app.py:1078 msgid "ETA" msgstr "Tempo rimanente stimato" #: qt/app.py:1140 msgid "Global" msgstr "Globale" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Home" #: qt/app.py:1170 msgid "Backup folders" msgstr "Cartelle di backup" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nome istantanea" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Sei sicuro di voler rimuovere questa istantanea?" msgstr[1] "Sei sicuro di voler rimuovere queste istantanee?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Crea copie di backup aggiungendo {suffix}\n" "prima di sovrascrivere o rimuovere elementi locali." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Le nuove versioni dei file verranno rinominate con il suffisso {suffix} " "prima del ripristino. Se non ne hai più bisogno, puoi rimuoverle con {cmd}:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Ripristina solo gli elementi che non esistono o\n" "sono più recenti di quelli di destinazione.\n" "Utilizza l'opzione \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Rimuovi gli elementi più recenti nella cartella originale." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Ripristina i file o le cartelle selezionati nella destinazione originale e " "elimina i file e le cartelle che non sono nell'istantanea. Fai molta " "attenzione perché questo cancellerà i file e le cartelle che sono stati " "esclusi durante l'acquisizione dell'istantanea." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Vuoi veramente ripristinare questo elemento nella nuova cartella\n" "{path}?" msgstr[1] "" "Vuoi veramente ripristinare questi elementi nella nuova cartella\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Vuoi veramente ripristinare questo elemento?" msgstr[1] "Vuoi veramente ripristinare questi elementi?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Sei sicuro di voler rimuovere tutti i file più recenti in '{path}'?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Sei sicuro di voler rimuovere tutti i file più nuovi nella tua cartella " "originale?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}ATTENZIONE{BOLDEND}: Cancellare i file nella radice del filesystem " "potrebbe danneggiare l'intero sistema." #: qt/app.py:1750 msgid "Snapshot" msgstr "Istantanea" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Ripristina {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Ripristina {path} su…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Le impostazioni della lingua avranno effetto solo dopo aver riavviato Back " "In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 #, fuzzy msgid "Your Back In Time Team" msgstr "Back In &Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Imposta lingua" #: qt/languagedialog.py:92 msgid "System default" msgstr "Default del sistema" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Usa la lingua del sistema operativo." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Tradotto: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Ciao\n" "Hai usato Back In Time in lingua {language} un po' di volte.\n" "La traduzione della versione di Back In Time installata in {language} è completa al {perc}. Indipendentemente dal tuo livello tecnico, puoi contribuire alla traduzione e migliorare così Back In Time.\n" "Visita {translation_platform_url} per contribuire. Per avere altre informazioni, puoi visitare {back_in_time_project_website}.\n" "Ci scusiamo per l'interruzione, questo messaggio non verrà mostrato nuovamente ma rimane disponibile per consultazione nel menu Aiuto.\n" "Il team Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "piattaforma di traduzione" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "La tua traduzione" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Visualizzazione Ultimo Registro" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Visualizza il registro dell'istantanea" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profilo:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Istantanee:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtro:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Tutti" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Cambiamenti" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Errori" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informazione" msgstr[1] "Informazioni" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "errori trasferimento rsync (sperimentale)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Errore, [I] Informazione, [C] Cambiamento" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "decodifica i percorsi" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Domanda" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profilo: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Visualizza ultimo log" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Avvia {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Lavoro in corso…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Inviati:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Velocità:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Istantanee" #: qt/qttools.py:427 msgid "Today" msgstr "Oggi" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Ieri" #: qt/qttools.py:443 msgid "This week" msgstr "Questa settimana" #: qt/qttools.py:450 msgid "Last week" msgstr "Scorsa settimana" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Questa NON è un'istantanea, bensì una vista attuale dei tuoi file locali" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Ultimo controllo {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Mostra il registro completo" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Proxy SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Host:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Porta:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Utente:" #: qt/settingsdialog.py:120 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 dettagli consulta \"-J\" nella documentazione del " "comando \"ssh\" o \"ProxyJump\" nella pagina del manuale." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Gestione profili" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Modifica" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Aggiungi" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Rimuovi" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Generale" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modalità:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Dove salvare le istantanee" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Cartella" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Impostazioni di SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Percorso:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Cifratura:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Chiave privata:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Seleziona un file di chiave privata esistente (di solito chiamato " "\"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Crea una nuova chiave SSH senza password (non permesso se è già stato " "selezionato un file di chiave privata)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Password" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Salva la password nel gestore delle password" #: qt/settingsdialog.py:378 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/settingsdialog.py:390 msgid "Advanced" msgstr "Avanzate" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Percorso completo per le istantanee:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Pianificazione" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Disabilitato" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Ad ogni avvio/riavvio" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Ogni minuto" msgstr[1] "Ogni {n} minuti" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Ogni ora" msgstr[1] "Ogni {n} ore" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Ogni {n} ora" msgstr[1] "Ogni {n} ore" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Orario personalizzato" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Ogni giorno" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Ripetutamente (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Quando il drive viene connesso (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Ogni settimana" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Ogni mese" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Ogni anno" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Giorno:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Giorno della settimana:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Ora:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Ore:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Avvia Back In Time ripetutamente. È utile se il computer non viene avviato " "regolarmente." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Ogni:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Ora(e)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Giorno(i)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Settimana(e)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Mese(i)" #: qt/settingsdialog.py:555 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 "" "Avvia Back In Time non appena il dispositivo viene collegato (solo una volta ogni X giorni).\n" "Ti verrà richiesta la tua password di sudo." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Abilita registrazione dei messaggi di debug" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "Scrive i messaggi di debug nel registro di sistema tramite \"--debug\"." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Attenzione: da usare solo temporaneamente per diagnostica, in quanto genera " "una grande quantità di messaggi." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Includi" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Includi file e cartelle" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Aggiungi file" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Aggiungi cartella" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Escludi" #: qt/settingsdialog.py:625 #, 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 verranno ignorati (es. {example1}). In questa modalità i " "nomi di file non sono prevedibili a causa della cifratura di EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Esclusione pattern, file o cartelle" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Aggiungi predefiniti" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Escludi file più grandi di:" #: qt/settingsdialog.py:698 #, 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:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Se la 'Modalità rsync completo' è disabilitata, questo avrà effetto solo sui" " nuovi file, perché rsync la tratta come un'opzione di trasferimento, non di" " esclusione. Per questo motivo i file già sottoposti a backup in precedenza " "rimarranno nelle istantanee anche se sono stati modificati." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Rimozione &automatica" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Più vecchi di:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Anno(i)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Se lo spazio libero è minore di:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Se gli inode liberi sono meno del:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Rimozione intelligente:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Esegui in sottofondo sull'host remoto." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "SPERIMENTALE" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Mantieni tutte le istantanee degli ultimi" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "giorno/i." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Mantieni un'istantanea al giorno per gli ultimi" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Mantieni un'istantanea alla settimana per le ultime" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "settimana/e." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Mantieni un'istantanea al mese per gli ultimi" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mese/i." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Mantieni un'istantanea all'anno per tutti gli anni." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Non rimuovere le istantanee a cui è stato assegnato un nome." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opzioni" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Attiva le notifiche" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Disabilita le istantanee quando si usa la batteria" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Impossibile stabilire la modalità di alimentazione" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Esegui solo un'istantanea alla volta" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Le altre istantanee verranno bloccate finché l'istantanea corrente non sarà " "completata. 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/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Esegui il backup dei file sostituiti durante il ripristino" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Le nuove versioni dei file verranno rinominate con il suffisso {suffix} " "prima del ripristino. Se non ne hai più bisogno, puoi rimuoverle con {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continua in caso di errore (mantiene un'istantanea incompleta)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Usa il checksum per riconoscere i cambiamenti" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" "Prendi un'istantanea anche se non ci sono modifiche rispetto alla " "precedente." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Livello di log:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nulla" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Op&zioni per esperti" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Attenzione: Cambiare queste opzioni solo se si sa cosa si sta facendo." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Esegui 'rsync' con '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "come cron job" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "sull'host remoto" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "quando si prende un'istantanea manuale" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Per favore installa 'nocache' per abilitare questa opzione)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "sulla macchina locale" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirigi stdout verso /dev/null nei cronjob." #: qt/settingsdialog.py:1005 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:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirigi stderr verso /dev/null nei cronjob." #: qt/settingsdialog.py:1016 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/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Limita la banda utilizzata da rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Preserva ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Preserva attributi estesi (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Copia collegamenti non sicuri (funziona solo con collegamenti assoluti)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Limita a un file system" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Le opzioni devono essere racchiuse tra virgolette, es. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Passa opzioni addizionali ad rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Aggiungi prefisso ai comandi SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefisso da eseguire prima di ciascun comando sull'host remoto." #: qt/settingsdialog.py:1188 #, 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 "" "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:1196 msgid "default" msgstr "default" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Controlla se l'host remoto è online" #: qt/settingsdialog.py:1214 msgid "" "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:1218 msgid "Check if remote host supports all necessary commands." msgstr "Controlla se l'host remoto supporta tutti i comandi necessari." #: qt/settingsdialog.py:1221 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/settingsdialog.py:1237 msgid "Restore Config" msgstr "Ripristina configurazione" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Modifica callback utente" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nuovo profilo" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Rinomina profilo" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Sei sicuro di voler eliminare il profilo \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Fortemente raccomandato{ENDBOLD}: (Tutte le raccomandazioni già " "incluse.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Fortemente raccomandato{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "L'orario personalizzato può essere solo un elenco di ore separate da virgola" " (es. 8,12,18,23) oppure */3 per backup periodici ogni 3 ore." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Non hai scelto un file di chiave privata per SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Vorresti generare una nuova coppia di chiavi pubblica/privata senza " "password?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Il file chiave privata \"{file}\" non esiste." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Vuoi copiare la tua chiave SSH pubblica sull'host remoto per abilitare il " "login senza password?" #: qt/settingsdialog.py:1878 #, 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:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "L'impronta digitale della chiave {keytype} è:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Si prega di verificare questa impronta digitale. Vorresti aggiungerla al tuo" " file 'known_hosts'?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Pattern di esclusione" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Escludi file" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Escludi cartella" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Includi file" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" è un link simbolico. La destinazione del link non sarà copiata nel backup se non la includerai esplicitamente.\n" "Vuoi piuttosto includere la destinazione del link simbolico?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Includi cartella" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Sei sicuro di voler cambiare la cartella delle istantanee?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Impossibile creare una nuova chiave SSH in {path}." #: qt/settingsdialog.py:2310 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:2369 msgid "(default: {})" msgstr "(predefinito: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "disabilitato" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "abilitato" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importa configurazione" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Configurazione non trovata" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importa" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Seleziona la cartella delle istantanee da cui caricare la configurazione. Il" " percorso potrebbe somigliare a: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Se il percorso si trova su un disco esterno o remoto, deve essere prima " "montato manualmente." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opzioni relative al confronto tra istantanee" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Comando:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametri:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Usa %1 e %2 come parametri del percorso" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Si prega di impostare un comando diff o premere Annulla." #: qt/snapshotsdialog.py:90 #, 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:98 #, 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:134 msgid "Differing snapshots only" msgstr "Solo istantanee diverse" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Mostra solo istantanee uguali a:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Controllo approfondito (più accurato ma più lento)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Elimina" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Seleziona tutte" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Confronta" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Vai a" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opzioni" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Non è possibile confrontare un'istantanea con se stessa." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Vuoi veramente cancellare \"{file}\" nell'istantanea \"{snapshot_id}\"?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Vuoi veramente cancellare \"{file}\" in {count} istantanee?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "ATTENZIONE: Questa operazione è irreversibile." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Escludere \"{path}\" dalle istantanee future?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Impossibile trovare crontab. Sei sicuro che cron sia installato? Se non lo è" #~ " dovresti disattivare tutti i backup automatici." #~ msgid "Full snapshot path" #~ msgstr "Percorso completo per le istantanee" #~ msgid "Mode" #~ msgstr "Modalità" #~ msgid "Profile" #~ msgstr "Profilo" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profilo '{profile}': Inserisci la password per {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Lo shebang nello script di callback dell'utente non è eseguibile." #~ msgid "WARNING" #~ msgstr "ATTENZIONE" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "la versione di encfs 1.7.2 e precedenti ha un bug con l'opzione --reverse. " #~ "Per favore aggiorna encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "lo script di callback utente non ha una linea shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} utilizza EncFS per la crittografia. Una recente revisione di sicurezza" #~ " ha rivelato numerosi possibili vettori d'attacco in questa configurazione. " #~ "Per favore consulta la sezione 'A NOTE ON SECURITY' in 'man backintime'." backintime-1.5.2/common/po/ja.po000066400000000000000000001565711465446530500165200ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:04+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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "警告" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "メインプロファイル" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "ローカル (EncFS暗号化)" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH暗号化" #: common/config.py:309 msgid "Local" msgstr "ローカル" #: common/config.py:311 #, fuzzy msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSHプライベートキー" #: common/config.py:314 msgid "Local encrypted" msgstr "ローカル暗号化" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "暗号" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH暗号化" #: common/config.py:327 msgid "Default" msgstr "標準設定" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "プロファイル: 「{name}」" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "スナップショットフォルダが無効です!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "バックアップするフォルダを少なくとも1つ選択する必要があります!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "バックアップフォルダを含めることはできません。" #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "バックアップのサブフォルダを含めることはできません。" #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "無効なオプションです。{path} はフォルダではありません。" #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "ホスト/ユーザ/プロファイルID を入力してください。" #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "書き込めません: {path}\n" "本当に書き込み権限がありますか?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "{path} " "の保存先ファイルシステムは、ハードリンクをサポートしないFATでフォーマットされています。ネイティブのLinuxファイルシステムを使用してください。" #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "{path} の保存先ファイルシステム は SMB マウントされた共有です。 " "リモートSMBサーバーがシンボリックリンクをサポートしているか確認してください、または {expertOptions} で {copyLinks} " "を有効化してください。" #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "リンクをコピーする(シンボリックリンクの参照解除)" #: common/config.py:497 msgid "Expert Options" msgstr "上級者向けオプション" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "{path} の保存先ファイルシステムは sshfs マウントされた共有です。 sshfs はハードリンクをサポートしていません 。代わりにモード " "'SSH' を使用してください。" #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "新しい crontab の書き込みに失敗しました。" #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "モード {mode} では、udev スケジュールは使用できません" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "{path} には該当の UUID がありませんでした" #: common/configfile.py:107 msgid "Failed to save config" msgstr "設定を保存できませんでした" #: common/configfile.py:143 msgid "Failed to load config" msgstr "設定を読み込めませんでした" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "プロファイル 「{name}」 はすでに存在しています。" #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "最後のプロファイルを削除することはできません。" #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "{command} をマウントできません" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "暗号化されたフォルダの設定がみつかりません。" #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "新しい暗号化フォルダを作成しますか?" #: common/encfstools.py:151 msgid "Cancel" msgstr "取消" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "パスワードを確認してください。" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "パスワードが一致しません。" #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "スナップショット取得" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "{mountprocess} を {mountpoint} からアンマウントできない。" #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "{command} が見つかりません。インストールしてください。例\"{installcommand}\"" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "マウントポイント {mntpoint} が空ではありません。" #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "{mode} プロファイル\"{profile}\"のパスワードを入力してください:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "失敗" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "パーミッションを復元" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "完了" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "バッテリー動作のときは延期する" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "スナップショットフォルダがみつかりません。\n" "もしリムーバブルドライブ上にあるのなら、接続して下さい。" #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "%s 秒待っています。" #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "スナップショット {snapshot_id} の取得に失敗しました。" #: common/snapshots.py:937 msgid "Finalizing" msgstr "完了処理中" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "フォルダを作成できません" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "設定ファイルの保存…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "パーミッションを保存…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "以前に作成した使用できるスナップショット {snapshot_id} がありました。" #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "前回作成した {snapshot_id} フォルダを削除しています" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "フォルダを削除できません" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "スナップショットの取得" #: common/snapshots.py:1417 msgid "Success" msgstr "成功" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "エラーによる部分的な転送" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "ソースファイルの消失による部分的な転送 (man rsync' を参照)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' は終了コード {exit_code} で終了しました" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "詳細は'man rsync'を参照" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "ネガティブの rsync 終了コードはシグナル番号です、'kill -l' および 'man kill' を参照してください" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "変更箇所がないので新しいスナップショットは必要ありません" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{path} を {new_path} に変更できません" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "賢く削除" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "古いスナップショットを削除" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "最小限のフリースペースの確保を試みる" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "最小限の{perc}フリースイノードの確保を試みる" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "現在" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs} をマウントできません" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agentが見つかりません。インストールされているか確認してください。" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "ssh の秘密鍵を解除できませんでした。パスワードが間違っているか、cron で使用できないパスワードです。" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "暗号 {cipher} は {host} で失敗。" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "リモートパスは存在するが、ディレクトリではない。" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "リモートパスは書き込みできません。" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "リモートパスは実行可能ではありません。" #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "フォルダを作成できません。" #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "リモートホスト {host} は {command} をサポートしていない" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "詳しくは 'man backintime' を参照してください" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "ホスト {host} のチェックコマンドが不明なエラーを返しました" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "リモートホスト {host} がハードリンクをサポートしていません" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "公開鍵 \"{pubkey}\" をリモートホスト \"{host}\" にコピーしてください" #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "ユーザ \"{user}\" のパスワードを入力してください。" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "情報" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "著作者" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "翻訳" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "ライセンス" #: qt/app.py:169 msgid "Shortcuts" msgstr "ショートカット" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "現在選択されているスナップショットには\n" "このフォルダがありません。" #: qt/app.py:256 msgid "Add to Include" msgstr "含むフォルダを指定" #: qt/app.py:258 msgid "Add to Exclude" msgstr "除外するフォルダを指定" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "スナップショットフォルダが見つかりません。" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "スナップショットフォルダが見つかりません。\n" "もしリムーバブルドライブ上にあるのなら、それを接続してからOKを押してください。" #: qt/app.py:481 msgid "Take a snapshot" msgstr "スナップショットの取得" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "ファイルの変更検出には、変更時間とサイズを使用します。" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "スナップショット取得(チェックサムモード)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "変更の検出にチェックサムを使用する。" #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "スナップショットの作成を一時停止する" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "スナップショットの作成を再開する" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "スナップショットの作成を停止する" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "スナップショットリストの更新" #: qt/app.py:508 msgid "Name snapshot" msgstr "名前付きスナップショット" #: qt/app.py:512 msgid "Remove snapshot" msgstr "スナップショットの削除" #: qt/app.py:516 msgid "View snapshot log" msgstr "スナップショットログの表示" #: qt/app.py:520 msgid "View last log" msgstr "最近のログを表示" #: qt/app.py:524 msgid "Manage profiles…" msgstr "プロファイルの管理…" #: qt/app.py:528 msgid "Shutdown" msgstr "電源を切る" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "スナップショットの作成終了後に電源を切る。" #: qt/app.py:532 msgid "Setup language…" msgstr "言語設定…" #: qt/app.py:536 msgid "Exit" msgstr "終了" #: qt/app.py:540 msgid "Help" msgstr "ヘルプ" #: qt/app.py:544 msgid "Profiles config file" msgstr "設定ファイルを保存" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "ウェブサイト" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "変更履歴" #: qt/app.py:553 msgid "FAQ" msgstr "よくある質問" #: qt/app.py:556 msgid "Ask a question" msgstr "質問する" #: qt/app.py:559 msgid "Report a bug" msgstr "バグを報告する" #: qt/app.py:562 msgid "Translation" msgstr "翻訳" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "復元" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "選択されたファイルやフォルダを元の場所に復元する。" #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "復元…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "選択されたファイルやフォルダを新しい場所に復元する。" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "現在表示されているフォルダとそれに含まれるファイルを元の場所に復元する。" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "現在表示されているフォルダとそれに含まれるファイルを新しい場所に復元する。" #: qt/app.py:595 msgid "Up" msgstr "上層へ" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "不可視ファイルを表示" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "スナップショットを比較…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "&バックアップ" #: qt/app.py:676 msgid "&Restore" msgstr "復元" #: qt/app.py:682 msgid "&Help" msgstr "ヘルプ" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "このウインドウを閉じると Back In Time はスナップショットの作成終了時に電源を切ることができなくなります\n" "閉じてよろしいでしょうか?" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "本当にファイルを復元してよろしいですか?" #: qt/app.py:987 msgid "Working:" msgstr "作業中:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "完了、バックアップの必要はありませんでした" #: qt/app.py:1044 msgid "Working" msgstr "作業中" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "エラー" #: qt/app.py:1076 msgid "Sent" msgstr "送信" #: qt/app.py:1077 msgid "Speed" msgstr "速度" #: qt/app.py:1078 msgid "ETA" msgstr "予定完了時間" #: qt/app.py:1140 msgid "Global" msgstr "全体" #: qt/app.py:1141 msgid "Root" msgstr "ルート" #: qt/app.py:1142 msgid "Home" msgstr "ホーム" #: qt/app.py:1170 msgid "Backup folders" msgstr "バックアップフォルダ" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "スナップショット名" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "スナップショットを削除してもよろしいですか?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "ローカルファイルを上書きまたは削除する前に\n" "末尾に {suffix} を付けたバックアップコピーを作成する。" #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "復元時により新しいファイルがある場合は拡張子{suffix} 付きでファイル名変更されます。もし不要の場合は以下のコマンドで削除してください : " "{cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "ファイルが存在しないか\n" "より新しい場合にのみ復元します。\n" " \"rsync --update\" のオプションを使用します。" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "元のフォルダ内の新しいファイルを削除する。" #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "選択したファイルやフォルダを元の保存先に復元し " "スナップショットに含まれないファイルやフォルダを削除します。これによってスナップショット作成時に除外されたファイルやフォルダが " "削除されるので、十分注意してください。" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "本当に新しいフォルダー {path} に\n" "ファイルを復元しますか?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "本当にファイルを復元してよろしいですか?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "{path} にあるより新しいファイルを削除してもよろしいですか?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "元のフォルダにあるより新しいファイルを削除してもよろしいですか?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "{BOLD}警告{BOLDEND}:ファイルシステムルートのファイルを削除すると、システム全体が壊れる可能性があります!" #: qt/app.py:1750 msgid "Snapshot" msgstr "スナップショット" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "{path} を復元" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "{path} を復元…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "言語設定は、Back In Time を再起動した後に有効になります。" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "言語設定" #: qt/languagedialog.py:92 msgid "System default" msgstr "標準設定" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "オペレーティングシステムの言語を使用する。" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "翻訳された: {percent}" #: qt/languagedialog.py:188 #, 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 "" "これまでに 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/languagedialog.py:217 msgid "translation platform" msgstr "翻訳プラットフォーム" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "翻訳へのご協力のお願い" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "最終ログを表示" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "スナップショットログを表示" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "プロファイル:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "スナップショット:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "フィルタ:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "全て" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "変更点" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "エラー" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "情報" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync 転送の失敗 (実験的)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] エラー、 [I] インフォメーション、 [C] 変更点" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "復号先" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "質問" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "プロファイル: 「{profile_name}」" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "最近のログを閲覧" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "{appname} を開始する" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "作業中…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "送信:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "速度:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "スナップショット" #: qt/qttools.py:427 msgid "Today" msgstr "今日" #: qt/qttools.py:434 msgid "Yesterday" msgstr "昨日" #: qt/qttools.py:443 msgid "This week" msgstr "今週" #: qt/qttools.py:450 msgid "Last week" msgstr "先週" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "スナップショットではなく、ローカルファイルのライブビューです" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "最終チェック {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "すべてのログを見る" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "ホスト:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "ポート:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "ユーザ:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "プロファイルの管理" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "編集" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "追加" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "削除" #: qt/settingsdialog.py:210 msgid "&General" msgstr "一般" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "モード:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "スナップショットの保存場所" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "フォルダ" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SHH 設定" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "パス名:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "暗号化方法:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "プライベート鍵:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "すでにあるプライベート鍵を使用する (通常 \"id_rsa\"というファイル名)" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "新しい SSH鍵をパスワードなしで作成する (すでにプライベート鍵を選択済みの場合は実行できません)。" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "パスワード" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "パスワードを鍵束に保存" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "パスワードを cron に渡す (セキュリティ上の問題: 管理者にパスワードを知られます)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "上級設定" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "スナップショットのフルパス:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "スケジュール" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "無効" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "起動/再起動毎に" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "{n} 分毎" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "毎時" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "{n} 時間毎" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "カスタム時間" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "毎日" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "繰り返し (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "ドライブが接続されたとき(udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "毎週" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "毎月" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "毎年" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "曜日:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "時:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "時間:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "Run Back In Time を繰り返し実行する。コンピュータを定期的に起動していない場合に便利です。" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "毎:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "時間" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "日" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "週" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "カ月" #: qt/settingsdialog.py:555 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 "" "ドライブが接続されたら Back In Time を実行 (ただし X日に一回)\n" "sudo のパスワードを求められます。" #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "編入する" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "含むファイルとフォルダ" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "ファイルを追加" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "フォルダを追加" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "除外する" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "ファイルやフォルダの除外パターン" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "標準設定に追加" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "これより大きいファイルは除外する:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "{size_unit}の値よりも大きいファイルは除外する。" #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "%(prefix)s より大きいファイルを除外する\n" "'Full rsync mode' を選択しているときは\n" "既にバックアップされている大きなファイルについては実行されますが、\n" "新しい大きなファイルは除外されます\n" "'rsync' の機能を利用したもので厳密にはここで除外対象を指定しているのではないからです。" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "自動削除" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "以下より古い場合:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "年" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "以下より空き容量が少ない場合:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "以下より空きのイノードが少ない場合:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "賢く削除:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "リモートホスト上のバックグラウンドで実行する。" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "実験検証中" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "すべてのスナップショットを保存する日数" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "日。" #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "日別スナップショットを保存する日数" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "週別スナップショットを保存する期間" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "週間。" #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "月別スナップショットを保存する期間" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "カ月。" #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "年別スナップショットを保存する期間。" #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "名前付きスナップショットは削除しないでください。" #: qt/settingsdialog.py:849 msgid "&Options" msgstr "オプション" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "通知を有効にする" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "バッテリ駆動の際はスナップショット取得を無効化" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "システムから電源情報が得られません" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "一度に作成するスナップショットの数をひとつに限定して実行する" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "他のスナップショット作業は現在のものが終了するまで保留されます。これはすべてのプロファイルに適用されます。逆に言えば、その条件が満たされないとこのオプションを使うことができません。" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "復元時に置換されたファイルをバックアップする" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "復元時により新しいファイルがある場合は拡張子{suffix} 付きでファイル名変更されます。もし不要の場合は以下のコマンドで削除してください : " "{cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "エラー発生時に継続 (不完全なスナップショットを維持)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "変更の検出にチェックサムを使用" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "変更の有無に関わらず、新しいスナップショットを取得する。" #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "ログレベル:" #: qt/settingsdialog.py:911 msgid "None" msgstr "なし" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "上級者向けオプション" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "何をしているか本当に理解している場合に限りこれらのオプションを変更してください。" #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "オプション '{cmd}' をつけて 'rsync' を実行:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "cron として実行" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "リモートホスト上で" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "手動スナップショット撮影時" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "('nocache'をインストールしてこのオプションを有効にしてください)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "ローカルマシン上で" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "cronジョブでstdoutを/dev/nullにリダイレクトしてください。" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "cronジョブでstderrを/dev/nullにリダイレクトしてください。" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "rsync の帯域幅使用量を制限する" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/秒" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "ACL を保存" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "拡張属性を維持(xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "危険なリンクをコピー(絶対リンクに対してのみ動作)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "オプションは必ず引用符で囲むこと 例:{example} 。" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "rsync に追加オプションを貼り付ける" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "SSHコマンドにプレフィックスを追加する" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "リモートホストの各コマンドの前に実行するプレフィックス。\n" "変数には \\$FOO でエスケープする必要があります。\n" "これはrsyncには触れない。そのため\n" "rsyncのプレフィックスを追加するには、\n" "”%(cbRsyncOptions)s” を %(rsync_options_value)s と一緒に使います。\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "標準設定" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "リモートホストがオンラインかどうか確認する" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "警告: 無効でリモートホストが利用できない場合 \n" "奇妙なエラーが発生する可能性があります。" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "リモートホストが必要なコマンドをすべてサポートしているか確認する。" #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "警告: 無効にした場合、\n" "リモートホストが必要なコマンドをすべてサポートしていないと、\n" "奇妙なエラーが発生する可能性があります。" #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "設定の復元" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "ユーザーコールバックの編集" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "新規プロファイル" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "プロファイルをリネーム" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "プロファイル ”{name}” を本当に削除しますか?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "非常におすすめ" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "SSH 用の秘密鍵ファイルを選択していません。\n" "新しいパスワードなしの公開鍵/秘密鍵ペアを生成しますか?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "秘密鍵ファイル「{file}」は存在しません。" #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "公開 SSH 鍵をリモートホストにコピーして、\n" "パスワード不要のログインを可能にしたいですか?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "ホスト {host} の信頼性を確立できません。" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "フィンガープリントを確認してください!’known_hosts’ ファイルに追加しますか?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "除外するパターン" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "除外するファイル" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "除外するフォルダ" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "含めるファイル" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "{path} はシンボリックリンクです このリンクにある本体は指定しない限りバックアップされません\n" "シンボリックリンクの代わりに本体を指定しますか?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "含めるフォルダ" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "スナップショットのフォルダを変更してもよろしいですか?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "{path} において新しい SSH鍵の作成に失敗しました" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "標準設定" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "無効" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "有効" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "設定が見つかりません" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "スナップショットの比較についてのオプション" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "コマンド" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "パラメータ:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "パスのパラメータに %1 と %2 を使う" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "スナップショットの差分のみ" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "等しいスナップショットだけをリストアップする:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "ディープチェック (より正確ですが遅いです)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "削除" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "全て選択" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "比較" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "移動" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "オプション" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "スナップショット自体とは比較できません。" #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "スナップショット {snapshot_id} の {file} を本当に削除したいのですか?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "スナップショット {count} の {file} を本当に削除したいのですか?" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "これを取り消すことはできない!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "今後のスナップショットから {path} を除外しますか?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "crontab が見つかりません。cronがインストールされていますか?もしそうでなければ、自動バックアップをすべて無効にしてください。" #~ msgid "Full snapshot path" #~ msgstr "すでに絶対パス" #~ msgid "Mode" #~ msgstr "モード" #~ msgid "Profile" #~ msgstr "プロファイル" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "プロファイル 「{profile}」: {mode} のパスワードを入力してください " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "ユーザーコールバックスクリプトの Shebang が実行可能でない。" #~ msgid "WARNING" #~ msgstr "警告" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "encfsのバージョン1.7.2と 以前は --reverse オプションにバグがあった。encfsをアップデートしてください。" #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "ユーザーコールバックスクリプトには shebang (#!/bin/sh) 行がありません。" #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} は暗号化にEncFSを使っている。最近のセキュリティ監査で、これに対する攻撃ベクトルがいくつか明らかになりました。“man " #~ "backintime “の “A NOTE ON SECURITY “を参照ください。" backintime-1.5.2/common/po/ko.po000066400000000000000000001520741465446530500165310ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:09+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "경고" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "메인 프로파일" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "로컬 암호화함" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH 암호화" #: common/config.py:309 msgid "Local" msgstr "로컬" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH 개인 키" #: common/config.py:314 msgid "Local encrypted" msgstr "로컬 암호화함" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "암호화" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH 암호화" #: common/config.py:327 msgid "Default" msgstr "기본" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "프로필: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "스냅샷 폴더가 올바르지 않습니다!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "백업할 폴더를 하나 이상 선택해야 합니다!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "백업 폴더를 포함할 수 없습니다." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "백업 하위 폴더를 포함할 수 없습니다." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "잘못된 옵션입니다. {path}은(는) 폴더가 아닙니다." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "호스트/사용자/프로필-ID는 비워둘 수 없습니다." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "다음 경로에 쓸 수 없습니다: {path}\n" "쓰기 권한이 있습니까?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "{path}의 대상 파일 시스템은 하드 링크를 지원하지 않는 FAT 형식으로 포맷했습니다. 기본 Linux 파일 시스템을 사용하세요." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "{path}의 대상 파일 시스템은 SMB 형식 마운트 공유입니다. 원격 SMB 서버가 심볼릭 링크를 지원하는지 확인하거나 " "{expertOptions}에서 {copyLinks}를 활성화하세요." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "링크 복사(기호 링크 역참조)" #: common/config.py:497 msgid "Expert Options" msgstr "고급 설정" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "{path}의 대상 파일 시스템은 SSHFS 마운트 공유입니다. SSHFS는 하드 링크를 지원하지 않습니다. 대신 'SSH' 모드를 " "사용하세요." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "새 crontab을 작성하지 못했습니다." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "일정 udev가 {mode} 모드에서 작동하지 않습니다" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "{path}에 대한 UUID를 찾을 수 없습니다" #: common/configfile.py:107 msgid "Failed to save config" msgstr "구성을 저장하지 못했습니다" #: common/configfile.py:143 msgid "Failed to load config" msgstr "구성을 불러오지 못했습니다" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\" 프로필이 이미 존재합니다." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "마지막 프로필은 제거할 수 없습니다." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "'{command}' 명령을 마운트할 수 없습니다" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "암호화된 폴더에 대한 구성을 찾을 수 없습니다." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "새 암호화 폴더를 만드시겠습니까?" #: common/encfstools.py:151 msgid "Cancel" msgstr "취소" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "암호를 확인하십시오." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "암호가 일치하지 않습니다." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "스냅샷 생성하기" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "{mountpoint}에서 {mountprocess}을(를) 마운트 해제할 수 없습니다." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "{command}을(를) 찾을 수 없습니다. {installcommand} 명령으로 설치하십시오" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "{mntpoint} 마운트 지점이 비어 있지 않습니다." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "\"{profile}\" {mode} 프로파일의 암호를 입력하십시오:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "실패" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "권한 복원" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "완료" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "배터리를 사용하는 동안 백업 연기" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "스냅샷 폴더를 찾을 수 없습니다.\n" "이동식 드라이브에 있는 경우 해당 드라이브를 연결하세요." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "%s초 기다립니다." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "{snapshot_id} 스냅샷을 생성하지 못했습니다." #: common/snapshots.py:937 msgid "Finalizing" msgstr "마무리 중" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "폴더를 만들 수 없습니다" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "구성 파일 저장 중…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "권한 저장 중…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "계속할 수 있는 남은 {snapshot_id}을(를) 찾았습니다." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "마지막 실행에서 남은 {snapshot_id} 폴더 제거 중" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "폴더를 제거할 수 없습니다" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "스냅샷 생성" #: common/snapshots.py:1417 msgid "Success" msgstr "성공" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "오류 때문에 일부만 전송했습니다" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "사라진 소스 파일로 인한 부분 전송('man rsync' 참조)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync'가 종료 코드 {exit_code}로 종료되었습니다" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "자세한 내용은 'man rsync'를 참조하세요" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "rsync 음수 종료 코드는 시그널 번호입니다. 'kill -l' 및 'man kill'을 참조하세요" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "아무것도 변경되지 않았으며 새 스냅샷이 필요하지 않습니다" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{new_path} 경로 이름을 {path} 경로 이름으로 바꿀 수 없습니다" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "똑똑한 제거" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "오래된 스냅샷 제거" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "최소 여유 공간을 유지하려고 합니다" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "최소 {perc}개의 사용 가능한 아이노드를 유지하려고 합니다" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "현재" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs}을(를) 마운트할 수 없습니다" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "SSH 에이전트를 찾을 수 없습니다. 설치되어 있는지 확인하시기 바랍니다." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "SSH 개인 키를 잠금 해제할 수 없습니다. 잘못된 암호거나 cron에 암호를 사용할 수 없습니다." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "{host}에 대한 암호 {cipher}이(가) 실패했습니다." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "원격 경로가 존재하지만 디렉터리가 아닙니다." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "원격 경로에 쓸 수 없습니다." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "원격 경로를 실행할 수 없습니다." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "원격 경로를 생성할 수 없습니다." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "원격 호스트 {host}은(는) {command}을(를) 지원하지 않습니다" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "자세한 지침은 'man backintime'을 참조하세요" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "호스트 {host}의 확인 명령이 알 수 없는 오류를 반환했습니다" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "원격 호스트 {host}은(는) 하드링크를 지원하지 않습니다" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "\"{pubkey}\" 공개 SSH 키를 \"{host}\" 원격 호스트에 복사합니다." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "\"{user}\" 암호를 입력하십시오." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "정보" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "저자" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "번역" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "라이센스" #: qt/app.py:169 msgid "Shortcuts" msgstr "바로 가기" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "이 폴더가 존재하지 않습니다.\n" "폴더가 없습니다." #: qt/app.py:256 msgid "Add to Include" msgstr "포함할 항목 추가" #: qt/app.py:258 msgid "Add to Exclude" msgstr "제외할 항목 추가" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "{app_name}을 어떤 설정 없이 처음 실행하는 것 같습니다." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "(백업 대상 폴더 또는 다른 컴퓨터에서) 기존 설정을 가져오시겠습니까?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "스냅샷 폴더를 찾을 수 없습니다." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "이동식 드라이브에 있는 경우 연결한 다음 확인을 누르십시오." #: qt/app.py:481 msgid "Take a snapshot" msgstr "스냅샷 생성" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "파일 변경 감지를 위해 수정 시간 및 크기를 사용합니다." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "체크섬을 포함한 스냅샷 생성하기" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "파일 변경 감지에 체크섬을 사용합니다." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "스냅샷 생성 일시 중지" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "스냅샷 생성 재시작" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "스냅샷 생성 중지" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "스냅샷 목록 새로 고침" #: qt/app.py:508 msgid "Name snapshot" msgstr "스냅샷 이름 변경" #: qt/app.py:512 msgid "Remove snapshot" msgstr "스냅샷 제거" #: qt/app.py:516 msgid "View snapshot log" msgstr "스냅샷 로그 보기" #: qt/app.py:520 msgid "View last log" msgstr "마지막 로그 보기" #: qt/app.py:524 msgid "Manage profiles…" msgstr "프로필 관리…" #: qt/app.py:528 msgid "Shutdown" msgstr "끄기" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "스냅샷이 끝난 후 시스템 종료하기." #: qt/app.py:532 msgid "Setup language…" msgstr "언어 설정…" #: qt/app.py:536 msgid "Exit" msgstr "끝내기" #: qt/app.py:540 msgid "Help" msgstr "도움말" #: qt/app.py:544 msgid "Profiles config file" msgstr "프로필 구성 파일" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "웹사이트" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "변경 내역" #: qt/app.py:553 msgid "FAQ" msgstr "자주 묻는 질문" #: qt/app.py:556 msgid "Ask a question" msgstr "질문하기" #: qt/app.py:559 msgid "Report a bug" msgstr "버그 신고" #: qt/app.py:562 msgid "Translation" msgstr "번역" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "복원" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "선택한 파일이나 폴더를 원래 대상으로 복원합니다." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "다음 위치에 복원 …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "선택한 파일 또는 폴더를 새로운 대상으로 복원합니다." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "현재 표시된 폴더와 해당 폴더의 모든 내용을 원래 대상으로 복원합니다." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "현재 표시된 폴더와 해당 폴더의 모든 내용을 새 대상으로 복원합니다." #: qt/app.py:595 msgid "Up" msgstr "위로" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "숨겨진 파일 표시" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "스냅샷 비교…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "백업" #: qt/app.py:676 msgid "&Restore" msgstr "복원" #: qt/app.py:682 msgid "&Help" msgstr "도움말" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "이 창을 닫으면 스냅샷을 마쳤을 때 Back In Time에서 시스템을 끌 수 없습니다." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "정말로 닫으시겠습니까?" #: qt/app.py:987 msgid "Working:" msgstr "진행 중:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "완료했습니다. 백업이 필요하지 않습니다" #: qt/app.py:1044 msgid "Working" msgstr "진행 중" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "오류" #: qt/app.py:1076 msgid "Sent" msgstr "전송" #: qt/app.py:1077 msgid "Speed" msgstr "속도" #: qt/app.py:1078 msgid "ETA" msgstr "예상 소요 시간" #: qt/app.py:1140 msgid "Global" msgstr "전역" #: qt/app.py:1141 msgid "Root" msgstr "루트" #: qt/app.py:1142 msgid "Home" msgstr "홈" #: qt/app.py:1170 msgid "Backup folders" msgstr "백업 폴더" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "스냅샷 이름" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "정말 이 스냅샷(들)을 제거하시겠습니까?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "로컬 요소를 덮어쓰거나 제거하기 전에\n" "뒤에 오는 {suffix}를 사용하여 백업 복사본을 만듭니다." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "최신 버전 파일은 복원하기 전 뒤에 {suffix}를 붙인 이름으로 바뀝니다. 더 이상 필요하지 않으면 {cmd}을(를) 사용하여 제거할" " 수 있습니다" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "존재하지 않는 요소만 복원하거나\n" "대상에 있는 요소보다 최신인 요소만 복원합니다.\n" "\"rsync --update\" 옵션을 사용합니다." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "원본 폴더에서 최신 요소를 제거합니다." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "선택한 파일 또는 폴더를 원래 대상으로 복원하고 스냅샷에 없는 파일 또는 폴더를 삭제합니다. 이렇게 하면 스냅샷을 생성하는 동안 제외했던" " 파일과 폴더를 삭제하므로 각별히 주의하십시오." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "이러한 요소(들)을 {path} 새 폴더\n" "에 복원하시겠습니까?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "정말로 이러한 요소(들)을 복원하시겠습니까?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "{path}에 있는 모든 최신 파일을 제거하시겠습니까?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "원본 폴더에서 최신 파일을 모두 제거하시겠습니까?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "{BOLD}경고{BOLDEND}: 파일 시스템 루트에서 파일을 삭제하면 전체 시스템이 망가질 수 있습니다." #: qt/app.py:1750 msgid "Snapshot" msgstr "스냅샷" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "{path} 복원" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "{path}를 다음 위치에 복원 …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "언어 설정은 Back In Time을 다시 시작한 후에만 적용됩니다." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 #, fuzzy msgid "Your Back In Time Team" msgstr "Back In &Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "언어 설정" #: qt/languagedialog.py:92 msgid "System default" msgstr "시스템 기본값" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "운영 체제의 언어를 사용합니다." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "{percent} 번역됨" #: qt/languagedialog.py:188 #, 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/languagedialog.py:217 msgid "translation platform" msgstr "번역 플랫폼" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "귀하의 번역" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "마지막 로그 보기" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "스냅샷 로그 보기" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "프로필:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "스냅샷:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "필터:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "모두" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "변경" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "오류" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "정보" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync 전송 실패 (시험용)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] 오류, [I] 정보, [C] 변경" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "경로 디코딩" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "질문" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "프로필: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "마지막 로그 보기" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "{appname} 시작" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "진행 중…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "전송:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "속도:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "남은시간:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "스냅샷" #: qt/qttools.py:427 msgid "Today" msgstr "오늘" #: qt/qttools.py:434 msgid "Yesterday" msgstr "어제" #: qt/qttools.py:443 msgid "This week" msgstr "어번 주" #: qt/qttools.py:450 msgid "Last week" msgstr "지난 주" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "이것은 스냅샷이 아니라 로컬 파일의 실시간 보기입니다" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "마지막 확인 시간 {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "전체 로그 보기" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH 프록시" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "호스트:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "포트:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "사용자:" #: qt/settingsdialog.py:120 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 "" "이 프록시로 대상 호스트에 연결합니다 (호스트 건너뛰기). \"ssh\" 명령 문서에서 \"-J\" 옵션 또는 \"ssh_config\"" " 설명서 페이지의 \"ProxyJump\" 옵션의 자세한 내용을 참고하십시오." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "프로필 관리" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "편집" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "추가" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "삭제" #: qt/settingsdialog.py:210 msgid "&General" msgstr "일반(&G)" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "모드:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "스냅샷을 저장할 위치" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "폴더" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH 설정" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "경로:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "암호:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "개인 키:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "개인 키 파일을 선택하십시오(보통 \"id_rsa\" 파일)" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "암호 없는 새 SSH 키 생성(개인 키 파일을 이미 선택했을 경우 허용하지 않음)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "암호" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "키링(Keyring)에 암호 저장" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "크론 캐시 암호(보안 문제: 루트 계정에서 암호를 볼 수 있음)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "고급 설정" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "전체 스냅샷 경로:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "일정" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "비활성화됨" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "부팅/재부팅 할 때 마다" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "매 {n}분 마다" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "{n}시간마다" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "매 {n}시간마다" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "시간 주기 설정" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "매일" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "반복 (아나크론)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "드라이브를 연결했을 때(udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "매주" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "매월" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "매년" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "일:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "요일:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "시간:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "시간:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "반복적으로 Back In Time을 실행합니다. 컴퓨터를 비정기적으로 사용할 때 쓸만합니다." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "매:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "시간" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "일" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "주" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "월" #: qt/settingsdialog.py:555 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 "" "드라이브를 연결하면 Back In Time을 실행합니다(매 X일에 한번씩).\n" "sudo 암호 입력이 필요합니다." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "디버깅 메시지 기록 활성" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "디버깅 수준 메시지를 \"--debug\" 옵션으로 시스템 로그에 기록합니다." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "위험: 엄청난 양의 출력 내용을 만들어내므로 진단 목적으로 잠시 동안만 사용하십시오." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "포함(&I)" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "폴더와 파일 포함" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "파일 추가" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "폴더 추가" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "제외(&E)" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "파일 또는 폴더의 예외 패턴" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "기본값 추가" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "크기가 큰 파일 제외:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "크기가 큰 파일 제외: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "%(prefix)s 값보다 큰 파일을 제외합니다.\n" "'Full rsync mode'를 사용하지 않을 경우 새 파일에만 영향을 줍니다.\n" "rsync에서는 전송 옵션이지, 제외 옵션이 아니기 때문입니다.\n" "그래서 이전에 백업한 큰 파일은 내용이 바뀌더라도\n" "스냅샷에 남습니다." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "자동 제거(&A)" #: qt/settingsdialog.py:726 #, fuzzy msgid "Older than:" msgstr "날짜가 다음 보다 오래된 경우" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "년" #: qt/settingsdialog.py:748 #, fuzzy msgid "If free space is less than:" msgstr "여유 공간이 다음보다 작은 경우" #: qt/settingsdialog.py:768 #, fuzzy msgid "If free inodes is less than:" msgstr "여유 공간이 다음보다 작은 경우" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "알아서 제거:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "원격 호스트의 백그라운드에서 실행합니다." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "실험적기능" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "모든 스냅샷의 최신 유지" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "일." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "하루에 최신 스냅샨 1건 유지" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "일주일에 최신 스냅샷 한건 유지" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "주." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "한달에 최신 스냅샷 한건 유지" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "달." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "일년에 최신 스냅샷 한건 유지." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "이름을 붙인 스냅샷은 제거하지 않습니다." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "선택 사항" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "알림 사용하기" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "배터리 사용중에는 스냅샷을 찍을 수 없습니다" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "시스템의 전원 상태를 알 수 없습니다" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "동시에 하나의 스냅샷 동작만 수행" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "현재 스냅샷을 처리할 때까지 다른 스냅샷 처리를 차단합니다. 이 동작은 전역 옵션입니다. 이 사용자의 모든 프로파일에 영향을 줍니다. " "다만, 다른 사용자에게도 이 옵션을 설정해야합니다." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "복원시 대체 파일 백업" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "최신 버전 파일은 복원하기 전 뒤에 {suffix}를 붙인 이름으로 바뀝니다. 더 이상 필요하지 않으면 {cmd}을(를) 사용하여 제거할" " 수 있습니다" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "오류가 있어도 진행(미완 스냅샷 유지)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "체크섬을 이용해 변경사항 찾기" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "변경 사항과 관계없이 새로운 스냅샷 생성." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "로깅 수준:" #: qt/settingsdialog.py:911 msgid "None" msgstr "없음" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "고급 설정" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "주의: 이 옵션들이 무엇을 의미하는지 정확하게 알 경우에만 바꾸세요." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "'{cmd}' 명령으로 'rsync' 실행:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "크론 작업으로 실행" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "원격 호스트에서" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "사용자정의 스냅샷을 찍을때" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(이 옵션을 활성화 하려면 'nocache'를 설치하세요)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "로컬 머신에서" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "크론 작업의 표준 출력을 /dev/null로 보냄." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "MTA를 설치했다면 크론 작업 출력 내용을 첨부한 전자메일을 크론에서 자동으로 보냅니다." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "크론 작업의 오류 출력을 /dev/null로 보냄." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "MTA를 설치했다면 크론 작업 오류 내용을 첨부한 전자메일을 크론에서 자동으로 보냅니다." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "rsync 대역폭 사용량 제한:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/sec" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "ACL 설정값 보존" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "확장 속성 (xattr) 보존" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "불안전한 링크 복사(절대 링크인 경우만 동작)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "하나의 파일 시스템으로 제한" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "옵션은 따옴표 안에 넣어야 합니다. 예. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "rsync에 추가 옵션 넣기" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "SSH 명령에 접두 옵션 추가" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "원격 호스트의 모든 명령 앞에 붙여 실행할 접두부입니다." #: qt/settingsdialog.py:1188 #, fuzzy, 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:1196 msgid "default" msgstr "기본값" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "원격 호스트가 접근 가능한지 확인" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "경고: 이 옵션이 꺼져있고 원격 호스트를 사용할 수 없다면 이상한 오류를 유발할 수 있습니다." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "원격 호스트에서 필요한 모든 명령을 지원하는지 확인합니다." #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "설정 복구" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "user-callback 편집" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "새로운 프로파일" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "프로파일명을 변경" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "정말 \"{name}\" 프로파일을 삭제하시겠습니까?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "강력 추천" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "새 암호 없이 공용/개인키 쌍을 만드시겠습니까?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "\"{file}\" 개인키 파일이 없습니다." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "암호 없는 로그인에 사용할 SSH 키를\n" "원격 호스트에 복사하시겠습니까?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "{host} 호스트 인증을 처리할 수 없습니다.\n" "\n" "{keytype} 키 형식의 지문키:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "이 지문키를 검증하세요! 'known_hosts' 파일에 추가할까요?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "제외 패턴" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "제외할 파일" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "제외할 폴더" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "파일 포함" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" 경로는 심볼릭 링크입니다. 포함하기 전까지는 백업하지 않습니다.\n" "백업에 심볼릭 링크를 추가하시겠습니까?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "폴더를 포함" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "정말 스냅샷 폴더를 바꾸시겠습니까?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "{path}에 새로운 SSH 키 생성 실패" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "이 패턴은 'SSH 암호화' 모드에서 동작하지 않기 때문에 사용할 수 없습니다." #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "기본값" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "비활성화" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "활성화" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "설정 없음" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "스냅샷 비교 옵션" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "명령" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters:" msgstr "매개변수" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "경로 매개변수로 %1과 %2를 사용" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "diff 명령을 설정하거나 취소를 누르십시오." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "스냅샷 비교만 진행" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "다음 조건에 동일한 스냅샷만 조회: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "자세히 확인(느리지만 정확함)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "삭제" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "전체 선택" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "비교" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "이동하기" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "옵션" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "스냅샷을 자신과 비교할 수 없습니다." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "정말로 {snapshot_id} 스냅샷에서 {file} 파일을 삭제하시겠습니까?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "정말로 {count}개의 스냅샷에서 {file} 파일을 삭제하시겠습니까?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "경고: 이 동작은 취소할 수 없습니다." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "앞으로의 스냅샷에서 {path} 경로를 제외하시겠습니까?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "crontab을 찾을 수 없습니다. cron을 설치했습니까? 그렇지 않으면 모든 자동 백업을 멈춰야 합니다." #~ msgid "Full snapshot path" #~ msgstr "전체 스냅샷 위치" #~ msgid "Mode" #~ msgstr "모드" #~ msgid "Profile" #~ msgstr "프로필" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "'{profile}' 프로필: {mode} 암호를 입력하세요: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "user-callback 스크립트의 쉬뱅 명령을 실행할 수 없습니다." #~ msgid "WARNING" #~ msgstr "경고" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "encfs 버전 1.7.2 이하에는 --reverse 옵션과 관련된 버그가 있습니다. encfs를 업데이트하세요." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "user-callback 스크립트에 쉬뱅(#!/bin/sh) 행이 없습니다." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app}은 암호화를 위해 EncFS를 사용합니다. 최근 보안 감사에서는 이에 대한 몇 가지 가능한 공격 경로가 밝혀졌습니다. \"man" #~ " backintime\"의 \"A NOTE ON SECURITY\"를 살펴보십시오." backintime-1.5.2/common/po/lt.po000066400000000000000000001472461465446530500165440ustar00rootroot00000000000000# Lithuanian 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \n" "Language-Team: Lithuanian \n" "Language: lt\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%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Įspėjimas" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Pagrindinis profilis" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "užšifruotas" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "Užšifruotas su SSH" #: common/config.py:309 msgid "Local" msgstr "Vietinis" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH privatus raktas" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "užšifruotas" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Užšifravimas" #: common/config.py:320 msgid "SSH encrypted" msgstr "Užšifruotas su SSH" #: common/config.py:327 msgid "Default" msgstr "Numatytasis" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profilis: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Momentinių kopijų aplankas negalioja!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "" "Turite pasirinkti bent vieną aplanką, kad išsaugoti kaip atsarginę kopiją!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:447 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} nėra aplankas." #: common/config.py:456 #, fuzzy msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID negali būti tuščias." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Neįmanoma įrašyti į: {path}\n" "Ar turite įrašymo prieigas?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Paskirties failų sistema, skirta {path}, suformatuota naudojant FAT, kuri " "nepalaiko kietųjų nuorodų. Prašome naudoti savąją Linux failų sistemą." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Paskirties{path} failų sistema yra SMB resursas. Įsitikinkite, kad SMB " "serveris palaiko simbolines sąsajas, arba {expertOptions} įjunkite " "{copyLinks}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopijuoti nuorodas (atsieti simbolines nuorodas)" #: common/config.py:497 msgid "Expert Options" msgstr "Parinktys ekspertams" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Paskirties failų sistema, skirta {path}, yra sshfs prijungta dalis. sshfs " "nepalaiko kietųjų nuorodų. Vietoj to naudokite režimą „SSH“." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Nepavyko įrašyti naujo crontab failo." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Nepavyko įdiegti Udev taisyklės profiliui {profile_id}.DBus " "Service'{dbus_interface}', buvo nepasiekiamas" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Suplanuoti udev neveikia naudojant režimą {mode}" #: common/config.py:1772 #, fuzzy, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Nepavyko rasti UUID adresui \"{path}\"" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Nepavyko išsaugoti konfiguracijos" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Nepavyko įkelti konfiguracijos" #: common/configfile.py:690 common/configfile.py:789 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilis \"{name}\" jau egzistuoja." #: common/configfile.py:735 #, fuzzy msgid "The last profile cannot be removed." msgstr "Tai negali būti atšaukta." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Neįmanoma įrengti '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Konfiguracija užšifruotam aplankui nerasta." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Sukurti naują užšifruotą aplanką?" #: common/encfstools.py:151 msgid "Cancel" msgstr "" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Prašome patvirtinti slaptažodį." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Išsaugoti momentinę nuotrauką" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "" #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "NEPAVYKO" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Atstatyti prieigas" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Atlikta" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Atsarginės kopijos kūrimas atidedamas, kol naudojamas akumuliatorius" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Nepavyko rasti momentinių nuotraukų aplanko.\n" "Jei jis yra keičiamame diske, prijunkite jį." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Laukti %s sekundes." msgstr[1] "Laukti %s sekundžių." msgstr[2] "Laukti %s sekundžių." #: common/snapshots.py:907 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Nepavyko išsaugoti momentinės kopijos {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Baigiama" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Nepavyko sukurti aplanko" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Išsaugomas konfigūracijos failas…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Prieigos yra išsaugomos…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Rastas likęs {snapshot_id}, kurį galima tęsti." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Šalinamas atlikęs {snapshot_id} aplankas iš praeitos operacijos" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Nepavyko pašalinti aplanko" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Padaryti momentinę nuotrauką" #: common/snapshots.py:1417 msgid "Success" msgstr "" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Niekas nepakito, įrašyti naujos momentinės kopijos neprivaloma" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Neįmanoma pervadinti {new_path} į {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Išmanus pašalinimas" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Senų momentinių kopijų pašalinimas" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Bandyti" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Bandoma išlaikyti mažiausiai {perc} laisvų inodų" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Dabar" #: common/sshtools.py:239 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Neįmanoma įrengti {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:692 #, fuzzy msgid "Remote path is not executable." msgstr "„Shebang“ vartotojo atgalinio iškvietimo scenarijuje nėra vykdomas." #: common/sshtools.py:697 #, fuzzy msgid "Couldn't create remote path." msgstr "Nepavyko sukurti aplanko." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" "Kopijuoti viešą SSH raktą \"{pubkey}\" į nuotolinį pagrindinį kompiuterį " "\"{host}\"" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Prašome įvesti slaptažodį vartotojui \"{user}\"" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Apie" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autoriai" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Vertimai" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licenzija" #: qt/app.py:169 msgid "Shortcuts" msgstr "Nuorodos" #: qt/app.py:189 #, fuzzy msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Šis aplankas neegzistuoja\n" "pasirinktoje momentinėje kopijoje." #: qt/app.py:256 msgid "Add to Include" msgstr "Pridėti prie įtraukti" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Pridėti, prie neįtraukti" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Nepavyko sukurti aplanko" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Nepavyko rasti momentinių nuotraukų aplanko.\n" "Jei jis yra keičiamame diske, prijunkite jį ir paspauskite OK." #: qt/app.py:481 #, fuzzy msgid "Take a snapshot" msgstr "Išsaugoti momentinę nuotrauką" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:486 #, fuzzy msgid "Take a snapshot (checksum mode)" msgstr "Išsaugoti momentinę kopiją su kontroline suma" #: qt/app.py:488 #, fuzzy msgid "Use checksums for file change detection." msgstr "Pasikeitimų aptikimui naudoti kontrolines sumas." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pristabdyti momentinės kopijos procesą" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Pratęsti momentinės kopijos procesą" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Stabdyti momentinės kopijos procesą" #: qt/app.py:504 #, fuzzy msgid "Refresh snapshot list" msgstr "Atnaujinti momentinių kopijų sąrašą" #: qt/app.py:508 #, fuzzy msgid "Name snapshot" msgstr "Išsaugoti momentinę nuotrauką" #: qt/app.py:512 #, fuzzy msgid "Remove snapshot" msgstr "Pašalinti momentinę kopiją" #: qt/app.py:516 #, fuzzy msgid "View snapshot log" msgstr "Peržiūrėti momentinių kopijų žurnalą" #: qt/app.py:520 #, fuzzy msgid "View last log" msgstr "Peržiūrėti paskutinį įrašą žurnale" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Pagrindinis profilis…" #: qt/app.py:528 msgid "Shutdown" msgstr "Išjungti" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Išjungti sistemą, kai momentinė kopija baigs veikti." #: qt/app.py:532 msgid "Setup language…" msgstr "" #: qt/app.py:536 msgid "Exit" msgstr "Išeiti" #: qt/app.py:540 msgid "Help" msgstr "Pagalba" #: qt/app.py:544 #, fuzzy msgid "Profiles config file" msgstr "Išsaugomas konfigūracijos failas" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Interneto svetainė" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Pakeitimų sąrašas" #: qt/app.py:553 msgid "FAQ" msgstr "D.U.K." #: qt/app.py:556 msgid "Ask a question" msgstr "Užduokite klausimą" #: qt/app.py:559 msgid "Report a bug" msgstr "Pranešti apie klaidą" #: qt/app.py:562 #, fuzzy msgid "Translation" msgstr "Vertimai" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Atstatyti" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Atkurkite pasirinktus failus arba aplankus į pradinę paskirties vietą." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 #, fuzzy msgid "Restore to …" msgstr "Atkurti į …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Atkurkite pasirinktus failus arba aplankus į naują paskirties vietą." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Atkurkite pasirinktus failus arba aplankus į originalią paskirties vietą." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Atkurkite šiuo metu rodomą aplanką ir visą jo turinį į naują paskirties " "vietą." #: qt/app.py:595 msgid "Up" msgstr "Aukštyn" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Rodyti paslėptus failus" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Išsaugoti momentinę nuotrauką…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 msgid "&Restore" msgstr "&Atstatyti" #: qt/app.py:682 msgid "&Help" msgstr "&Pagalba" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Jeigu uždarysite šį langą, „Back In Time“ negalės išjungti Jūsų kompiuterio pasibaigus momentinės kopijos kūrimui.\n" "Ar tikrai norite uždaryti?" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Ar tikrai norite atkurti failus" #: qt/app.py:987 msgid "Working:" msgstr "Dirbu:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Atlikta, atsarginė kopija nebūtina" #: qt/app.py:1044 msgid "Working" msgstr "Dirbama" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Klaida" #: qt/app.py:1076 msgid "Sent" msgstr "Išsiųsta" #: qt/app.py:1077 msgid "Speed" msgstr "Greitis" #: qt/app.py:1078 msgid "ETA" msgstr "Numatomas atvykimo laikas" #: qt/app.py:1140 msgid "Global" msgstr "Globalus" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Pradžia" #: qt/app.py:1170 msgid "Backup folders" msgstr "Atsarginių kopijų aplankai" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Momentinės kopijos pavadinimas" #: qt/app.py:1313 #, fuzzy msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Ar jūs tikrai norite pašalinti momentinę kopiją" msgstr[1] "Ar jūs tikrai norite pašalinti momentinę kopiją" msgstr[2] "Ar jūs tikrai norite pašalinti momentinę kopiją" #: qt/app.py:1408 #, fuzzy, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Sukurkite atsargines kopijas su pabaiga {suffix} prieš tai\n" "vietinių failų perrašymas arba pašalinimas." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Prieš atkuriant, naujesnės failų versijos bus pervardytos su pabaiga {suffix}.\n" "Jei jums jų nebereikia, galite juos pašalinti naudodami {cmd}" #: qt/app.py:1432 #, fuzzy msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Atkurkite tik tuos failus, kurių nėra arba\n" "yra naujesnės nei paskirties vietos.\n" "Naudojant parinktį „rsync --update“." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Pašalinkite naujesnius failus iš pradinio aplanko." #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Atkurti pasirinktus failus ar aplankus į pradinį taikinį ir\n" "ištrinti failus/aplankus, kurių nėra momentinėje kopijoje.\n" "Tai ištrins failus/aplankus, kurie nebuvo įtraukti į kopiją jos kūrimo metu.\n" "Būkite labai atsargūs!!!" #: qt/app.py:1481 #, fuzzy, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Ar tikrai norite atkurti failus\n" "į naują aplanką {path}" msgstr[1] "" "Ar tikrai norite atkurti failus\n" "į naują aplanką {path}" msgstr[2] "" "Ar tikrai norite atkurti failus\n" "į naują aplanką {path}" #: qt/app.py:1490 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Ar tikrai norite atkurti failus" msgstr[1] "Ar tikrai norite atkurti failus" msgstr[2] "Ar tikrai norite atkurti failus" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Ar tikrai norite pašalinti visus naujesnius failus iš {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Ar tikrai norite pašalinti visus naujesnius failus iš pradinio aplanko?" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "ĮSPĖJIMAS: Failų, esančių failų sistemos šaknyje trynimas gali sugadinti " "jūsų visą operacinę sistemą!!!" #: qt/app.py:1750 msgid "Snapshot" msgstr "Momentinė kopija" #: qt/app.py:1787 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Atstatyti {path}" #: qt/app.py:1789 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Atstatyti {path} į …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 #, fuzzy msgid "System default" msgstr "numatytasis" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:188 #, 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 "" "Sveiki\n" "Jau kelis kartus naudojotės Back In Time {language} kalba.\n" "Jūsų įdiegtos Back In Time versijos vertimas į {language} yra {perc} baigtas. Nepaisant jūsų įgūdžių lygio galite prisidėti prie vertimo, taip prisidėdami prie pačio Back In Time.\n" "Kviečiame apsilankyti {translation_platform_url} jei norite prisidėti. Jei jums reikalinga pagalba ar turite klausimų, prašome apsilankyti {back_in_time_project_website}.\n" "Atsiprašome už sutrukdymą, ši žinutė daugiau rodoma nebus. Ši informacija pasiekiama bet kuriuo laiku pagalbos meniu.\n" "Jūsų Back In Time komanda" #: qt/languagedialog.py:217 #, fuzzy msgid "translation platform" msgstr "Vertimai" #: qt/languagedialog.py:242 #, fuzzy msgid "Your translation" msgstr "Vertimai" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Paskutinio \"log\" vaizdas" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Momentinės kopijos žurnalo vaizdas" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 #, fuzzy msgid "Profile:" msgstr "Profilis" #: qt/logviewdialog.py:84 #, fuzzy msgid "Snapshots:" msgstr "Momentinės kopijos" #: qt/logviewdialog.py:99 #, fuzzy msgid "Filter:" msgstr "Filtras" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Visi" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Pakeitimai" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Kaidos" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Informacija" msgstr[1] "Informacija" msgstr[2] "Informacija" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Klaida, [I] Informacija, [C] Pakeisti" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "iškoduoti adresus" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Klausimas" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profilis: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Peržiūrėti paskutinį įrašą žurnale" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Paleisti {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Dirbama…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Išsiųsta:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Greitis:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Momentinės kopijos" #: qt/qttools.py:427 msgid "Today" msgstr "Šiandien" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Vakar" #: qt/qttools.py:443 msgid "This week" msgstr "Šią savaitę" #: qt/qttools.py:450 msgid "Last week" msgstr "Praeitą savaitę" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Tai NE momentinė kopija, o tiesioginis vietinių failų vaizdas" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Paskutinis patikrinimas {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Parodyti visą žurnalą" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Pagrindinis kompiuteris:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Prievadas:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Naudotojas:" #: qt/settingsdialog.py:120 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:170 #, fuzzy msgid "Manage profiles" msgstr "Pagrindinis profilis" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Keisti" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Pridėti" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Ištrinti" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Bendri" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Būsena:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Kur išsaugoti momentines kopijas" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Aplankas" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH nustatymai" #: qt/settingsdialog.py:290 #, fuzzy msgid "Path:" msgstr "Adresas" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Šifras:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Privatus Raktas:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Pasirinkite esamą privataus rakto failą (pagal nutylėjimą užvadintą " "\"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Sukurti naują SSH raktą be slaptažodžio (negalima, jei privataus rakto " "failas jau pasirinktas)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Slaptažodis" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Išsaugoti slaptažodį" #: qt/settingsdialog.py:378 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/settingsdialog.py:390 msgid "Advanced" msgstr "Išplėstiniai parametrai" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Visas momentinės kopijos kelias:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Tvarkaraštis" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Išjungta" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Po kiekvieno operacinės sistemos paleidimo/perkrovimo" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Kas {n} minutes" msgstr[1] "Kas {n} minutes" msgstr[2] "Kas {n} minutes" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Kas valandą" msgstr[1] "Kas valandą" msgstr[2] "Kas valandą" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Kas {n} valandos" msgstr[1] "Kas {n} valandos" msgstr[2] "Kas {n} valandos" #: qt/settingsdialog.py:457 #, fuzzy msgid "Custom hours" msgstr "Pasirinktinos valandos" #: qt/settingsdialog.py:458 #, fuzzy msgid "Every day" msgstr "Kasdien" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Pakartotinai (anachronistiškai)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Kai tvarkyklė yra prijungta (udev)" #: qt/settingsdialog.py:461 #, fuzzy msgid "Every week" msgstr "Kas savaitę" #: qt/settingsdialog.py:462 #, fuzzy msgid "Every month" msgstr "Kas mėnesį" #: qt/settingsdialog.py:463 #, fuzzy msgid "Every year" msgstr "Kasmet" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Savaitės diena:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Valanda:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Valandos:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Pakartotinai paleiskite Back In Time. Tai naudinga, jei kompiuteris neveikia" " reguliariai." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Kiekvienas:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Valandą (-as)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Diena (os/ų)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Savaitė (ės/čių)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Mėnesį (-ius)" #: qt/settingsdialog.py:555 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 "" "Paleisti „Back In Time“ iš karto po disko prijungimo (tik kas X dienų)\n" "Bus prašoma įvesti Jūsų sudo slaptažodį." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Įtraukti" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Įtraukti failus bei aplankus" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Pridėti failą" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Pridėti aplanką" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "Išs&kirti" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Išskirkite šablonus, failus ar aplankus" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Pridėti numatytąjį" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Išskirti failus, didesnius, nei:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Išskirti failus, didesnius, nei: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been 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/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Pašalinti automatiškai" #: qt/settingsdialog.py:726 #, fuzzy msgid "Older than:" msgstr "Senesni nei" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Metai (-ų)" #: qt/settingsdialog.py:748 #, fuzzy msgid "If free space is less than:" msgstr "Jei laisvos vietos mažiau nei" #: qt/settingsdialog.py:768 #, fuzzy msgid "If free inodes is less than:" msgstr "Jei laisvų inodų mažiau nei" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Išmanus pašalinimas:" #: qt/settingsdialog.py:793 #, fuzzy msgid "Run in background on remote host." msgstr "Vykdykite nuotolinio pagrindinio kompiuterio fone." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTINIS" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Išsaugokite visas momentines kopijas paskutinei" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Diena(os)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Paskutinį kartą išsaugokite vieną momentinę kpiją per dieną" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Paskutinį kartą išsaugokite vieną momentinę kopiją per savaitę" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Savaitė(s)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Paskutinį kartą išsaugokite vieną momentinę kopiją per mėnesį" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "Mėnesis(iai)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "" "Paskutinį kartą išsaugokite vieną momentinę kopiją per metus visus metus." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Nešalinti momentinių kopijų, turinčių pavadinimus." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "Pa&rinktys" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Įgalinti pranešimus" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Išjunkite momentines kopijas, kai naudojate akumuliatorių" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Maitinimo būsena nepasiekiama iš sistemos" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Vienu metu paleisti tik vieną momentinę kopiją" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." 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/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Atkūrimo metu sukurkite pakeistų failų atsarginę kopiją" #: qt/settingsdialog.py:879 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Prieš atkuriant, naujesnės failų versijos bus pervardytos su pabaiga {suffix}.\n" "Jei jums jų nebereikia, galite juos pašalinti naudodami {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Tęskite klaidas (išsaugokite neišsamias momentines kopijas)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Pasikeitimų aptikimui naudoti kontrolines sumas" #: qt/settingsdialog.py:899 #, fuzzy msgid "Take a new snapshot whether there were changes or not." msgstr "" "Padarykite naują momentinę kopiją, nepaisant ar buvo pakeitimų, ar ne." #: qt/settingsdialog.py:906 #, fuzzy msgid "Log Level:" msgstr "Žurnalo lygis" #: qt/settingsdialog.py:911 msgid "None" msgstr "Tuščia" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Parinktys &ekspertams" #: qt/settingsdialog.py:936 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Keiskite šias parinktis tik tuo atveju, jei tikrai žinote, ką darote." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Paleiskite „rsync“ su „{cmd}“:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "kaip cron job" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "nuotoliniame pagrindiniame kompiuteryje" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "darant momentinę kopiją rankiniu būdu" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Norėdami įjungti šią parinktį, įdiekite 'nocache')" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "vietiniame kompiuteryje" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Nukreipkite stdout į /dev/null cronjobs." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Nukreipkite stderr į /dev/null cronjobs." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "Apriboti rsync pralaidumo naudojimą" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Išsaugoti ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Išsaugoti išplestinius atributus (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopijuoti nesaugias nuorodas (veikia tik su absoliučiomis nuorodomis)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, 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:1171 msgid "Paste additional options to rsync" msgstr "Įklijuokite papildomas parinktis į rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Pridėkite priešdėlį prie SSH komandų" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "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" #: qt/settingsdialog.py:1196 msgid "default" msgstr "numatytasis" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Patikrinkite, ar nuotolinis kompiuteris yra prisijungęs" #: qt/settingsdialog.py:1214 #, 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:1218 #, fuzzy msgid "Check if remote host supports all necessary commands." msgstr "" "Patikrinkite, ar nuotolinis kompiuteris palaiko visas reikalingas komandas" #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "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/settingsdialog.py:1237 msgid "Restore Config" msgstr "Atkurti Config" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Redaguoti naudotojo-galinį iškvietimą" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Naujas profilis" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Pervadinti profilį" #: qt/settingsdialog.py:1318 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Ar jūs tikrai norite ištrinti profilį \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Labai patariama" #: qt/settingsdialog.py:1634 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 "" "Tinkintos valandos gali būti tik kableliais atskirtas valandų sąrašas (pvz.," " 8,12,18,23) arba */3, kai atsarginės kopijos daromos kas 3 valandas." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Jūs nepasirinkote SSH privataus rakto failo.\n" "Ar norėtumėte sugeneruoti naują viešojo / privataus raktų porą be slaptažodžių?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Privataus rakto failas \"{file}\" neegzistuoja." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Ar norite nukopijuoti viešąjį SSH raktą į\n" "nuotolinio kompiuterio, kad įgalintumėte prisijungimą be slaptažodžio?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "{host} autentiškumo nustatyti nepavyko.\n" "\n" "{keytype} rakto kontrolinis kodas yra:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Patvirtinkite šį piršto atspaudą! Ar norėtumėte pridėti jį prie failo " "\"known_hosts\"?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Išskirti šabloną" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Išskirti failą" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Išskirti aplanką" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Įtraukti failą" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "„{path}“ yra simbolis. Susieto tikslo atsarginė kopija nebus sukurta, kol neįtrauksite ir jos.\n" "Ar vietoj to norėtumėte įtraukti simbolio nuorodą?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Įtraukti aplanką" #: qt/settingsdialog.py:2169 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Ar tikrai norite pakeisti momentinių kopijų aplanką?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Nepavyko sukurti naujo SSH rakto {path}" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "numatytasis" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "išjungta" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "įjungta" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Konfigūracija nerasta" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Komanda" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters:" msgstr "Parametrai" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "naudoti %1 ir %2 kaip kelio parametrus" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Tik skirti skirtingus momentinius vaizdus" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Išvardykite tik tokias momentines nuotraukas kaip: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Gilus apžiūrėjimas (tikslesnis, bet lėtesnis)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Ištrint" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Pasirinkti viską" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Eiti į" #: qt/snapshotsdialog.py:204 #, fuzzy msgid "Options" msgstr "&Parinktys" #: qt/snapshotsdialog.py:355 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Jūs negalite palyginti momentinės kopijos su ja pačia." #: qt/snapshotsdialog.py:398 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Ar tikrai norite ištrinti \"{file}\" momentinėje nuotraukoje " "\"{snapshot_id}\"?" #: qt/snapshotsdialog.py:404 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Ar tikrai norite ištrinti „{file}“ iš {count} momentinių nuotraukų?" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "Tai negali būti atšaukta!" #: qt/snapshotsdialog.py:426 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Ištraukti \"{path}\" iš momentinių nuotraukų ateityje?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Neįmanoma rasti crontab.\n" #~ "Ar esate įsitikinę, kad cron įdiegtas?\n" #~ "Jei ne, turėtumėte išjungti visas automatines atsargines kopijas." #~ msgid "Full snapshot path" #~ msgstr "Pilnas kelias į momentinę kopiją" #~ msgid "Mode" #~ msgstr "Būsena" #~ msgid "Profile" #~ msgstr "Profilis" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profilis '{profile}': Įveskite slaptažodį dėl {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "„Shebang“ vartotojo atgalinio iškvietimo scenarijuje nėra vykdomas." #~ msgid "WARNING" #~ msgstr "DĖMESIO" #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "" #~ "vartotojo atgalinio iškvietimo scenarijus neturi shebang (#!/bin/sh) " #~ "eilutės." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} šifravimui naudoja EncFS. Neseniai atliktas saugumo auditas atskleidė " #~ "keletą galimų vekotrių tokio tipo puolimui. Prašome peržiūrėti \"ŽINUTĖ APIE" #~ " SAUGUMĄ\" \"man backintime\" puslapyje\"." backintime-1.5.2/common/po/messages.pot000066400000000000000000001101631465446530500201040ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the "Back In Time" package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: \"Back In Time\" \"1.5.2-dev\"\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-08-05 17:07+0200\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=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1271 msgid "Warning" msgstr "" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "" #: common/config.py:309 msgid "Local" msgstr "" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2194 msgid "SSH private key" msgstr "" #: common/config.py:314 msgid "Local encrypted" msgstr "" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "" #: common/config.py:320 msgid "SSH encrypted" msgstr "" #: common/config.py:327 msgid "Default" msgstr "" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "" #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "" #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" #: common/config.py:496 qt/settingsdialog.py:1139 msgid "Copy links (dereference symbolic links)" msgstr "" #: common/config.py:497 msgid "Expert Options" msgstr "" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "" #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "" #: common/configfile.py:107 msgid "Failed to save config" msgstr "" #: common/configfile.py:143 msgid "Failed to load config" msgstr "" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "" #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "" #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "" #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "" #: common/encfstools.py:151 msgid "Cancel" msgstr "" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "" #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:1006 qt/app.py:1041 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "" msgstr[1] "" #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "" #: common/snapshots.py:937 msgid "Finalizing" msgstr "" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "" #: common/snapshots.py:1417 msgid "Success" msgstr "" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" #: common/snapshots.py:2989 qt/app.py:1752 msgid "Now" msgstr "" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "" #: common/sshtools.py:986 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:990 common/sshtools.py:999 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:994 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1015 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1169 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" #: common/sshtools.py:1171 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "" #: qt/app.py:169 msgid "Shortcuts" msgstr "" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:256 msgid "Add to Include" msgstr "" #: qt/app.py:258 msgid "Add to Exclude" msgstr "" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "" #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" #: qt/app.py:481 msgid "Take a snapshot" msgstr "" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "" #: qt/app.py:508 msgid "Name snapshot" msgstr "" #: qt/app.py:512 msgid "Remove snapshot" msgstr "" #: qt/app.py:516 msgid "View snapshot log" msgstr "" #: qt/app.py:520 msgid "View last log" msgstr "" #: qt/app.py:524 msgid "Manage profiles…" msgstr "" #: qt/app.py:528 msgid "Shutdown" msgstr "" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:532 msgid "Setup language…" msgstr "" #: qt/app.py:536 msgid "Exit" msgstr "" #: qt/app.py:540 msgid "Help" msgstr "" #: qt/app.py:544 msgid "Profiles config file" msgstr "" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "" #: qt/app.py:550 qt/app.py:1384 msgid "Changelog" msgstr "" #: qt/app.py:553 msgid "FAQ" msgstr "" #: qt/app.py:556 msgid "Ask a question" msgstr "" #: qt/app.py:559 msgid "Report a bug" msgstr "" #: qt/app.py:562 msgid "Translation" msgstr "" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:580 qt/app.py:1555 qt/app.py:1587 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new destination." msgstr "" #: qt/app.py:595 msgid "Up" msgstr "" #: qt/app.py:598 qt/settingsdialog.py:2461 msgid "Show hidden files" msgstr "" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 msgid "&Restore" msgstr "" #: qt/app.py:682 msgid "&Help" msgstr "" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "" #: qt/app.py:996 msgid "Working:" msgstr "" #: qt/app.py:1044 msgid "Done, no backup needed" msgstr "" #: qt/app.py:1053 msgid "Working" msgstr "" #: qt/app.py:1062 qt/messagebox.py:78 msgid "Error" msgstr "" #: qt/app.py:1085 msgid "Sent" msgstr "" #: qt/app.py:1086 msgid "Speed" msgstr "" #: qt/app.py:1087 msgid "ETA" msgstr "" #: qt/app.py:1149 msgid "Global" msgstr "" #: qt/app.py:1150 msgid "Root" msgstr "" #: qt/app.py:1151 msgid "Home" msgstr "" #: qt/app.py:1179 msgid "Backup folders" msgstr "" #: qt/app.py:1275 msgid "Snapshot Name" msgstr "" #: qt/app.py:1322 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "" msgstr[1] "" #: qt/app.py:1417 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1425 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" #: qt/app.py:1441 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" #: qt/app.py:1476 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1479 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because " "this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" #: qt/app.py:1490 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" msgstr[1] "" #: qt/app.py:1499 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "" msgstr[1] "" #: qt/app.py:1514 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1517 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1523 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" #: qt/app.py:1759 msgid "Snapshot" msgstr "" #: qt/app.py:1796 #, python-brace-format msgid "Restore {path}" msgstr "" #: qt/app.py:1798 #, python-brace-format msgid "Restore {path} to …" msgstr "" #: qt/app.py:1957 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1279 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps " "are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 msgid "System default" msgstr "" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:188 #, 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/languagedialog.py:217 msgid "translation platform" msgstr "" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:76 qt/settingsdialog.py:187 qt/settingsdialog.py:426 #: qt/settingsdialog.py:2640 msgid "Profile:" msgstr "" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "" #: qt/logviewdialog.py:105 qt/settingsdialog.py:933 msgid "All" msgstr "" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:932 msgid "Changes" msgstr "" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:929 #: qt/settingsdialog.py:932 msgid "Errors" msgstr "" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "" msgstr[1] "" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "" #: qt/qttools.py:427 msgid "Today" msgstr "" #: qt/qttools.py:434 msgid "Yesterday" msgstr "" #: qt/qttools.py:443 msgid "This week" msgstr "" #: qt/qttools.py:450 msgid "Last week" msgstr "" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:284 qt/settingsdialog.py:414 msgid "Host:" msgstr "" #: qt/settingsdialog.py:107 qt/settingsdialog.py:289 msgid "Port:" msgstr "" #: qt/settingsdialog.py:111 qt/settingsdialog.py:294 qt/settingsdialog.py:420 msgid "User:" msgstr "" #: qt/settingsdialog.py:120 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:179 msgid "Manage profiles" msgstr "" #: qt/settingsdialog.py:196 msgid "Edit" msgstr "" #: qt/settingsdialog.py:200 qt/settingsdialog.py:680 msgid "Add" msgstr "" #: qt/settingsdialog.py:204 qt/settingsdialog.py:627 qt/settingsdialog.py:698 msgid "Remove" msgstr "" #: qt/settingsdialog.py:219 msgid "&General" msgstr "" #: qt/settingsdialog.py:229 qt/settingsdialog.py:2642 msgid "Mode:" msgstr "" #: qt/settingsdialog.py:248 qt/settingsdialog.py:2170 msgid "Where to save snapshots" msgstr "" #: qt/settingsdialog.py:264 msgid "Folder" msgstr "" #: qt/settingsdialog.py:272 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:299 msgid "Path:" msgstr "" #: qt/settingsdialog.py:305 msgid "Cipher:" msgstr "" #: qt/settingsdialog.py:311 msgid "Private Key:" msgstr "" #: qt/settingsdialog.py:321 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:332 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" #: qt/settingsdialog.py:362 qt/settingsdialog.py:371 qt/settingsdialog.py:377 msgid "Password" msgstr "" #: qt/settingsdialog.py:383 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:387 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:402 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:432 qt/settingsdialog.py:2296 msgid "Full snapshot path:" msgstr "" #: qt/settingsdialog.py:439 msgid "Schedule" msgstr "" #: qt/settingsdialog.py:451 msgid "Disabled" msgstr "" #: qt/settingsdialog.py:452 msgid "At every boot/reboot" msgstr "" #: qt/settingsdialog.py:454 qt/settingsdialog.py:456 qt/settingsdialog.py:458 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "" msgstr[1] "" #: qt/settingsdialog.py:460 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "" msgstr[1] "" #: qt/settingsdialog.py:462 qt/settingsdialog.py:464 qt/settingsdialog.py:466 #: qt/settingsdialog.py:468 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "" msgstr[1] "" #: qt/settingsdialog.py:469 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:470 msgid "Every day" msgstr "" #: qt/settingsdialog.py:471 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:472 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:473 msgid "Every week" msgstr "" #: qt/settingsdialog.py:474 msgid "Every month" msgstr "" #: qt/settingsdialog.py:475 msgid "Every year" msgstr "" #: qt/settingsdialog.py:480 msgid "Day:" msgstr "" #: qt/settingsdialog.py:491 msgid "Weekday:" msgstr "" #: qt/settingsdialog.py:507 msgid "Hour:" msgstr "" #: qt/settingsdialog.py:522 msgid "Hours:" msgstr "" #: qt/settingsdialog.py:533 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:540 msgid "Every:" msgstr "" #: qt/settingsdialog.py:554 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:555 qt/settingsdialog.py:750 msgid "Day(s)" msgstr "" #: qt/settingsdialog.py:556 qt/settingsdialog.py:751 msgid "Week(s)" msgstr "" #: qt/settingsdialog.py:557 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:567 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:576 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:580 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:582 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:595 msgid "&Include" msgstr "" #: qt/settingsdialog.py:602 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:619 qt/settingsdialog.py:684 msgid "Add file" msgstr "" #: qt/settingsdialog.py:623 qt/settingsdialog.py:688 msgid "Add folder" msgstr "" #: qt/settingsdialog.py:633 msgid "&Exclude" msgstr "" #: qt/settingsdialog.py:637 #, 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:660 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:693 msgid "Add default" msgstr "" #: qt/settingsdialog.py:706 msgid "Exclude files bigger than:" msgstr "" #: qt/settingsdialog.py:710 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "" #: qt/settingsdialog.py:712 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" #: qt/settingsdialog.py:732 msgid "&Auto-remove" msgstr "" #: qt/settingsdialog.py:738 msgid "Older than:" msgstr "" #: qt/settingsdialog.py:752 msgid "Year(s)" msgstr "" #: qt/settingsdialog.py:760 msgid "If free space is less than:" msgstr "" #: qt/settingsdialog.py:780 msgid "If free inodes is less than:" msgstr "" #: qt/settingsdialog.py:794 msgid "Smart removal:" msgstr "" #: qt/settingsdialog.py:805 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:806 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:812 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:816 qt/settingsdialog.py:823 msgid "day(s)." msgstr "" #: qt/settingsdialog.py:819 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:826 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:830 msgid "week(s)." msgstr "" #: qt/settingsdialog.py:833 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:837 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:840 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:849 msgid "Don't remove named snapshots." msgstr "" #: qt/settingsdialog.py:861 msgid "&Options" msgstr "" #: qt/settingsdialog.py:866 msgid "Enable notifications" msgstr "" #: qt/settingsdialog.py:870 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:874 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:877 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:880 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:888 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:891 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/settingsdialog.py:903 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:907 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:911 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:918 msgid "Log Level:" msgstr "" #: qt/settingsdialog.py:923 msgid "None" msgstr "" #: qt/settingsdialog.py:943 msgid "E&xpert Options" msgstr "" #: qt/settingsdialog.py:948 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:953 qt/settingsdialog.py:969 qt/settingsdialog.py:991 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:960 qt/settingsdialog.py:976 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:965 qt/settingsdialog.py:986 qt/settingsdialog.py:1007 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:981 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:994 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:1001 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:1012 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1017 msgid "" "Cron will automatically send an email with attached output of cronjobs if an " "MTA is installed." msgstr "" #: qt/settingsdialog.py:1023 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1028 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an " "MTA is installed." msgstr "" #: qt/settingsdialog.py:1036 msgid "Limit rsync bandwidth usage:" msgstr "" #: qt/settingsdialog.py:1039 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:1083 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:1101 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:1124 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1160 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1180 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1183 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1197 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1199 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1200 #, 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/settingsdialog.py:1208 msgid "default" msgstr "" #: qt/settingsdialog.py:1223 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1226 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1230 msgid "Check if remote host supports all necessary commands." msgstr "" #: qt/settingsdialog.py:1233 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:1249 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1251 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1272 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision " "on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1294 msgid "New profile" msgstr "" #: qt/settingsdialog.py:1311 msgid "Rename profile" msgstr "" #: qt/settingsdialog.py:1327 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "" #: qt/settingsdialog.py:1431 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1436 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "" #: qt/settingsdialog.py:1643 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:1690 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1691 msgid "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1701 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1856 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" #: qt/settingsdialog.py:1887 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" #: qt/settingsdialog.py:1890 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1898 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:2070 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:2083 msgid "Exclude file" msgstr "" #: qt/settingsdialog.py:2087 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:2111 msgid "Include file" msgstr "" #: qt/settingsdialog.py:2120 qt/settingsdialog.py:2150 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you " "include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2141 msgid "Include folder" msgstr "" #: qt/settingsdialog.py:2178 msgid "Are you sure you want to change snapshots folder?" msgstr "" #: qt/settingsdialog.py:2203 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "" #: qt/settingsdialog.py:2323 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2382 msgid "(default: {})" msgstr "" #: qt/settingsdialog.py:2383 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2383 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2426 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2482 qt/settingsdialog.py:2578 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2507 msgid "Import" msgstr "" #: qt/settingsdialog.py:2535 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2540 msgid "" "If the folder is located on an external or remote drive, it must be manually " "mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "" "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "" #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" backintime-1.5.2/common/po/nb.po000066400000000000000000001466371465446530500165270ustar00rootroot00000000000000# Norwegian Bokmal 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:09+0000\n" "Last-Translator: buhtz \n" "Language-Team: Norwegian Bokmål \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Advarsel" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Hovedprofil" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "kryptert lokalt" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH kryptert" #: common/config.py:309 msgid "Local" msgstr "Lokal" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "privat SSH-nøkkel" #: common/config.py:314 msgid "Local encrypted" msgstr "kryptert lokalt" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Kryptering" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH kryptert" #: common/config.py:327 msgid "Default" msgstr "Standardverdi" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Øyeblikksbildemappe er ugyldig!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Du må velge minst en mappe å sikkerhetskopiere!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Du kan ikke inkludere sikkerhetskopi-mappen." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Du kan ikke inkludere undermapper av sikkerhetskopi-mappen." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ugyldig valg. {path} er ikke en mappe." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Vert/Bruker/Profil-ID kan ikke være tom." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Kan ikke skrive til: {path}\n" "Er du sikker på at du har skrivetilgang?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Målfilsystem for {path} er formatert med FAT som ikke støtter hardlenker. " "Bruk heller et filsystem tilpasset Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Målfilsystemet for {path} er et delt nettverksområde av typen SMB. Se til at" " målfilsystemet støtter symbolske lenker (symlinks), eller aktivér " "{copyLinks} i {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopier symbolske lenker som filer (og fjern lenkereferansen)" #: common/config.py:497 msgid "Expert Options" msgstr "Avanserte innstillinger" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Målfilsystemet for {path} er delt sshfs-montert område, men sshfs støtter " "ikke harde lenker. Vennligst bruk «SSH»-modus i stedet." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Klarte ikke å skrive ny crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Klarte ikke å installere regel for Udev for profilen {profile_id}. DBus-" "tjenesten '{dbus_interface}' var ikke tilgjengelig" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Timeplan udev virker ikke med modusen {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Kunne ikke finne UUID for {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Kunne ikke skrive konfigurasjons-fil" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Kunne ikke laste konfigurasjon-sfil" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" finnes allerede." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Den siste eksistererende profilen kan ikke fjernes." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Kan ikke montere '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Fant ikke konfigurasjon for kryptert mappe." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Lage en ny kryptert mappe?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Avbryt" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Vennligst bekreft passordet." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Passordene stemmer ikke overens." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Ta øyeblikksbilde" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan ikke avmontere {mountprocess} fra {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "Finner ikke {command}. Vennligst installer den, for eksempel via " "«{installcommand}»" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Monteringspunket {mntpoint} er ikke tomt." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Skriv inn passord for {mode} profil «{profile}»:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "FEILET" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Gjenopprett tillatelser" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Ferdig" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Batteridrift utsetter backup" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Kan ikke finne øyeblikksbildemappen.\n" "Hvis den er på en ekstern disk, vennligst plugg den i." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Venter %s sekund." msgstr[1] "Venter %s sekunder." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Øyeblikksbilde feilet {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Fullfører" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Kan ikke opprette mappe" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Lagrer konfigurasjonsfil…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Lagrer tillatelser…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Fant resten {snapshot_id} som kan fortsettes." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Fjerner reste-mappen {snapshot_id} fra forrige kjøring" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Mappen kan ikke slettes" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Tar øyeblikksbilde" #: common/snapshots.py:1417 msgid "Success" msgstr "Suksess" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Delvis overføring på grunn av feil" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Ufullstendig overføring grunnet tap av kildefiler (se 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' avsluttet med kode {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Se 'man rsync' for flere detaljer" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "Negative rsync-returkoder er signalnumre. Se 'kill -l' og 'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Ingenting er endret, så nytt øyeblikksbilde er ikke nødvendig" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Kan ikke endre navn fra {new_path} til {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Smart sletting" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Slett gamle øyeblikksbilder" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Forsøk å bevare min ledig plass" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Forsøker å bevare minst {perc} ledige inodes" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Nå" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan ikke montere {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Fant ingen SSH-agent. Vennligst se til at den er installert." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Klarte ikke å låse opp den private SSH-nøkkelen. Enten er passordet feil, " "eller så er ikke passordet tilgjengelig for cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Chiffer {cipher} feilet for {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Fjern-katalogbanen finnes, men er ikke en mappe." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Fjern-katalogbanen er ikke skrivbar." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Fjern-katalogbanen er ikke kjørbar." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Kan ikke opprette ekstern bane." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Ekstern vert {host} støtter ikke {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Slå opp 'man backintime' for videre instruksjoner" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Kommandosjekk på verten {host} returnerte en ukjent feil" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Fjern-verten {host} støtter ikke harde lenker" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Kopier offentlig ssh nøkkel \"{pubkey}\" til den andre verten «{host}»." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Vennligst skriv inn passord for «{user}»." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Om" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Forfattere" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Oversettelser" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Lisens" #: qt/app.py:169 msgid "Shortcuts" msgstr "Snarveier" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Mappen finnes ikke\n" "i det valgte øyeblikksbildet." #: qt/app.py:256 msgid "Add to Include" msgstr "Legg til for å inkludere" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Legg til for å ekskludere" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "{app_name} virker å kjøre for første gang, da intet oppsett finnes." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Kan ikke opprette mappe." #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Kan ikke finne øyeblikksbilde-mappe.\n" "Hvis den er på en ekstern disk må du koble denne til, og deretter trykke OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Ta et øyeblikksbilde" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Bruk endringstid og størrelse for å spore fil-endringer." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Ta et øyeblikksbilde (sjekksum-modus)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Bruk sjekksummer for å spore filendringer." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Sett øyeblikksbilde-prosessen på pause" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Fortsett øyeblikksbilde-prosessen" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Stopp øyeblikksbilde-prosessen" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Oppdaterer listen over øyeblikksbilder" #: qt/app.py:508 msgid "Name snapshot" msgstr "Gi navn til øyeblikksbilde" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Fjern øyeblikksbilde" #: qt/app.py:516 msgid "View snapshot log" msgstr "Vis logg for øyeblikksbilde" #: qt/app.py:520 msgid "View last log" msgstr "Vis siste logg" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Administrer profiler…" #: qt/app.py:528 msgid "Shutdown" msgstr "Skru av" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Skrur av systemet etter at øyeblikksbildet er ferdig." #: qt/app.py:532 msgid "Setup language…" msgstr "Velg språk…" #: qt/app.py:536 msgid "Exit" msgstr "Avslutt" #: qt/app.py:540 msgid "Help" msgstr "Hjelp" #: qt/app.py:544 msgid "Profiles config file" msgstr "Profilens konfigurasjonsfil" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Nettside" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Endringslogg" #: qt/app.py:553 msgid "FAQ" msgstr "Ofte stilte spørsmål" #: qt/app.py:556 msgid "Ask a question" msgstr "Still et spørsmål" #: qt/app.py:559 msgid "Report a bug" msgstr "Rapporter en feil" #: qt/app.py:562 msgid "Translation" msgstr "Oversettelse" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Gjennopprett" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Gjenopprett valgte filer og mapper til opprinnelig sted." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Gjennopprett til …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Gjenopprett valgte filer og mapper til et nytt sted." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Gjenopprett de viste mappene og alt deres innhold til det opprinnelige " "stedet." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Gjenopprett de viste mappene og alt deres innhold til et nytt sted." #: qt/app.py:595 msgid "Up" msgstr "Opp" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Vis skjulte filer" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Sammenlign øyeblikksbilder…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "&Sikkerhetskopi" #: qt/app.py:676 msgid "&Restore" msgstr "&Gjennopprett" #: qt/app.py:682 msgid "&Help" msgstr "&Hjelp" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Hvis du lukker dette vinduet vil ikke Back In Time være i stand til å skru av systemet når øyeblikksbildet er ferdig.\n" "Er du sikker på at du vil lukke?" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Er du sikker på at du vil gjenopprette dette elementet?" #: qt/app.py:987 msgid "Working:" msgstr "Arbeider:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Ferdig, sikkerhetskopiering trengs ikke" #: qt/app.py:1044 msgid "Working" msgstr "Arbeider" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Feil" #: qt/app.py:1076 msgid "Sent" msgstr "Sendt" #: qt/app.py:1077 msgid "Speed" msgstr "Hastighet" #: qt/app.py:1078 msgid "ETA" msgstr "Forventet ferdig" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Rot" #: qt/app.py:1142 msgid "Home" msgstr "Hjem" #: qt/app.py:1170 msgid "Backup folders" msgstr "Øyeblikksbilde mapper" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Navn på øyeblikksbilde" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Er du sikker på at du vil fjerne dette øyeblikksbildet?" msgstr[1] "Er du sikker på at du vil fjerne disse øyeblikksbildene?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Lag en sikkerhetskopi med endelse {suffix}\n" "før lokale elementer blir fjernet eller overskrevet." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Nyere versjoner av filer vil få nytt navn med endelsen {suffix} før de blir gjenopprettet.\n" "Hvis du ikke trenger dem lengre kan du fjerne dem med {cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Gjenopprett kun elementer som enten ikke eksisterer\n" "eller er nyere enn de eksisterende elementene.\n" "Bruker \"rsync --update\"-opsjonen." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Fjern elementer som er nyere i den originale mappen." #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Gjenopprett valgte filer og mapper til det originale stedet og\n" "slett filer og mapper som ikke er inkuldert i øyeblikksbildet.\n" "Vær veldig forsiktig, fordi dette vil\n" "slette alle filene og mappene som var\n" "ekskludert da øyeblikksbildet ble tatt." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Er du sikker på at du vil gjenopprette dette elementet til den nye mappen\n" "{path}?" msgstr[1] "" "Er du sikker på at du vil gjenopprette disse elementene til den nye mappen\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Er du sikker på at du vil gjenopprette dette elementet?" msgstr[1] "Er du sikker på at du vil gjenopprette disse elementene?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Er du sikker på at du vil fjerne alle nyere filer i {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Er du sikker på at du vil fjerne alle nyere filer i den originale mappen?" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "ADVARSEL: Å slette filer i root-filsystemet kan ødelegge hele systemet ditt!" #: qt/app.py:1750 msgid "Snapshot" msgstr "Øyeblikksbilde" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Gjenopprett {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Gjennopprett {path} til …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Språkinnstillingene trer først i kraft etter en omstart av Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Innstalleringsspråk" #: qt/languagedialog.py:92 msgid "System default" msgstr "Standardverdi for systemet" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Bruk samme språk som operativsystemet." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Oversatt: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Hei!\n" "Du har brukt Back In Time med {language} språkdrakt noen ganger allerede.\n" "Oversettelsen til {language} for din installerte versjon av Back In Time er {perc} ferdig. Uavhengig av din tekniske kompetanse kan du bidra med oversettelse og dermed også til Back In Time.\n" "Det er fint om du besøker {translation_platform_url} hvis du har lyst til å bidra. For mer hjelp og spørsmål, besøk {back_in_time_project_website}.\n" "Vi beklager forstyrrelsen, og denne meldingen vil ikke dukke opp igjen. Denne dialogen er tilgjengelig når som helst via hjelpemenyen.\n" "Hilsen Back In Time-laget ditt" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "oversettelses-plattform" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Din oversettelse" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Se seneste logg" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Se logg for øyeblikksbilde" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Øyeblikksbilder:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Alt" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Endringer" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Feil" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Informasjon" msgstr[1] "Informasjon" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Feil, [I] Informasjon, [C] Endring" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "dokode stier" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Spørsmål" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Vis siste logg" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Start {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Arbeider…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Sendt:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Hastighet:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Øyeblikksbilder" #: qt/qttools.py:427 msgid "Today" msgstr "Idag" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Igår" #: qt/qttools.py:443 msgid "This week" msgstr "Denne uken" #: qt/qttools.py:450 msgid "Last week" msgstr "Forrige uke" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Dette er IKKE et øyeblikksbilde, men en sanntidsvisning av dine lokale filer" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Sist sjekket {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Vis hele loggen" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Tjener:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Bruker:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Administrer profiler" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Rediger" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Legg til" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Fjern" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Generelt" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modus:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Hvor lagre øyeblikksbilder" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Mappe" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH-innstillinger" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Sti:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Chiffer:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Privat SSH-nøkkel:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Velg en eksisterende privat nøkkel (normalt kalt \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Lag en ny SSH-nøkkel uten passord (ikke tillatt hvis en privat nøkkel " "allerede er valgt)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Passord" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Lagre passord til nøkkelringen" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Legg passordet i hurtigminne for cron (sikkerhetsutfordring: root-bruker kan" " lese passordet)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Avansert" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Fullstendig øyeblikksbilde-bane:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Timeplan" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Deaktivert" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "For hver start/omstart" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Hvert minutt" msgstr[1] "Hvert {n}. minutt" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Hver time" msgstr[1] "Hver time" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Hver time" msgstr[1] "Hver {n}. time" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Velg timer" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Hver dag" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Gjentakende (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Når lagringsenheten kobles til (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Hver uke" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Hver måned" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Hvert år" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Ukedag:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Time:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Timer:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Kjør Back In Time gjentakende. Dette er nyttig hvis datamaskinen ikke kjører" " hele tiden." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Hver:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Time(r)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dag(er)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Uke(r)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Måned(er)" #: qt/settingsdialog.py:555 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 "" "Kjør Back In Time så snart lagringsenheten koble til (kun en gang hver X dager)).\n" "Du vil bli spurt om ditt sudo-passord." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Inkluder" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Inkluder filer og mapper" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Legg til fil" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Legg til katalog" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Ekskludér" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Ekskluderingsmønstre, filer og mapper" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Legg til standardmønstre" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Ekskluder filer som er større enn:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Ekskluder filer som er større enn: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Ekskluder filer som er større enn verdien i %(prefix)s.\n" "Med 'Full rsync mode' avslått vil dette bare gjelde nye filer\n" "siden dette for rsync er en overførings-opsjon, ikke en ekskluderings-opsjon.\n" "Det betyr at store filer som har blitt sikkerhetskopiert forblir i øyeblikksbildet\n" "selv om de har endret seg." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Automatisk &fjerning" #: qt/settingsdialog.py:726 #, fuzzy msgid "Older than:" msgstr "Eldre enn" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "År" #: qt/settingsdialog.py:748 #, fuzzy msgid "If free space is less than:" msgstr "Hvis det er mindre ledig plass enn" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Hvis ledige inoder er færre enn:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Smart sletting:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Kjør i bakgrunnen på den eksterne verten." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTELT" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Behold alle øyeblikksbilder tatt de siste" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dag(ene)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Behold ett øyeblikksbilde pr dag for de siste" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Behold ett øyeblikksbilde pr uke for de siste" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "uke(ene)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Behold ett øyeblikksbilde pr måned for de siste" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "måned(ene)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Behold ett øyeblikksbilde pr år for alle år." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ikke fjern navngitte øyeblikksbilder." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Alternativer" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Slå på meldinger" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Slå av funksjonen snapshot ved bateridrift" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Batteristatus er ikke tilgjengelig fra systemet" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Ta kun ett øyeblikksbilde om gangen" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Andre øyeblikksbilder vil bli blokkert inntil det nåværende er ferdig.\n" "Dette er en global opsjon, så det vil gjelde for alle profiler til denne brukeren.\n" "Opsjonen må fortsatt aktiveres for andre brukere." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Ta sikkerhetskopi av filer som blir overskrevet av gjenoppretting" #: qt/settingsdialog.py:879 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Nyere versjoner av filer vil få nytt navn med endelsen {suffix} før de blir gjenopprettet.\n" "Hvis du ikke trenger dem lengre kan du fjerne dem med {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" "Fortsett når feil oppstår (behold øyeblikksbilder som ikke er komplette)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Bruk sjekksum for å oppdage endringer" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Ta et nytt øyeblikksbilde enten det har vært endringer eller ikke." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Logg-nivå:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Ingen" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "E&kspert-innstillinger" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Advarsel: ikke endre disse innstillingene med mindre du vet hva du gjør." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Kjør 'rsync' med '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "som cron-jobb" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "på ekstern vert" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "når en lager et øyeblikksbilde manuelt" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Vennligst installer 'nocache' for å slå på denne opsjonen)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "på lokal maskin" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Rediriger stdout til /dev/null i cron-jobber." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Rediriger stderr til /dev/null i cron-jobber." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Begrens båndbredden for rsync til:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/sek" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Behold ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Behold utvidede attributter (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopier usikre lenker (virker kun på absolutte lenker)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opsjoner må settes i gåseøyne, for eksempel {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Lim inn tilleggsopsjoner til rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Legg til et prefiks for SSH-kommandoer" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Prefiks å kjøre før hver kommando på ekstern vert.\n" "Variabler må starte med en \"rømningskarakter\", en bakoverstrek, for eksempel \\$FOO.\n" "Dette berører ikke rsync. For å legge til en et prefiks for\n" "rsync bruker du \"%(cbRsyncOptions)s\" med\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "standard" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Sjekk om ekstern vert er oppe" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Advarsel: Hvis denne opsjonen er avslått samtidig som ekstern vert\n" "ikke er tilgjengelig kan det oppstå en del\n" "merkelige feil." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Sjekk om ekstern vert har støtte for alle nødvendige kommandoer." #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Advarsel: hvis opsjonen er avslått og den eksterne verten\n" "ikke støtter alle de nødvendige kommandoene\n" "kan det oppstå en del merkelige feil." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Gjenopprett konfigurasjon" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Endre user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Ny profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Omdøp profil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Er du sikker på at du vil slette profilen \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Svært anbefalt" #: qt/settingsdialog.py:1634 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 "" "Egendefinerte timer kan kun være en kommaseparert liste med timer (for " "eksempel 2,4,6,18,22) eller */3 for periodisk sikkerhetskopiering hver 3. " "time." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Du valgte ikke en privat SSH-nøkkel.\n" "Ønsker du å lage et nytt passord-løst nøkkelpar (offentlig/privat nøkkelpar)?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Privatnøkkel-fil \"{file}\" finnes ikke." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Ønsker du å kopiere din offentlige SSH-nøkkel til den\n" "eksterne verten slik at du kan bruke passordløs innlogging?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "Ektheten til den eksterne verten {host} kan ikke slås fast.\n" "\n" "{keytype}-nøkkelen sitt fingeravtrykk er:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Vennligst verifiser dette fingeravtrykket! Ønsker du å legge det til i din " "'known_hosts'-fil?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Utelukk mønster" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Ekskluder fil" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Ekskludér katalog" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Inkluder fil" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" er en symbolsk lenke. Det vil ikke bli tatt sikkerhetskopi av målet for lenken inntil du inkluderer dette også.\n" "Ønsker du å inkludere målet for den symbolske lenken istedenfor?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Inkludér katalog" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Er du sikker på at vil bytte mappe for øyeblikksbilder?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Fikk ikke til å lage nye SSH-nøkler i {path}." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "standard" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "avslått" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "påslått" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Fant ingen konfigurasjon" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opsjoner for å sammenligne øyeblikksbilder" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Kommando:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametre:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Bruk %1 and %2 for path parameters" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Kun øyeblikksbilder med forskjeller" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Liste med kun like øyeblikksbilder til:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Dypsjekk (mer nøyktig, men tregt)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Slett" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Velg alle" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Sammenlign" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Gå Til" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Alternativer" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Du kan ikke sammenlikne et øyeblikksbilde med seg selv." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Ønsker du virkelig å slette {file} i øyeblikksbildet {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Ønsker du virkelig å slette {file} i {count} øyeblikksbilder?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "ADVARSEL: Dette kan ikke gjøres om." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Utelukk {path} fra fremtidige øyeblikksbilder?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Klarte ikke finne crontab. Er du sikker på at cron er installert? Hvis ikke " #~ "bør du skru av alle automatiske sikkerhetskopier." #~ msgid "Full snapshot path" #~ msgstr "Fullstendig sti til øyeblikksbilde" #~ msgid "Mode" #~ msgstr "Modus" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil '{profile}' Skriv passord for {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang i user-callback-skriptet er ikke eksekverbart." #~ msgid "WARNING" #~ msgstr "ADVARSEL" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs versjon 1.7.2 og eldre har en feil med valget --reverse. Vennligst " #~ "oppdater encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "user-callback-skriptet har ingen shebang (#!/bin/sh)-linje." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} bruker EncFS for kryptering. En nylig sikkerhetsrevisjon avdekket " #~ "flere mulige angrepsvektorer mot EncFS. Vennligst ta en titt på \"A NOTE ON " #~ "SECURITY\" i \"man backintime\"." backintime-1.5.2/common/po/nl.po000066400000000000000000001571361465446530500165350ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-15 05:11+0000\n" "Last-Translator: rsschaap \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Let op" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Hoofdprofiel" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Lokaal (EncFS-versleuteld)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (EncFS-versleuteld)" #: common/config.py:309 msgid "Local" msgstr "Lokaal" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH private sleutel" #: common/config.py:314 msgid "Local encrypted" msgstr "Lokaal versleuteld" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Versleuteling" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH-versleuteld" #: common/config.py:327 msgid "Default" msgstr "Standaard" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profiel: “{name}”" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Momentopname-folder is ongeldig!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Selecteer minstens één map waarvan een back-up gemaakt moet worden!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Backup-map kan niet worden opgenomen." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Back-up-submap kan niet worden opgenomen." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ongeldige optie. {path} is geen map." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/Gebruiker/Profiel-ID mag niet leeg zijn." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Kan niet schrijven naar: {path}\n" "Beschikt u over schrijfrechten?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Het doelbestandssysteem van ‘{path}’ is geformatteerd als FAT, dat geen " "harde koppeling, hard-links, ondersteunt. Gebruik in plaats daarvan een " "authentiek Linux-bestandssysteem." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Het doelbestandssysteem van ‘{path}’ is een met SMB aangekoppelde " "netwerkopslag. Zorg ervoor dat de externe SMB-server symbolische koppelingen" " ondersteunt of activeer ‘{copyLinks}’ in ‘{expertOptions}’." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Koppelingen kopiëren (symbolische koppelingen derefereren)" #: common/config.py:497 msgid "Expert Options" msgstr "Geavanceerde opties" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Doelbestandssysteem voor '{path}' is een met sshfs aangekoppelde " "netwerkopslag. sshfs ondersteunt geen harde koppelingen. Gebruik in plaats " "daarvan de modus 'SSH'." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Kon geen nieuwe crontab schrijven." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron is niet actief, hoewel het commando crontab beschikbaar is. Geplande " "back-uptaken 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/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Kon udev-regel voor profiel {profile_id} niet installeren. DBus-dienst " "'{dbus_interface}' was niet beschikbaar" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Methode udev werkt niet bij modus {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "" "Kon UUID (Universeel Unieke IDentificatiecode) voor \"{path}\" niet vinden" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Kon configuratie niet opslaan" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Kon configuratie niet laden" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profiel \"{name}\" bestaat al." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Het laatste profiel kan niet worden verwijderd." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Kan '{command}' niet aankoppelen" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Configuratie voor versleutelde map niet gevonden." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Een nieuwe versleutelde map aanmaken?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Annuleren" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Bevestig het wachtwoord." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Wachtwoord komt niet overeen." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Momentopname maken" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan {mountprocess} niet ontkoppelen van {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} niet gevonden. Installeer het a.u.b. (bijv. via " "\"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Koppelpunt {mntpoint} niet leeg." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Voer het wachtwoord in voor {mode} profiel \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "MISLUKT" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Rechten herstellen" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Voltooid" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Back-up uitstellen indien op batterij" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Kan momentopnamemap niet vinden.\n" "Als deze op een extern medium staat, gelieve dit aan te sluiten." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Wacht %s seconde." msgstr[1] "Wacht %s seconden." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Kon momentopname {snapshot_id} niet maken." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Voltooien" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Kan map niet aanmaken" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Configuratiebestand opslaan…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Rechten opslaan…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Restant van '{snapshot_id}' gevonden waarmee voortgegaan kan worden." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Resterende map '{snapshot_id}' van de laatste keer verwijderen" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Kan map niet verwijderen" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Momentopname wordt gemaakt" #: common/snapshots.py:1417 msgid "Success" msgstr "Succes" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Gedeeltelijke overdracht vanwege een fout" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Gedeeltelijke overdracht vanwege verdwenen bronbestanden (zie 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' eindigde met exitcode {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Zie 'man rsync' voor meer informatie" #: common/snapshots.py:1445 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:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Er is niets veranderd, geen nieuwe momentopname nodig" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Kan {new_path} niet hernoemen naar {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Slim verwijderen" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Oude momentopnames verwijderen" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Een minimum aan vrije ruimte proberen te behouden" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Een minimum van {perc} vrije inodes proberen te behouden" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Nu" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan {sshfs} niet aankoppelen" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent niet gevonden. Zorg ervoor dat het is geïnstalleerd." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Kon SSH-privésleutel niet ontgrendelen. Het wachtwoord is ongeldig of is " "niet beschikbaar voor cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Versleutelingsmethode {cipher} mislukt voor {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Extern pad bestaat maar is geen map." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Extern pad is niet beschrijfbaar." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Extern pad is niet uitvoerbaar." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Kon extern pad niet aanmaken." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Externe host {host} ondersteunt {command} niet" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Raadpleeg 'man backintime' voor verdere instructies" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Controlecommando's op host {host} gaven onbekende foutmelding" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Externe host {host} ondersteunt geen harde koppelingen" #: common/sshtools.py:1164 #, 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:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Voer het wachtwoord in voor \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Info" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Auteurs" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Vertalingen" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licentie" #: qt/app.py:169 msgid "Shortcuts" msgstr "Sneltoetsen" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Deze map bestaat niet\n" "in de momenteel geselecteerde momentopname." #: qt/app.py:256 msgid "Add to Include" msgstr "Toevoegen aan Opnemen" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Toevoegen aan Uitsluiten" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "Het lijkt erop dat {app_name} voor de eerste keer wordt uitgevoerd, omdat er" " geen configuratie is gevonden." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Een bestaande configuratie importeren (vanuit een back-updoelmap of een " "andere computer)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Kan momentopnamemap niet vinden." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Als deze op een verwijderbaar medium staat, gelieve dit aan te sluiten en op" " OK te drukken." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Een momentopname maken" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Modificatietijd en grootte gebruiken voor detectie van bestandswijzigingen." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Momentopname maken (modus controlegetal)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Controlegetallen gebruiken voor het opsporen van bestandswijzigingen." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Maken van momentopname pauzeren" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Maken van momentopname hervatten" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Maken van momentopname stoppen" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Momentopnamelijst vernieuwen" #: qt/app.py:508 msgid "Name snapshot" msgstr "Momentopname benoemen" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Momentopname verwijderen" #: qt/app.py:516 msgid "View snapshot log" msgstr "Momentopnamelogboek bekijken" #: qt/app.py:520 msgid "View last log" msgstr "Laatste logboek bekijken" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Profielen beheren…" #: qt/app.py:528 msgid "Shutdown" msgstr "Uitschakelen" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Computer uitschakelen wanneer de momentopname voltooid is." #: qt/app.py:532 msgid "Setup language…" msgstr "Taal instellen…" #: qt/app.py:536 msgid "Exit" msgstr "Afsluiten" #: qt/app.py:540 msgid "Help" msgstr "Hulp" #: qt/app.py:544 msgid "Profiles config file" msgstr "Profielconfiguratiebestand" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Website" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Wijzigingslogboek" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ" #: qt/app.py:556 msgid "Ask a question" msgstr "Een vraag stellen" #: qt/app.py:559 msgid "Report a bug" msgstr "Een fout melden" #: qt/app.py:562 msgid "Translation" msgstr "Vertaling" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Toont het bericht over deelname aan vertaling opnieuw." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Versleutelingsovergang (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Toont opnieuw het bericht over het verwijderen van EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Herstellen" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "De geselecteerde bestanden of mappen naar de oorspronkelijke bestemming " "herstellen." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Herstellen naar …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" "De geselecteerde bestanden of mappen naar een nieuw bestemming herstellen." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "De momenteel weergegeven map en alle inhoud ervan naar de oorspronkelijke " "bestemming herstellen." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "De momenteel weergegeven map en alle inhoud ervan naar een nieuwe bestemming" " herstellen." #: qt/app.py:595 msgid "Up" msgstr "Omhoog" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Verborgen bestanden tonen" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Momentopnames vergelijken…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Back-up" #: qt/app.py:676 msgid "&Restore" msgstr "Te&rugzetten" #: qt/app.py:682 msgid "&Help" msgstr "&Hulp" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Als u dit venster sluit, zal Back In Time de computer niet uit kunnen " "schakelen wanneer de momentopname voltooid is." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Weet u zeker dat u het wilt sluiten?" #: qt/app.py:987 msgid "Working:" msgstr "Bezig:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Klaar, geen back-up nodig" #: qt/app.py:1044 msgid "Working" msgstr "Bezig" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Fout" #: qt/app.py:1076 msgid "Sent" msgstr "Verzonden" #: qt/app.py:1077 msgid "Speed" msgstr "Snelheid" #: qt/app.py:1078 msgid "ETA" msgstr "Geschatte afhandelingstijd" #: qt/app.py:1140 msgid "Global" msgstr "Algemeen" #: qt/app.py:1141 msgid "Root" msgstr "Hoofdmap" #: qt/app.py:1142 msgid "Home" msgstr "Persoonlijke map" #: qt/app.py:1170 msgid "Backup folders" msgstr "Back-upmappen" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Naam momentopname" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Weet u zeker dat u deze momentopname wilt verwijderen?" msgstr[1] "Weet u zeker dat u deze momentopnames wilt verwijderen?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Back-ups met achtervoegsel {suffix} maken, voordat\n" "lokale elementen overschreven of verwijderd worden." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "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:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Alleen elementen terugzetten die niet bestaan of\n" "nieuwer zijn dan die in de bestemming.\n" "De optie \"rsync --update\" wordt gebruikt." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Nieuwere elementen in oorspronkelijke map verwijderen." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Geselecteerde bestanden of mappen naar de oorspronkelijke bestemming " "herstellen en bestanden/mappen die niet in de momentopname voorkomen, " "verwijderen. Wees hier uiterst voorzichtig mee. Hierdoor worden " "bestanden/mappen verwijderd die waren uitgesloten tijdens het maken van de " "momentopname." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Weet u zeker dat u dit element wilt herstellen naar de nieuwe map\n" "{path}?" msgstr[1] "" "Weet u zeker dat u deze elementen wilt herstellen naar de nieuwe map\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to 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/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "Weet u zeker dat u alle nieuwere bestanden in {path} wilt verwijderen?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Weet u zeker dat u alle nieuwere bestanden in uw oorspronkelijke map wilt " "verwijderen?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your 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:1750 msgid "Snapshot" msgstr "Momentopname" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "{path} terugzetten" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "{path} terugzetten naar …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "De taalinstellingen worden pas van kracht na het herstarten van Back In " "Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Ondersteuning voor EncFS zal in de nabije toekomst worden stopgezet. Het " "wordt niet aanbevolen om die modus nog verder te gebruiken voor een profiel." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Een beslissing over een vervanging voor blijvende ondersteuning van " "versleutelde back-ups is nog in behandeling, afhankelijk van de " "projectmiddelen en de beschikbaarheid van medewerkers. Meer details zijn " "beschikbaar in dit {whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "witboek" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "De ondersteuning voor versleutelde momentopnameprofielen ondergaat " "belangrijke veranderingen en EncFS zal in de nabije toekomst verwijderd " "worden." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "De/het volgende profiel(en) gebruik(t)(en) versleuteling met EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Een beslissing over een vervanging voor blijvende ondersteuning van " "versleutelde back-ups is nog in behandeling, afhankelijk van de " "projectmiddelen en de beschikbaarheid van medewerkers. Gebruikers worden " "uitgenodigd om deel te nemen aan deze discussie. Bijgewerkte details over de" " volgende stappen zijn beschikbaar in dit {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Dit bericht zal niet meer worden weergegeven. Dit dialoogvenster is op elk " "moment beschikbaar via het hulpmenu." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Uw Back In Time-team" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Taal instellen" #: qt/languagedialog.py:92 msgid "System default" msgstr "Systeemstandaard" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "De taal van het besturingssysteem gebruiken." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Vertaald: {percent}" #: qt/languagedialog.py:188 #, 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 "" "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/languagedialog.py:217 msgid "translation platform" msgstr "vertaalplatform" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Uw vertaling" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Laatste logboekweergave" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Weergave momentopnamelogboek" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profiel:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Momentopnames:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Alles" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Wijzigingen" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Fouten" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informatie" msgstr[1] "Informatie" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "fouten in de rsync-overdracht (experimenteel)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Fout, [I] Informatie, [C] Wijziging" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "paden decoderen" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Vraag" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profiel: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Laatste logboek bekijken" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "{appname} starten" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Bezig…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Verzonden:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Snelheid:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "Geschatte afhandelingstijd:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Momentopnames" #: qt/qttools.py:427 msgid "Today" msgstr "Vandaag" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Gisteren" #: qt/qttools.py:443 msgid "This week" msgstr "Deze week" #: qt/qttools.py:450 msgid "Last week" msgstr "Vorige week" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Dit is GEEN momentopname maar een actuele weergave van uw lokale bestanden" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Laatste controle {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Volledig logboek tonen" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH-Proxy" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Host:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Poort:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Gebruiker:" #: qt/settingsdialog.py:120 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 "" "Via deze proxy verbinding maken met de doelhost (ook wel jumphost genoemd). " "Zie \"-J\" in de documentatie bij het \"ssh\"-commando of \"ProxyJump\" in " "de manpagina van \"ssh_config\" voor details." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Profielen beheren" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Bewerken" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Toevoegen" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Verwijderen" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Algemeen" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modus:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Waar wilt u de momentopnames opslaan" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Map" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH-instellingen" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Pad:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Versleutelingsmethode:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Privésleutel:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Kies een bestaand privésleutelbestand (normaal met de naam \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Een nieuwe SSH-sleutel zonder wachtwoord aanmaken (niet toegestaan als er al" " een privésleutelbestand is geselecteerd)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Wachtwoord" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Wachtwoord toevoegen aan sleutelbos" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Wachtwoord in cache opslaan voor Cron (Beveiligingsprobleem: beheerder kan " "wachtwoord lezen)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Geavanceerd" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Volledig momentopnamepad:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Planning" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Uitgeschakeld" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Bij elke opstart/herstart" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Elke minuut" msgstr[1] "Elke {n} minuten" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Elk uur" msgstr[1] "Elke {n} uur" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Elk uur" msgstr[1] "Elke {n} uur" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Aangepaste tijdstippen" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Dagelijks" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Herhaaldelijk (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Zodra de schijf is aangesloten (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Wekelijks" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Maandelijks" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Jaarlijks" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Dag:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Dag van de week:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Uur:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Uren:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Back In Time herhaaldelijk uitvoeren. Dit is handig wanneer de computer niet" " regelmatig wordt gebruikt." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Elk(e):" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Uur/Uren" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dag(en)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Week/Weken" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Maand(en)" #: qt/settingsdialog.py:555 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 "" "Back In Time uitvoeren zodra de schijf is aangesloten (slechts één keer per X dagen).\n" "Er zal om uw beheerderswachtwoord gevraagd worden." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Het loggen van foutopsporingsberichten inschakelen" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" "Schrijft berichten van debug-gehalte naar het systeemlogboek via \"--" "debug\"." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Let op: gebruik dit alleen tijdelijk voor diagnostische doeleinden, omdat " "het een grote hoeveelheid uitvoer genereert." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Opnemen" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Bestanden en mappen opnemen" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Bestand toevoegen" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Map toevoegen" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Uitsluiten" #: qt/settingsdialog.py:625 #, 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}: in de modus 'SSH-versleuteld' zijn alleen enkele of " "dubbele sterretjes functioneel (bijvoorbeeld {example2}). Andere soorten " "jokertekens en patronen worden genegeerd (bijvoorbeeld {example1}). " "Bestandsnamen zijn in deze modus onvoorspelbaar vanwege de versleuteling " "door EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Patronen, bestanden of mappen uitsluiten" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Standaard toevoegen" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Bestanden uitsluiten die groter zijn dan:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Bestanden uitsluiten die groter zijn dan de waarde in {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Als 'Volledige rsync-modus' is uitgeschakeld, heeft dit alleen invloed op " "nieuwe bestanden, omdat dit voor rsync een overdrachtsoptie is en geen " "uitsluitingsoptie. Daarom zullen grote bestanden waarvan eerder een back-up " "is gemaakt, blijven bestaan in momentopnames, zelfs als ze zijn gewijzigd." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Automatisch &verwijderen" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Ouder dan:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Jaar" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Als de vrije ruimte kleiner is dan:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Als er minder inodes vrij zijn dan:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Slim verwijderen:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Op de achtergrond uitvoeren op externe host." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTEEL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Alle momentopnames bewaren van de laatste" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dag(en)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Eén momentopname per dag bewaren van de laatste" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Eén momentopname per week bewaren van de laatste" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "week/weken." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Eén momentopname per maand bewaren van de laatste" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "maand(en)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Eén momentopname per jaar bewaren van alle jaren." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Genoemde momentopnames niet verwijderen." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "O&pties" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Meldingen activeren" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Momentopnames uitschakelen bij batterijgebruik" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Voedingsstatus niet beschikbaar via systeem" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Slechts één momentopname tegelijk uitvoeren" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Andere momentopnames worden geblokkeerd totdat de huidige momentopname is " "voltooid. Dit is een globale optie. Ze heeft dus invloed op alle profielen " "van deze gebruiker. Maar u moet dit ook voor alle andere gebruikers " "activeren." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Back-up maken van vervangen bestanden bij terugzetten" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "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 {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Doorgaan bij fouten (onvolledige momentopnames bewaren)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Controlegetal gebruiken om wijzigingen te detecteren" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Een nieuwe momentopname maken, of er nu wijzigingen waren of niet." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Logboekniveau:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Geen" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "&Geavanceerde opties" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Let op: wijzig deze opties alleen als u echt weet wat u doet." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "'rsync' uitvoeren met '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "als crontaak" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "op externe host" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "bij het maken van een handmatige momentopname" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Installeer 'nocache' om deze optie in te schakelen)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "op de lokale machine" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "stdout omleiden naar /dev/null in crontaken." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Cron zal automatisch een e-mail sturen met in bijlage de uitvoer van " "crontaken als er een MTA is geïnstalleerd." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "stderr omleiden naar /dev/null in crontaken." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Cron zal automatisch een e-mail sturen met in bijlage de foutmeldingen van " "crontaken als er een MTA is geïnstalleerd." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Het bandbreedtegebruik van rsync beperken:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "ACL behouden" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Uitgebreide attributen (xattr) behouden" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Onveilige koppelingen kopiëren (werkt alleen met absolute koppelingen)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Beperken tot één bestandssysteem" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opties moeten tussen aanhalingstekens staan, bijvoorbeeld {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Extra opties aan rsync toevoegen" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Voorvoegsel aan SSH-commando's toevoegen" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" "Voorvoegsel dat vóór elk commando op de externe host moet worden uitgevoerd." #: qt/settingsdialog.py:1188 #, 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 "" "Variabelen moeten worden gemaskeerd met \\$FOO. Dit heeft geen betrekking op" " rsync. Dus om een voorvoegsel voor rsync toe te voegen gebruikt u " "\"{example_value}\" met {rsync_options_value}." #: qt/settingsdialog.py:1196 msgid "default" msgstr "standaard" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Controleren of de externe host online is" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Let op: als dit is uitgeschakeld en de externe host niet beschikbaar is, kan" " dit leiden tot rare fouten." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Controleren of de externe host alle benodigde commando's ondersteunt." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Let op: als dit uitgeschakeld is en de externe host ondersteunt niet alle " "benodigde commando's, kan dit leiden tot rare fouten." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Configuratie herstellen" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "user-callback bewerken" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "Ondersteuning voor EncFS wordt in de nabije toekomst stopgezet. Een " "beslissing over een vervanger voor het blijven ondersteunen van versleutelde" " back-ups is nog in behandeling, afhankelijk van de middelen van het project" " en de beschikbaarheid van medewerkers. Meer details zijn beschikbaar in dit" " {whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nieuw profiel" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Profiel hernoemen" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Weet u zeker dat u het profiel \"{name}\" wilt verwijderen ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "{BOLD}Aanbevolen{ENDBOLD}: (Alle aanbevelingen zijn al inbegrepen.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Sterk aanbevolen{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "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:1681 msgid "You did not choose a private key file for SSH." msgstr "U heeft geen privésleutelbestand voor SSH gekozen." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Wilt u een nieuw publiek/privé sleutelpaar genereren zonder wachtwoord?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Privésleutelbestand \"{file}\" bestaat niet." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Wilt u uw openbare SSH-sleutel naar de externe host kopiëren om inloggen " "zonder wachtwoord mogelijk te maken?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "De authenticiteit van host {host} kan niet worden vastgesteld." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} sleutelvingerafdruk is:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Controleer deze vingerafdruk. Wilt u deze toevoegen aan uw bestand " "'known_hosts'?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Patroon uitsluiten" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Bestand uitsluiten" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Map uitsluiten" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Bestand opnemen" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" is een symbolische koppeling. Van het gekoppelde doel wordt geen back-up gemaakt totdat u het ook opneemt.\n" "Wilt u eerder het doel van de symbolische koppeling opnemen?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Map opnemen" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Weet u zeker dat u de momentopnamemap wilt wijzigen?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Aanmaken nieuwe SSH-sleutel in {path} mislukt." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" "Geblokkeerd omdat dit patroon niet gebruikt kan worden bij 'SSH " "versleuteld'." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(standaard: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "uitgeschakeld" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "ingeschakeld" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Configuratie importeren" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Geen configuratie gevonden" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importeren" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Selecteer de momentopnamemap waaruit het configuratiebestand moet worden " "geïmporteerd. Het pad kan er als volgt uitzien: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Als de map zich op een externe host of externe schijf bevindt, moet deze " "vooraf handmatig worden aangekoppeld." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opties voor het vergelijken van momentopnames" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Commando:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parameters:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "%1 en %2 voor padparameters gebruiken" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Stel een diff-commando in of druk op Annuleren." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "Het commando \"{cmd}\" kan niet worden gevonden op dit systeem. Probeer iets" " anders of druk op Annuleren." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Er zijn geen parameters ingesteld voor het diff-commando. Standaardwaarde " "\"{params}\" wordt gebruikt." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Alleen momentopnames die verschillen" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Alleen momentopnames tonen die gelijk zijn aan:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Diepgaande controle (nauwkeuriger, maar langzaam)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Wissen" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Alles selecteren" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Vergelijken" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Gaan naar" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opties" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "U kunt een momentopname niet met zichzelf vergelijken." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Wilt u {file} in momentopname {snapshot_id} echt verwijderen?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Wilt u {file} in {count} momentopnames echt verwijderen?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "LET OP: dit kan niet worden teruggedraaid." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "{path} uitsluiten van toekomstige momentopnames?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? 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." #~ msgid "Full snapshot path" #~ msgstr "Volledig momentopnamepad" #~ msgid "Mode" #~ msgstr "Modus" #~ msgid "Profile" #~ msgstr "Profiel" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profiel '{profile}': Voer wachtwoord in voor {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang in user-callback-script is niet uitvoerbaar." #~ msgid "WARNING" #~ msgstr "WAARSCHUWING" #~ 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 versie a.u.b. bij." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "user-callback-script heeft geen shebang-regel (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} gebruikt EncFS voor versleuteling. Een recente veiligheidscontrole " #~ "bracht in dit verband verschillende mogelijke aanvalsmethodes aan het licht." #~ " Bekijk eens \"A NOTE ON SECURITY\" in \"man backintime\"." backintime-1.5.2/common/po/nn.po000066400000000000000000001450771465446530500165400ustar00rootroot00000000000000# Norwegian Nynorsk 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \n" "Language-Team: Norwegian Nynorsk \n" "Language: nn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Åtvaring" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Hovudprofil" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "kryptert" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH-kryptert" #: common/config.py:309 msgid "Local" msgstr "Lokal" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH privatlykjel" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "kryptert" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Kryptering" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH-kryptert" #: common/config.py:327 msgid "Default" msgstr "Standardverdi" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Mappa for augneblinksbileta er ugyldig!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Du må velja minst éi mappe for sikkerheitskopiering!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Du kan ikkje inkludere sikkerheitskopierings-mappa." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Du kan ikkje inkludere undermapper av sikkerheitskopierings-mappa." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ugyldig val. {path} er ikkje ei mappe." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Tenar/Brukar/Profil-ID kan ikkje vere tom." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Kan ikkje skrive til: {path}\n" "Er du sikker på at du har skrivetilgang?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Målfilsystem for {path} er formatert med FAT som ikkje støttar harde " "lenkjer. Bruk heller eit filsystem tilpassa Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Målfilsystemet for {path} er eit delt nettverksområde av typen SMB. Sjå til " "at målfilsystemet støttar symbolske lenkjer (symlinks), eller aktiver " "{copyLinks} i {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopier symbolske lenkjer som filer (og fjern lenkjereferansen)" #: common/config.py:497 msgid "Expert Options" msgstr "Avanserte innstillingar" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Målfilsystemet for {path} er eit delt nettverksområde montert med sshfs, men" " sshfs støttar ikkje harde lenkjer. Ver venleg og bruka modus 'SSH' i staden" " for." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Klara ikkje å skriva ny crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Kunne ikkje installere Udev-regel for profilen {profile_id}. DBus-tenesta " "{dbus_interface} var ikkje tilgjengeleg" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Timeplan udev verker ikkje med modusen {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Kunne ikkje finne UUID for {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Kunne ikkje lagre oppsett" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Kunne ikkje laste oppsett" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilen \"{name}\" finst allereie." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Den siste profilen kan ikkje fjernast." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Kan ikkje montere '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Fann ikkje konfigurasjon for kryptert mappe." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Lage ei ny kryptert mappe?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Avbryt" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Ver venleg å stadfesta passordet." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Passordene stemmer ikkje overeins." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Ta augneblinksbilete" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan ikkje avmontere {mountprocess} frå {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "Fann ikkje {}. Ver venleg å installere t.d. {}" #: common/mount.py:720 #, fuzzy, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Monteringspunktet {} er ikkje tomt." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "FEILA" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Gjennopprett tillatelsar" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Fullført" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Ventar med sikkerhetskopi medan på batteri" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Finn ikkje augneblinksmappa.\n" "Dersom ho er på ei ekstern lagringseining, plugg ho i." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Ventar %s sekund." msgstr[1] "Ventar %s sekund." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Klarte ikkje å ta augneblinksbilete {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Sluttfører" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Klarer ikkje å laga mappa" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Lagre oppsettsfila…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Lagre løyve …" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Fann påbegynt {snapshot_id} som kan fortsettjast på." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Fjernar reste-mappa {snapshot_id} frå førre køyring" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Klarer ikkje å fjerna mappa" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Ta augneblinksbilete" #: common/snapshots.py:1417 msgid "Success" msgstr "Suksess" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Delvis overføring grunna feil" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Ufullstendig overføring grunna tap av kjeldefiler (sjå 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' avslutta med kode {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Sjå 'man rsync' for fleire detaljar" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Negative rsync-sluttkodar er signalnummer. Sjå 'kill -l' og 'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Ingenting endra, nytt augeblinksbilete ikkje påkrevd" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Kan ikkje omdøype {new_path} til {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Smart-sletting" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Fjern gamle augneblinksbilete" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Prøver å oppretthalde minimum ledig plass" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Prøver å halde minimum {perc} ledige inodar" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "No" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan ikkje montere {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Fann ikkje ssh-agent. Sjekk at det er installert." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Kunne ikkje låse opp privat ssh-nøkkel. Feil passord eller passord ikkje " "tilgjengeleg for cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Kryptoalgoritmen {cipher} feila for {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Fjern sti finst, men er ikkje ei mappe." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Fjern sti er ikkje skrivbar." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Fjern sti er ikkje køyrbar." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Klarer ikkje å laga mappa på det andre systemet." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Maskina {host} støttar ikkje {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Les 'man backintime' for viare instruksar" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Testkommando på maskina {host} returnerte ukjend feil" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Maskina {host} støttar ikkje hardlenker" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Kopier offentleg ssh-nøkkel \"{pubkey}\" til maskina \"{host}\"" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Oppgi passord for \"{user}\"" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Om" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Forfattarar" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Omsetjingar" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Lisens" #: qt/app.py:169 msgid "Shortcuts" msgstr "Snarvegar" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Denne mappa finst ikkje\n" "i det valde augenblinksbiletet." #: qt/app.py:256 msgid "Add to Include" msgstr "Legg til for å inkludere" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Legg til for å ekskludere" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Klarer ikkje å laga mappa" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Finn ikkje augneblinksbiletmappa.\n" "Dersom ho er på ei ekstern lagringseining, plugg eininga i og trykk OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Taka eit augneblinksbilete" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Bruk tidspunkt for endring og størrelse for å detektere endringar i filer." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Ta eit augneblinksbilete (sjekksum-modus)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Bruk sjekksummar for å oppdage endringar." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Set augneblinksbilete-prosessen på pause" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Taka opp att augneblinksbilete-prosessen" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Stansa augneblinksbilete-prosessen" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Oppdater lista over augneblinksbilete" #: qt/app.py:508 msgid "Name snapshot" msgstr "Namngjeva augneblinksbilete" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Fjern augneblinksbilete" #: qt/app.py:516 msgid "View snapshot log" msgstr "Vis logg for augneblinksbilete" #: qt/app.py:520 msgid "View last log" msgstr "Vis siste logg" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Administrer profilar…" #: qt/app.py:528 msgid "Shutdown" msgstr "Slå av" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Slår av systemet etter at augneblinksbilete er ferdig." #: qt/app.py:532 msgid "Setup language…" msgstr "Sett opp språk…" #: qt/app.py:536 msgid "Exit" msgstr "Avslutt" #: qt/app.py:540 msgid "Help" msgstr "Hjelp" #: qt/app.py:544 msgid "Profiles config file" msgstr "Profilens konfig-fil" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Nettstad" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Endringslogg" #: qt/app.py:553 msgid "FAQ" msgstr "Vanlege spørsmål" #: qt/app.py:556 msgid "Ask a question" msgstr "Still eit spørsmål" #: qt/app.py:559 msgid "Report a bug" msgstr "Rapporter ein feil" #: qt/app.py:562 msgid "Translation" msgstr "Oversetning" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Før tilbake" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Før tilbake valde filer og mapper til opphavleg stad." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Før tilbake til …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Før tilbake valde filer og mapper til ein ny stad." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "Før tilbake den viste mappa og alt dens innhald til opphavleg stad." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Før tilbake den viste mappa og alt dens innhald til ein ny stad." #: qt/app.py:595 msgid "Up" msgstr "Opp" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Vis gøymde filer" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Jamstill augneblinksbilete…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "&Sikkerheitskopier" #: qt/app.py:676 msgid "&Restore" msgstr "Før &tilbake" #: qt/app.py:682 msgid "&Help" msgstr "&Hjelp" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Om du lukkar vindauget vil ikkje Back In Time kunne skru av maskina di når augeblinksbiletet er ferdig.\n" "Vil du verkeleg lukke vindauget?" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Vil du verkeleg gjennoppretta dette elementet?" #: qt/app.py:987 msgid "Working:" msgstr "Arbeider:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Ferdig, trong ikkje tryggleikskopiera" #: qt/app.py:1044 msgid "Working" msgstr "Arbeider" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Feil" #: qt/app.py:1076 msgid "Sent" msgstr "Sendt" #: qt/app.py:1077 msgid "Speed" msgstr "Hastigheit" #: qt/app.py:1078 msgid "ETA" msgstr "Forventa avslutta" #: qt/app.py:1140 msgid "Global" msgstr "Globalt" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Heim" #: qt/app.py:1170 msgid "Backup folders" msgstr "Kopieringsmapper" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Biletnamn" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Vil du verkeleg fjerna dette augneblinksbiletet?" msgstr[1] "Vil du verkeleg fjerna desse augneblinksbileta?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Sikkerheitskopier med {suffix} lagt til på slutten\n" "før overskriving eller fjerning av lokale element." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Nyare versjonar av filene vil verte omdøypte med etterfølgjande {suffix} før gjenoppretting.\n" "Om du ikkje treng dei meir kan du slette dei med {cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Berre gjennopprett element som ikkje eksisterer eller\n" "er nyare enn dei som er i destinasjonen.\n" "Ved å bruka rsync --update opsjon." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Fjern nyare element i kjeldemappe." #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Gjennopprett valde filer eller mapper til kjeldedestinasjon og\n" "slett filer og mapper som ikkje er i snapshot.\n" "åtvaring: dette vil\n" "sletta filer og mapper som vart\n" "ekskludert medan snapshot vart generert." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Vil du verkeleg gjennoppretta dette elementet inn i den nye mappa\n" "{path}?" msgstr[1] "" "Vil du verkeleg gjennopprette desse elementa inn i den nye mappa\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Vil du verkeleg gjennoppretta dette elementet?" msgstr[1] "Vil du verkeleg gjennoppretta desse elementa?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Er du sikker du vil fjerna alle nye filer i {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Er du sikker på at du vil fjerna alle nyare filer i di originale mappa?" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "ÅTVARING: Sletting av filer i rota av filsystemet kan knekka heile systemet " "ditt!" #: qt/app.py:1750 msgid "Snapshot" msgstr "snapshot" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Gjennoppretta {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Gjennopprett {path} til …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "Språk instillingane vert aktivert etter omstart av Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Oppsettspråk" #: qt/languagedialog.py:92 msgid "System default" msgstr "System standard" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Bruk operativsystemets språk." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Omsett: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Hei\n" "Du har bruka Back In Time på {language} nokre gonger no.\n" "Utgåva du har no er {perc} sett om til {language}. Same kor mykje teknisk kunnskap du har, kan du vera med på å setja om og dimed òg hjelpa Back In Time.\n" "Gå til {translation_platform_url} om du til vera med på dette. Sjå {back_in_time_project_website} for opplysing og spørsmål.\n" "Me orsakar avbrotet og kjem ikkje til å syna denne førespurnaden att. Dette vindauget finn du når som helst frå hjelp-menyen.\n" "Helsing laget for Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "Oversetjingsplattform" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Di oversetjing" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Visning av siste logg" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Visning av snapshot logg" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 #, fuzzy msgid "Snapshots:" msgstr "Augneblinksbilete" #: qt/logviewdialog.py:99 #, fuzzy msgid "Filter:" msgstr "Filter" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Alle" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Endringar" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Feil" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Informasjon" msgstr[1] "Informasjon" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync overføringsfeil (eksperimentell)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Feil, [I] Informasjon, [C] Endring" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "Dekoder stiar" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Spørsmål" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Sjå siste logg" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Start {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Arbeider…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Sendt:" #: qt/qtsystrayicon.py:206 #, fuzzy msgid "Speed:" msgstr "Hastigheit" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Augneblinksbilete" #: qt/qttools.py:427 msgid "Today" msgstr "I dag" #: qt/qttools.py:434 msgid "Yesterday" msgstr "I går" #: qt/qttools.py:443 msgid "This week" msgstr "Denne veka" #: qt/qttools.py:450 msgid "Last week" msgstr "Førre veke" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Dette er IKKJE eit snapshot, det er ei notids visning av dine lokale filer" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Sist sjekka {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Vis full logg" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Vert:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Brukar:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Handter profilar" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Endra" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Legg til" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Fjern" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Generell" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modus:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Kvar vil du lagra snapshot" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Mappa" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH innstillingar" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Sti:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Chiffer:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Privat lykjel:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Vel ei eksisterande privatlykjel fil (normalt namngitt \"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Lag ein ny SSH lykjel utan passord (ikkje lov viss ei privatlykjel fil " "allereie er vald)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Passord" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Lagra passord til lykjelring" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "Cache passord for Cron (Tryggleiksproblem: root kan lesa passord)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Avansert" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 #, fuzzy msgid "Full snapshot path:" msgstr "Fullt snapshot sti: " #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Timeplan" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Slått av" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Ved kvar start/omstart" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Kvart {n}. minutt" msgstr[1] "Kvart {n}. minutt" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Kvar time" msgstr[1] "Kvar time" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Kvar {n}. time" msgstr[1] "Kvar {n}. time" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Eigendefinert tidsplan" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Kvar dag" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Repetert (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Når eining vert tilkopla (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Kvar veka" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Kvar månad" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Kvart år" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Vekedag:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Time:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Timar:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Repeter køyring av Back In Time. Dette er nyttig viss datamaskina ikkje " "køyrer regelmessig." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Kvar:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Time(-ar)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dag(ar)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Veke(r)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Månad(ar)" #: qt/settingsdialog.py:555 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 "" "Køyr Back In Time så kjapt som mogleg når eininga vert kopla til (berre ein gong kvar X dag(ar)).\n" "Du vil bli avkrevd ditt sudo passord." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Inkluder" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Inkluder filer og mapper" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Legg til fil" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Legg til mappe" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Ekskluder" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Ekskluder mønster, filer eller mapper" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Legg til default" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Ekskluder filer større enn:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Ekskluder filer større enn: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been 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/settingsdialog.py:720 msgid "&Auto-remove" msgstr "%Autofjern" #: qt/settingsdialog.py:726 #, fuzzy msgid "Older than:" msgstr "Eldre enn" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "År" #: qt/settingsdialog.py:748 #, fuzzy msgid "If free space is less than:" msgstr "Viss ledig plass er mindre enn" #: qt/settingsdialog.py:768 #, fuzzy msgid "If free inodes is less than:" msgstr "Viss ledige inodes er mindre enn" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Smart-sletting:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Køyr i bakgrunnen på nettverksvert." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTELL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Hald på snapshots dei siste" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Dag(ar)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Hald på ein snapshot per dag for dei siste" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Hald på eit snapshot per veka for dei siste" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Veke(r)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Hald på eit snapshot per månad for dei siste" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "Månad(ar)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Hald på eit snapshot per år for alle åra." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ikkje fjern namngitte snapshot." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opsjonar" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Aktiver notifikasjonar" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Deaktiver snapshot når batteri er i bruk" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Straumstatus ikkje tilgjengeleg frå systemet" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Køyr kun eit snapshot i slengen" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." 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/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Backup endrar filer under gjennoppretting" #: qt/settingsdialog.py:879 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Nyare versjonar av filene vil verte omdøypte med etterfølgjande {suffix} før gjenoppretting.\n" "Om du ikkje treng dei meir kan du slette dei med {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Fortsett ved feil (hald på ufullstendige snapshot)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Bruk sjekksum for å oppdaga endringar" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Ta eit nytt snapshot uansett om det har vore endringar eller ei." #: qt/settingsdialog.py:906 #, fuzzy msgid "Log Level:" msgstr "Loggnivå" #: qt/settingsdialog.py:911 msgid "None" msgstr "Ingen" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "E&kspert val" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Åtvaring: Berre endra desse vala om du verkeleg veit kva du gjer." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Køyr 'rsync' med '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "Som cron jobb" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "på fjern vert" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "Medan eit manuelt snapshot vert tatt" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Ver gild å installer 'nocache' for å aktivera dette valet)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "På lokal maskin" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Omdiriger stdout til /dev/null i kronjobbar." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Omdiriger stderr til /dev/null i kronjobbar." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "Avgrensa rsync båndbreddebruk" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/sek" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Hald på ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Hald på utvida attributtar (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopier utrygge lenkjer (verkar berre ved direkte lenkjer" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Valet må vera sitert t.d. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Lim inn ytterlegare val til rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Legg til prefiks til ssh kommandoer" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Prefikser for å køyra kvar einaste kommando på nettverksvert.\n" "Variabler må vera maskert med \\$FOO.\n" "Dette rører ikkje rsync, Så for å legga til prefiks\n" "for rsync bruk \"%(cbRsyncOptions)s\" med\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "standardverdi" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Sjekk om nettverksvert er på nettet" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Åtvaring: viss deaktivert og nettverksverten\n" "er ikkje tilgjengeleg, dette kan leda til\n" "nokre rare feil." #: qt/settingsdialog.py:1218 #, fuzzy msgid "Check if remote host supports all necessary commands." msgstr "Sjekk om nettverksvert støttar alle naudsynte kommandoar" #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Åtvaring: viss deaktivert og nettverksverten\n" "ikkje støttar alle naudsynte kommandoar\n" "kan dette føra til rare feil." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Gjennopprett konfigurasjon" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Rediger user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Ny profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Gje profilen nytt namn" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Vil du verkeleg sletta profilen \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Sterkt tilrådd" #: qt/settingsdialog.py:1634 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 "" "Eigendefinerte timar kan berre vera ei kommaseparert lista med timar (t.d. " "8,12,18,23) eller */3 for periodisk backup kvar 3. time." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Du har ikkje valt privatlykjel for SSH.\n" "Har du lyst til å laga eit nytt offentleg/privat lykjel par?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Privatlykkjelfil \"{file}\" eksisterer ikkje." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Har du lyst til å kopiera din offentlege SSH lykjel til\n" "nettverksvert for å aktivera passordlaus pålogging?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "Autentiseringa av verten {host} er ikkje mogleg.\n" "\n" "{keytype} lykjel fingeravtrykk er:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Ver gild å verifister dette fingeravtrykket! Vil du legga det til di " "'known_hosts' fil?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Ekskluder mønster" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Ekskluder fil" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Ekskluder mappe" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Inkluder fil" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" 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:2132 msgid "Include folder" msgstr "Inkluder mappe" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Vil du verkeleg endra snapshot mappa?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Ny SSH lykjel i {path} kunne ikkje lagast" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "standardverdi" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "deaktivert" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "aktivert" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Ingen konfigurasjon funne" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Val om å samanlikna snapshot" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Kommando" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters:" msgstr "Parametrar" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Bruk %1 og %2 for sti parametrar" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Berre forskjellige snapshot" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "List opp like snapshot til: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Djupsjekk (Meir eksakt, men treigare)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Slett" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Vel alle" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Samanlikna" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Gå til" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Alternativa" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Du kan ikkje samanlikna eit snapshot med seg sjølv." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Vil du verkeleg sletta {file} i dette snapshot{snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Vil du verkeleg sletta {file} i {count} snapshot?" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "Dette kan ikkje gjerast om!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Ekskluder {path} frå nye snapshot?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Finn ikkje crontab.\n" #~ "Er du sikker på at cron er installert?\n" #~ "Dersom ikkje, skru av all automatisk sikkerheitskopiering." #~ msgid "Full snapshot path" #~ msgstr "Full snapshot sti" #~ msgid "Mode" #~ msgstr "Modus" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil '{profile}': Oppgi passord for {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang i user-callback skriptet er ikkje eksekverbart." #~ msgid "WARNING" #~ msgstr "ÅTVARING" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs versjon 1.7.2 og tidlegare har ein feil med tilvalet --reverse. Du må " #~ "oppgradere encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "user-callback skriptet har ikkje shebang (#!/bin/sh) line." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} brukar EncFS til kryptering. Ein nyleg tryggleiksrevisjon syner fleire" #~ " moglege åtaksvektorar for dette. Ver gild å sjekk \"NOTIS OM TRYGGLEIK\" i " #~ "\"man backintime\"." backintime-1.5.2/common/po/pl.po000066400000000000000000001564671465446530500165450ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-06 09:13+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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Ostrzeżenie" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Profil główny" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Lokalny (zaszyfrowane EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (zaszyfrowane EncFS)" #: common/config.py:309 msgid "Local" msgstr "Lokalny" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Klucz prywatny SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Zaszyfrowany lokalnie" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Szyfrowanie" #: common/config.py:320 msgid "SSH encrypted" msgstr "Zaszyfrowany SSH" #: common/config.py:327 msgid "Default" msgstr "Domyślny" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Katalog migawek jest nieprawidłowy!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Musisz wybrać co najmniej jeden katalog do wykonania kopii zapasowej!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Katalog kopii zapasowej nie może być uwzględniany." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Podkatalog kopii zapasowej nie może być uwzględniany." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Nieprawidłowa opcja. {path} nie jest katalogiem." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Pola hosta, użytkownika ani ID profilu nie mogą być puste." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nie można zapisać do: {path}\n" "Czy na pewno posiadasz dostęp do tego katalogu?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Docelowy system plików dla {path} jest sformatowany za pomocą FAT, który nie" " wspiera dowiązań twardych. Proszę użyć linuksowego systemu plików." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. 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/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopiowanie dowiązań (odwołanie dowiązań symbolicznych)" #: common/config.py:497 msgid "Expert Options" msgstr "Opcje zaawansowane" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Docelowy system plików dla {path} to udział zamontowany za pomocą sshfs. " "Sshfs nie wspiera dowiązań twardych. Użyj trybu 'SSH'." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Nie udało się zapisać nowej tablicy zadań (crontab)." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." 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. Wypróbuj polecenie \"systemctl enable cron\" lub " "uzyskaj pomoc na kanałach wsparcia dystrybucji GNU Linux." #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Nie udało się zainstalować reguły udev dla profilu {profile_id}. Usługa DBus" " '{dbus_interface}' nie była dostępna" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Harmonogram udev nie działa z trybem {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Nie udało się odnaleźć UUID dla {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Nie udało się zapisać konfiguracji" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Nie udało się wczytać konfiguracji" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" już istnieje." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Ostatniego profilu nie można usunąć." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Nie można zamontować '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Nie znaleziono konfiguracji dla zaszyfrowanego katalogu." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Czy stworzyć nowy zaszyfrowany katalog?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Anuluj" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Prosimy potwierdzić hasło." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Hasło nie pasuje." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Utwórz migawkę" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Nie można odmontować {mountprocess} z {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "Nie znaleziono {command}. Zainstaluj to (np. poprzez \"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Punkt montowania {mntpoint} nie jest pusty." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Wpisz hasło do profilu \"{profile}\" {mode}:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "NIEPOWODZENIE" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Przywracanie uprawnień" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Gotowe" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Kopia zapasowa przełożona z powodu pracy na baterii" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Nie można odnaleźć katalogu migawek.\n" "Jeśli znajduje się na dysku wymiennym, podłącz go." #: common/snapshots.py:839 #, 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." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Nie udało się wykonać migawki {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Finalizuję" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Nie można utworzyć katalogu" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Zapisywanie pliku konfiguracyjnego…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Zapisywanie uprawnień…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Znaleziono pozostałości {snapshot_id}, które mogą być kontynuowane." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Usuwanie pozostałości katalogu {snapshot_id} z ostatniego wykonania" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Nie mogę usunąć katalogu" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Utwórz migawkę" #: common/snapshots.py:1417 msgid "Success" msgstr "Sukces" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Transfer częściowy z powodu błędu" #: common/snapshots.py:1421 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/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' zakończył się kodem wyjścia {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Więcej informacji można znaleźć w 'man rsync'" #: common/snapshots.py:1445 msgid "" "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/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Nic się nie zmieniło, nie ma potrzeby tworzenia nowej migawki" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Nie można zmienić nazwy z {new_path} na {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Inteligentne usuwanie" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Usuwanie poprzednich migawek" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Próba utrzymamia minimum wolnego miejsca" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Próba utrzymania minimum {perc} wolnych i-węzłów" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Teraz" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Nie można zamontować {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Nie znaleziono ssh-agenta. Upewnij się, że jest zainstalowany." #: common/sshtools.py:444 msgid "" "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/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Szyfr {cipher} nie powiódł się dla {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Zdalna ścieżka istnieje, ale nie jest katalogiem." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Ścieżka zdalna nie jest zapisywalna." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Ścieżka zdalna nie jest wykonywalna." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Nie można utworzyć zdalnej ścieżki." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Zdalny host {host} nie obsługuje {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Dalsze instrukcje znajdziesz w 'man backintime'" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Polecenia sprawdzania na hoście {host} zwróciły nieznany błąd" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Zdalny host {host} nie obsługuje dowiązań twardych" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Skopiuj publiczny ssh-key \"{pubkey}\" do zdalnego hosta \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Wpisz hasło dla \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "O programie" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autorzy" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Tłumaczenia" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licencja" #: qt/app.py:169 msgid "Shortcuts" msgstr "Skróty" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Ten katalog nie istnieje\n" "w obecnie wybranej migawce." #: qt/app.py:256 msgid "Add to Include" msgstr "Dodaj do uwzględnionych" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Dodaj do wykluczonych" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "Wygląda na to, że {app_name} działa po raz pierwszy, ponieważ nie znaleziono" " żadnej konfiguracji." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Zaimportować istniejącą konfigurację (z katalogu docelowego kopii zapasowej " "lub innego komputera)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Nie można znaleźć katalogu migawek." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "Jeśli jest na dysku wymiennym, podłącz go, a następnie naciśnij OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Utwórz migawkę" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Użyj czasu i rozmiaru modyfikacji do wykrywania zmian w pliku." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Wykonaj migawkę (sprawdzanie sumy kontrolnej)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Używaj sum kontrolnych do wykrywania zmian plików." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Wstrzymaj wykonywanie migawki" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Wznów wykonywanie migawki" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Zakończ wykonywanie migawki" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Odśwież listę migawek" #: qt/app.py:508 msgid "Name snapshot" msgstr "Nazwa migawki" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Usuń migawkę" #: qt/app.py:516 msgid "View snapshot log" msgstr "Zobacz dziennik migawki" #: qt/app.py:520 msgid "View last log" msgstr "Podgląd ostatniego dziennika" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Zarządzaj profilami…" #: qt/app.py:528 msgid "Shutdown" msgstr "Zamknięcie" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Zamknij system po wykonaniu migawki." #: qt/app.py:532 msgid "Setup language…" msgstr "Ustaw język…" #: qt/app.py:536 msgid "Exit" msgstr "Wyjście" #: qt/app.py:540 msgid "Help" msgstr "Pomoc" #: qt/app.py:544 msgid "Profiles config file" msgstr "Plik konfiguracyjny profili" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Strona WWW" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Lista zmian" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ" #: qt/app.py:556 msgid "Ask a question" msgstr "Zadaj pytanie" #: qt/app.py:559 msgid "Report a bug" msgstr "Zgłoś błąd" #: qt/app.py:562 msgid "Translation" msgstr "Tłumaczenie" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Ponownie pokazuje komunikat o uczestnictwie w tłumaczeniu." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Przejście szyfrowania (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Ponownie pokazuje wiadomość o usuwaniu EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Przywróć" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Przywróć wybrane pliki lub katalogi do pierwotnego miejsca docelowego." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Przywróć do…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Przywróć wybrane pliki lub katalogi do nowego miejsca docelowego." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Przywróć aktualnie wyświetlany katalog i całą jego zawartość do pierwotnego " "miejsca docelowego." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Przywróć aktualnie wyświetlany katalog i całą jego zawartość do nowego " "miejsca docelowego." #: qt/app.py:595 msgid "Up" msgstr "Do góry" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Pokaż ukryte pliki" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Porównaj migawki…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Kopia zapasowa" #: qt/app.py:676 msgid "&Restore" msgstr "&Przywróć" #: qt/app.py:682 msgid "&Help" msgstr "P&omoc" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Jeśli zamkniesz to okno, Back In Time nie będzie mógł zamknąć systemu po " "zakończeniu tworzenia migawki." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Czy naprawdę chcesz to zamknąć?" #: qt/app.py:987 msgid "Working:" msgstr "Działanie:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Gotowe, archiwizacja niepotrzebna" #: qt/app.py:1044 msgid "Working" msgstr "Działanie" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Błąd" #: qt/app.py:1076 msgid "Sent" msgstr "Wysłane" #: qt/app.py:1077 msgid "Speed" msgstr "Prędkość" #: qt/app.py:1078 msgid "ETA" msgstr "Do końca" #: qt/app.py:1140 msgid "Global" msgstr "Globalne" #: qt/app.py:1141 msgid "Root" msgstr "Systemowy" #: qt/app.py:1142 msgid "Home" msgstr "Domowy" #: qt/app.py:1170 msgid "Backup folders" msgstr "Katalogi kopii zapasowej" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nazwa migawki" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Czy na pewno usunąć migawkę?" msgstr[1] "Czy na pewno usunąć migawki?" msgstr[2] "Czy na pewno usunąć migawki?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Utwórz kopie zapasowe z końcowym {suffix}\n" "przed nadpisaniem lub usunięciem elementów lokalnych." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Przed przywróceniem nazwy nowszych wersji plików zostaną zmienione z " "końcowym {suffix}. Jeśli już ich nie potrzebujesz, możesz je usunąć za " "pomocą następującego polecenia:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Przywróć tylko te elementy, które nie istnieją lub\n" "są nowsze niż te w miejscu docelowym.\n" "Korzystanie z opcji \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Usuń nowsze elementy z oryginalnego katalogu." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Przywróć wybrane pliki lub katalogi do pierwotnego miejsca docelowego i usuń" " pliki lub katalogi, których nie ma w migawce. Zachowaj szczególną " "ostrożność, ponieważ spowoduje to usunięcie plików i katalogów wykluczonych " "podczas wykonywania migawki." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Czy na pewno chcesz przywrócić ten element w nowym katalogu\n" "{path}?" msgstr[1] "" "Czy na pewno chcesz przywrócić te elementy w nowym katalogu\n" "{path}?" msgstr[2] "" "Czy na pewno chcesz przywrócić te elementy w nowym katalogu\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Czy na pewno chcesz przywrócić ten element?" msgstr[1] "Czy na pewno chcesz przywrócić te elementy?" msgstr[2] "Czy na pewno chcesz przywrócić te elementy?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Czy na pewno chcesz usunąć wszystkie nowsze pliki z {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Czy na pewno chcesz usunąć wszystkie nowsze pliki z oryginalnego katalogu?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your 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:1750 msgid "Snapshot" msgstr "Migawka" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Przywróć {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Przywróć {path} do…" #: qt/app.py:1948 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/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Wsparcie dla EncFS zostanie przerwane w dającej się przewidzieć przyszłości." " Ponadto nie zaleca się używania tego trybu dla profilu." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Decyzja o przedłużeniu ciągłego wsparcia zaszyfrowanych kopii zapasowych " "jest nadal w toku w zależności od zasobów projektu i dostępności " "współpracowników. Więcej szczegółów jest dostępnych w {whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "białej księdze" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Obsługa zaszyfrowanych profili migawek jest poddawana znaczącym zmianom, a " "EncFS zostanie usunięty w dającej się przewidzieć przyszłości." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Poniższe profile używają szyfrowania z EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Decyzja o przedłużeniu ciągłego wsparcia zaszyfrowanych kopii zapasowych " "jest nadal w toku w zależności od zasobów projektu i dostępności " "współpracowników. Użytkownicy są zaproszeni do dołączenia do tej dyskusji. " "Zaktualizowane szczegóły dotyczące następnych kroków są dostępne w " "{whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Ten komunikat nie zostanie wyświetlony ponownie. To okno dialogowe jest " "dostępne w dowolnym momencie za pośrednictwem menu pomocy." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Zespół Back In Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Ustaw język" #: qt/languagedialog.py:92 msgid "System default" msgstr "Domyślne systemowe" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Użyj języka systemu operacyjnego." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Przetłumaczono: {percent}" #: qt/languagedialog.py:188 #, 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 "" "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, ta wiadomość nie pojawi się sama ponownie, ale będzie dostępna w menu pomocy.\n" "Zespół Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "platformę do tłumaczenia" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Twoje tłumaczenia" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Ostatni widok dziennika" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Widok dziennika migawki" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Migawki:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtr:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "wszystkie" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "zmiany" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "błędy" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informacja" msgstr[1] "Informacje" msgstr[2] "Informacji" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "błędy transferu rsync (eksperymentalne)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] błąd, [I] informacja, [C] zmiana" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "dekoduj ścieżki" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Pytanie" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Podgląd ostatniego dziennika" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Uruchom {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Działanie…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Wysłane:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Prędkość:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "Czas do końca:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Migawki" #: qt/qttools.py:427 msgid "Today" msgstr "Dziś" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Wczoraj" #: qt/qttools.py:443 msgid "This week" msgstr "Ten tydzień" #: qt/qttools.py:450 msgid "Last week" msgstr "Poprzedni tydzień" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "To NIE jest migawka, ale podgląd na żywo lokalnych plików" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Ostatnie sprawdzenie {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Pokaż pełny dziennik" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Serwer proxy SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Host:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Użytkownik:" #: qt/settingsdialog.py:120 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 "" "Połącz się z hostem docelowym za pośrednictwem tego serwera proxy (znanego " "również jako host przeskoku). Aby uzyskać szczegółowe informacje, zobacz " "\"-J\" w dokumentacji polecenia \"ssh\" lub \"ProxyJump\" na stronie " "podręcznika \"ssh_config\"." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Zarządzaj profilami" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Edycja" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Dodaj" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Usuń" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Ogólne" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Tryb:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Gdzie zapisać migawki" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Katalog" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Ustawienia SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Ścieżka:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Szyfr:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Klucz prywatny:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Wybierz istniejący plik klucza prywatnego (zwykle o nazwie \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Utwórz nowy klucz SSH bez hasła (niedozwolone, jeśli plik klucza prywatnego " "jest już wybrany)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Hasło" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Zapisz hasło w portfelu" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Hasło pamięci podręcznej dla cron (problem bezpieczeństwa: root może " "odczytać hasło)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Zaawansowane" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Pełna ścieżka migawki:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Harmonogram" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Nieaktywny" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Przy każdym uruchomieniu / restarcie" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Co {n} minutę" msgstr[1] "Co {n} minuty" msgstr[2] "Co {n} minut" #: qt/settingsdialog.py:448 #, 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:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Co {n} godzinę" msgstr[1] "Co {n} godziny" msgstr[2] "Co {n} godzin" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Własny harmonogram" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Codziennie" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Wielokrotnie (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Po podłączeniu dysku (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Co tydzień" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Co miesiąc" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Co rok" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Dzień:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Dzień powszedni:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Godzina:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Godziny:" #: qt/settingsdialog.py:521 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:528 msgid "Every:" msgstr "Co:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "godzin" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "dni" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "tygodni" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "miesięcy" #: qt/settingsdialog.py:555 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 "" "Uruchamiaj Back In Time zaraz po podłączeniu napędu (tylko raz na X dni).\n" "Zostanie wyświetlony monit o podanie hasła sudo." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Włącz rejestrowanie komunikatów debugowania" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" "Zapisuje komunikaty poziomu debugowania w dzienniku systemowym poprzez \"--" "debug\"." #: qt/settingsdialog.py:570 msgid "" "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:583 msgid "&Include" msgstr "&Dołącz" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Dołącz pliki i katalogi" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Dodaj plik" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Dodaj katalog" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Wyklucz" #: qt/settingsdialog.py:625 #, 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}Informacje{ENDBOLD}: w trybie \"Zaszyfrowany SSH\" działają tylko " "pojedyncze lub podwójne gwiazdki (np. {example2}). Inne typy symboli " "wieloznacznych i wzorców będą ignorowane (np. {example1}). W tym trybie " "nazwy plików są nieprzewidywalne ze względu na szyfrowanie przez EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Wyklucz wzorce, pliki lub katalogi" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Dodaj domyślne" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Wyklucz pliki większe niż:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Wyklucz pliki większe niż wartość w {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Po wyłączeniu \"Pełnego trybu rsync\" będzie to miało wpływ tylko na nowe " "pliki, ponieważ w przypadku rsync jest to opcja przesyłania, a nie opcja " "wykluczenia. Dlatego też duże pliki, których kopie zapasowe utworzono " "wcześniej, pozostaną w migawkach, nawet jeśli zostaną zmodyfikowane." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Automatyczne usuwanie" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Starsze niż:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "lat" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Jeśli wolna przestrzeń jest mniejsza niż:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Jeśeli wolnych i-węzłów jest mniej niż:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Inteligentne usuwanie:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Uruchom w tle na zdalnym hoście." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EKSPERYMENTALNE" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Zachowaj wszystkie migawki na koniec" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dzień/dni." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Przechowaj jedną migawkę na dzień" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Przechowaj jedną migawkę na tydzień" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "tygodni(e)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Przechowaj jedną migawkę na miesiąc" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "miesiąc/miesięcy." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Przechowuj jedną migawkę na rok przez wszystkie lata." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Nie usuwaj nazwanych migawek." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Ustawienia" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Włącz powiadomienia" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Wyłącz migawki podczas zasilania z baterii" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Status zasilania niedostępny w systemie" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Uruchamiaj tylko jedną migawkę na raz" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Inne migawki zostaną zablokowane do czasu wykonania bieżącej migawki. Jest " "to opcja globalna, więc będzie to miało wpływ na wszystkie profile tego " "użytkownika. Ale musisz aktywować tę opcję także dla wszystkich innych " "użytkowników." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Kopia zapasowa zastąpionych plików podczas przywracania" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Przed przywróceniem nazwy nowszych wersji plików zostaną zmienione z " "końcowym {suffix}. Jeśli już ich nie potrzebujesz, możesz je usunąć za " "pomocą {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Kontynuuj przy błędach (pozwól na niekompletne migawki)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Używaj sum kontrolnych do wykrywania zmian" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" "Zrób nową migawkę, niezależnie od tego, czy nastąpiły zmiany, czy nie." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Poziom dziennika:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Brak" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Opcje e&ksportu" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Uwaga: zmień te opcje tylko wtedy, gdy naprawdę wiesz, co robisz." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Uruchamiaj 'rsync' z '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "jako zadanie crona" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "na zdalnym hoście" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "podczas wykonywania ręcznej migawki" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Zainstaluj \"nocache\", aby włączyć tę opcję)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "na lokalnej maszynie" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Przekieruj stdout z zadań crona do /dev/null." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Cron automatycznie wyśle wiadomość e-mail z załączonymi wynikami zadań cron," " jeśli jest zainstalowany MTA." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Przekieruj stderr z zadań crona do /dev/null." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Cron automatycznie wyśle wiadomość e-mail z załączonymi błędami zadań cron, " "jeśli jest zainstalowany MTA." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Ograniczenie przepustowości rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Zachowaj ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Zachowaj rozszerzone atrybuty (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Kopiowanie niebezpiecznych dowiązań (działa tylko z dowiązaniami " "bezwzględnymi)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Ogranicz do jednego systemu plików" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opcje muszą być ujęte w cudzysłów, np. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Dodatkowe opcje dla rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Dodaj prefiks do poleceń SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefiks uruchamiany przed każdym poleceniem na zdalnym hoście." #: qt/settingsdialog.py:1188 #, 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 "" "Zmienne wymagają ucieczki za pomocą \\$FOO. Nie dotyczy to rsync. Aby dodać " "przedrostek dla rsync, użyj \"{example_value}\" z {rsync_options_value}." #: qt/settingsdialog.py:1196 msgid "default" msgstr "domyślnie" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Sprawdź, czy zdalny host jest dostępny" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Ostrzeżenie: jeśli opcja jest wyłączona, a zdalny host nie jest dostępny, " "może to prowadzić do dziwnych błędów." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Sprawdź, czy zdalny host obsługuje wszystkie niezbędne polecenia." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Ostrzeżenie: jeśli opcja jest wyłączona, a zdalny host nie obsługuje " "wszystkich niezbędnych poleceń, może to prowadzić do dziwnych błędów." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Przywróć konfigurację" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Edytuj user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "Wsparcie dla EncFS zostanie przerwane w dającej się przewidzieć przyszłości." " Decyzja o przedłużeniu ciągłego wsparcia zaszyfrowanych kopii zapasowych " "jest nadal w toku w zależności od zasobów projektu i dostępności " "współpracowników. Więcej szczegółów jest dostępnych w {whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nowy profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Zmień nazwę profilu" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Na pewno chcesz usunąć profil \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Wysoce zalecane{ENDBOLD}: (Wszystkie rekomendacje są już " "uwzględnione.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Wysoce zalecane{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "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:1681 msgid "You did not choose a private key file for SSH." msgstr "Nie wybrano pliku klucza prywatnego dla SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "Czy chcesz wygenerować nową parę kluczy publiczny/prywatny bez hasła?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Plik klucza prywatnego \"{file}\" nie istnieje." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Czy chcesz skopiować swój publiczny klucz SSH na zdalny host, aby umożliwić " "logowanie bez hasła?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Nie można ustalić autentyczności hosta {host}." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "Odcisk palca klucza {keytype} to:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Zweryfikuj ten odcisk palca. Czy chcesz dodać go do swojego pliku " "'known_hosts'?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Wzór wykluczenia" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Wyklucz plik" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Wyklucz katalog" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Dołącz plik" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" jest dowiązaniem symbolicznym. Kopia zapasowa połączonego celu nie zostanie utworzona, dopóki go również nie dołączysz.\n" "Czy zamiast tego chcesz dołączyć cel dowiązania symbolicznego?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Uwzględnij katalog" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Czy na pewno chcesz zmienić katalog migawek?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Nie udało się utworzyć nowego klucza SSH w {path}." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "Wyłączone, ponieważ ten wzorzec nie działa w trybie \"Zaszyfrowany SSH\"." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(domyślnie: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "wyłączone" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "włączone" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importuj konfigurację" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Nie znaleziono konfiguracji" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importuj" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Wybierz katalog migawek, z którego ma zostać zaimportowany plik " "konfiguracyjny. Ścieżka może wyglądać następująco: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Jeśli katalog znajduje się na dysku zewnętrznym lub zdalnym, należy go " "wcześniej zamontować ręcznie." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opcje porównywania migawek" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Polecenie:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametry:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Użyj %1 i %2 dla ścieżki parametrów" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Ustaw polecenie diff lub naciśnij przycisk Anuluj." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "W tym systemie nie można znaleźć polecenia \"{cmd}\". Spróbuj czegoś innego " "lub naciśnij Anuluj." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Dla polecenia diff nie ustawiono żadnych parametrów. Używanie wartości " "domyślnej \"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Tylko migawki różniące się" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Wyświetlaj tylko migawki równe:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Szczegółowa analiza (dokładniejsza, ale bardziej czasochłonna)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Usuń" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Wybierz wszystko" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Porównaj" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Idź do" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opcje" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Nie możesz porównywać migawki samej ze sobą." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Czy na pewno chcesz usunąć {file} w migawce {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Czy na pewno chcesz usunąć {file} z {count} migawek?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "OSTRZEŻENIE: nie można tego cofnąć." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Wykluczyć {path} z przyszłych migawek?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Nie można znaleźć tablicy zadań (crontab). Czy na pewno cron jest " #~ "zainstalowany? Jeśli nie, należy wyłączyć wszystkie automatyczne kopie " #~ "zapasowe." #~ msgid "Full snapshot path" #~ msgstr "Pełna ścieżka migawki" #~ msgid "Mode" #~ msgstr "Tryb" #~ msgid "Profile" #~ msgstr "Profil" #, fuzzy, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil '{profile}': podaj hasło do {mode}: " #~ msgid "WARNING" #~ msgstr "OSTRZEŻENIE" #, fuzzy #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs w wersji 1.7.2 i wcześniejsze miały bład z opcją --reverse. " #~ "Zaktualizuj encfs." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} używa EncFS do szyfrowania. Niedawny audyt bezpieczeństwa ujawnił " #~ "kilka możliwych wektorów ataku. Spójrz na \"A NOTE ON SECURITY\" w \"man " #~ "backintime\"." backintime-1.5.2/common/po/pt.po000066400000000000000000001561211465446530500165400ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-13 17:52+0000\n" "Last-Translator: filipeaaoliveira \n" "Language-Team: Portuguese \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Aviso" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Encriptado localmente (EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH encriptado com EncFS" #: common/config.py:309 msgid "Local" msgstr "Local" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Chave privada SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Encriptado localmente" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Encriptação" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH encriptado" #: common/config.py:327 msgid "Default" msgstr "Padrão" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "A pasta de snapshots não é válida!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Deve selecionar pelo menos uma pasta para fazer a cópia de segurança!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "A pasta de backup não pode ser incluída." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "A subpasta de backup não pode ser incluída." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opção inválida. {path} não é uma pasta." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "O Host/Utilizador/ID de Perfil não pode estar vazio." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Não é possível escrever em: {path}\n" "Tem a certeza de que tem permissões de escrita?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "O sistema de ficheiros de destino para {path} está formatado com FAT, que " "não suporta hard-links. Por favor, utilize um sistema de ficheiros nativo do" " Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "O caminho de ficheiros de destino para {path} é uma partilha montada por " "SMB. Certifique-se de que o servidor SMB remoto suporta links simbólicos ou " "ative {copyLinks} em {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Copiar links (desreferenciar links simbólicos)" #: common/config.py:497 msgid "Expert Options" msgstr "Opções Avançadas" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "O sistema de ficheiros de destino para {path} é uma partilha montada por " "sshfs. O sshfs não suporta hard-links. Por favor, utilize o modo 'SSH'." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Não foi possível escrever o novo crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "O Cron não está a ser executado apesar do comando crontab estar disponível. " "As tarefas de cópia de segurança agendadas não serão executadas. O Cron pode" " estar instalado, mas não ativado. Experimente o comando “systemctl enable " "cron” ou consulte os canais de suporte da sua distribuição GNU Linux." #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "O agendamento do udev não funciona com o modo {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Não foi possível encontrar o UUID para {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Não foi possível guardar a configuração" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Não foi possível carregar a configuração" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "O perfil \"{name}\" já existe." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "O último perfil não pode ser removido." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Não é possível montar '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Configuração para pasta encriptada não encontrada." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Criar uma nova pasta encriptada?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Cancelar" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Por favor, confirme a palavra-passe." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "A palavra-passe não coincide." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Tirar snapshot" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Não é possível desmontar {mountprocess} de {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} não encontrado. Por favor, instale-o (por exemplo, " "\"{installcommand}\")" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Ponto de montagem {mntpoint} não está vazio." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Colocar a password do perfil {mode} \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "FALHOU" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Restaurar permissões" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Concluído" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Adiando a cópia de segurança enquanto a bateria está em uso" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Não é possível encontrar a pasta de snapshots.\n" "Se estiver numa unidade removível, por favor, ligue-a." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "A aguardar %s segundo." msgstr[1] "A aguardar %s segundos." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Falha ao tirar snapshot {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "A finalizar" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Não é possível criar a pasta" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "A guardar o ficheiro de configuração…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "A guardar permissões…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Encontrado snapshot pendente {snapshot_id} que pode ser continuado." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "A remover a pasta pendente {snapshot_id} da última execução" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Não é possível remover a pasta" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "A tirar snapshot" #: common/snapshots.py:1417 msgid "Success" msgstr "Sucesso" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Transferência parcial devido a erro" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Transferência parcial devido a ficheiros de origem desaparecidos (consulte " "'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' terminou com o código de saída {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Consulte 'man rsync' para mais detalhes" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Códigos de saída negativos do rsync são números de sinais, consulte 'kill " "-l' e 'man kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Nada foi alterado, não é necessário um novo snapshot" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Não é possível renomear {new_path} para {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Remoção inteligente" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "A remover snapshots antigos" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "A tentar manter o espaço livre mínimo" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "A tentar manter um mínimo de {perc} de inodes livres" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Agora" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Não é possível montar {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" "\"ssh-agent\" não encontrado. Por favor, certifique-se de que está " "instalado." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Não foi possível desbloquear a chave privada SSH. Palavra-passe incorreta ou" " palavra-passe não disponível para cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cifra {cipher} falhou para {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "O caminho remoto existe, mas não é um diretório." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "O caminho remoto não é gravável." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "O caminho remoto não é executável." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Não foi possível criar o caminho remoto." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "O host remoto {host} não suporta {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Consulte 'man backintime' para obter mais instruções" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Os comandos de verificação no host {host} retornaram um erro desconhecido" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "O host remoto {host} não suporta hardlinks" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Copiar a chave pública SSH \"{pubkey}\" para o host remoto \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Por favor, introduza a palavra-passe de\"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Sobre" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autores" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Traduções" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licença" #: qt/app.py:169 msgid "Shortcuts" msgstr "Atalhos" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Esta pasta não existe\n" "no snapshot selecionado atualmente." #: qt/app.py:256 msgid "Add to Include" msgstr "Adicionar à Inclusão" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Adicionar à Exclusão" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} parece estar a correr pela primeira vez uma vez que não foi " "encontrada nenhuma configuração." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Importar uma configuração existente (de um destino de backup ou outro " "computador)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Não é possível encontrar a pasta dos snapshots." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Se estiver numa unidade removível, por favor, ligue-a e depois pressione OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Tirar um snapshot" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Utilizar o tempo de modificação e o tamanho para deteção de alterações nos " "ficheiros." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Tirar um snapshot (modo de verificação de checksum)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Utilizar checksums para deteção de alterações nos ficheiros." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pausar o processo de snapshot" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Continuar o processo de snapshot" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Parar o processo de snapshot" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Atualizar lista de snapshots" #: qt/app.py:508 msgid "Name snapshot" msgstr "Nomear snapshot" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Remover snapshot" #: qt/app.py:516 msgid "View snapshot log" msgstr "Ver registo do snapshot" #: qt/app.py:520 msgid "View last log" msgstr "Ver último registo" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Gerir perfis…" #: qt/app.py:528 msgid "Shutdown" msgstr "Desligar" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Desligar o sistema após a conclusão do snapshot." #: qt/app.py:532 msgid "Setup language…" msgstr "Configurar idioma…" #: qt/app.py:536 msgid "Exit" msgstr "Sair" #: qt/app.py:540 msgid "Help" msgstr "Ajuda" #: qt/app.py:544 msgid "Profiles config file" msgstr "Ficheiro de configuração de perfis" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Página web" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Registo de alterações" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ" #: qt/app.py:556 msgid "Ask a question" msgstr "Fazer uma pergunta" #: qt/app.py:559 msgid "Report a bug" msgstr "Reportar um erro" #: qt/app.py:562 msgid "Translation" msgstr "Tradução" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Mostra a mensagem sobre participação nas traduções novamente." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Transição Encriptada (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Mostra a mensagem sobre a remoção EncFS novamente." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Restaurar" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Restaurar os ficheiros ou pastas selecionados para o destino original." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Restaurar para …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Restaurar os ficheiros ou pastas selecionados para um novo destino." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Restaurar a pasta atualmente mostrada e todo o seu conteúdo para o destino " "original." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Restaurar a pasta atualmente mostrada e todo o seu conteúdo para um novo " "destino." #: qt/app.py:595 msgid "Up" msgstr "Cima" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Mostrar ficheiros ocultos" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Comparar snapshots…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Atrás no &Tempo" #: qt/app.py:665 msgid "&Backup" msgstr "&Cópia de Segurança" #: qt/app.py:676 msgid "&Restore" msgstr "&Restaurar" #: qt/app.py:682 msgid "&Help" msgstr "&Ajuda" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Se fechar esta janela, o Atrás no Tempo não conseguirá desligar o seu " "sistema quando o snapshot estiver concluído." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Tem a certeza de que deseja fechar?" #: qt/app.py:987 msgid "Working:" msgstr "A trabalhar:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Concluído, sem necessidade de cópia de segurança" #: qt/app.py:1044 msgid "Working" msgstr "A trabalhar" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Erro" #: qt/app.py:1076 msgid "Sent" msgstr "Enviado" #: qt/app.py:1077 msgid "Speed" msgstr "Velocidade" #: qt/app.py:1078 msgid "ETA" msgstr "ETA (tempo estimado)" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Raiz" #: qt/app.py:1142 msgid "Home" msgstr "Pasta Pessoal" #: qt/app.py:1170 msgid "Backup folders" msgstr "Pastas de Cópia de Segurança" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nome do Snapshot" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Tem a certeza de que deseja remover este snapshot?" msgstr[1] "Tem a certeza de que deseja remover estes snapshots?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Criar cópias de segurança com sufixo {suffix}\n" "antes de substituir ou remover elementos locais." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "As versões mais recentes dos ficheiros serão renomeadas com o sufixo " "{suffix} antes de serem restauradas. Se já não precisa delas, pode removê-" "las com o seguinte comando:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Apenas restaurar elementos que não existem ou\n" "são mais recentes do que os existentes no destino.\n" "Utilizando a opção \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Remover elementos mais recentes na pasta original." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Restaurar os ficheiros ou pastas selecionados para o destino original e " "eliminar os ficheiros ou pastas que não estão no snapshot. Tenha extrema " "precaução, pois isto irá eliminar ficheiros e pastas que foram excluídos " "durante a criação do snapshot." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Tem a certeza de que deseja restaurar este elemento na nova pasta\n" "{path}?" msgstr[1] "" "Tem a certeza de que deseja restaurar estes elementos na nova pasta\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Tem a certeza de que deseja restaurar este elemento?" msgstr[1] "Tem a certeza de que deseja restaurar estes elementos?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "Tem a certeza de que deseja remover todos os ficheiros mais recentes em " "{path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Tem a certeza de que deseja remover todos os ficheiros mais recentes na sua " "pasta original?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}Alerta{BOLDEND}: Apagar ficheiros na raiz do sistema de ficheiros pode" " danificar o sistema." #: qt/app.py:1750 msgid "Snapshot" msgstr "Capturas de ecrã" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Restaurar {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurar {path} para…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "As definições de linguagem serão aplicadas depois de reiniciar o Atrás no " "Tempo." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "O suporte para o EncFS será descontinuado num futuro próximo. " "Consequentemente, não é recomendado utilizar este modo para um perfil." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "A decisão sobre a substituição do suporte contínuo de cópias de segurança " "encriptadas ainda está pendente, dependendo dos recursos do projeto e da " "disponibilidade do colaborador. Mais detalhes estão disponíveis neste " "{whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "artigo técnico" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "O suporte para perfis de instantâneos encriptados está a sofrer alterações " "significativas e o EncFS será removido num futuro próximo." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Os seguintes perfis usam encriptação EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "A decisão sobre a substituição do suporte contínuo de cópias de segurança " "encriptadas ainda está pendente, dependendo dos recursos do projeto e da " "disponibilidade do colaborador. Os utilizadores estão convidados a " "participar nesta discussão. Detalhes atualizados sobre os próximos passos " "estão disponíveis neste {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Esta mensagem não será mostrada novamente. O diálogo está disponível a " "qualquer altura através do menu de ajuda." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "A sua equipa do Back In Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Linguagem de configuração" #: qt/languagedialog.py:92 msgid "System default" msgstr "Predefinição do sistema" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Usar a linguagem do sistema operativo." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Traduzido: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Olá\n" "Já usou o Atrás no Tempo em {language} algumas vezes.\n" "A tradução para {language} da versão do Atrás no Tempo atualmente instalada está {perc} completa. Independentemente do seu nível de conhecimentos de informática, poderá contribuir para a tradução e, portanto, para o próprio Atrás no Tempo.\n" "Visite {translation_platform_url} se quiser contribuir. Para obter mais ajuda ou para qualquer questão, visite {back_in_time_project_website}.\n" "Pedimos desculpa pala interrupção. Esta mensagem não voltará a ser mostrada, mas estará sempre disponível através do menu de ajuda.\n" "A Equipa do Atrás no Tempo" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "plataforma de tradução" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "A sua tradução" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Última Visualização do Log" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Visualização do Log do Snapshot" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Perfil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Capturas de ecrã:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtro:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Tudo" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Alterações" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Erros" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informação" msgstr[1] "Informações" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "falhas de transferências rsync (experimental)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Erro, [I] Informação, [C] Alteração" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "caminhos descodificados" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Pergunta" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Perfil: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Ver Último Log" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Iniciar {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "A processar…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Enviado:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Velocidade:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Capturas de ecrã" #: qt/qttools.py:427 msgid "Today" msgstr "Hoje" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Ontem" #: qt/qttools.py:443 msgid "This week" msgstr "Esta semana" #: qt/qttools.py:450 msgid "Last week" msgstr "Semana passada" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Isto NÃO é um snapshot, mas uma visualização em tempo real dos seus " "ficheiros locais" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Última marcação {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Mostrar log completo" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Proxy SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Servidor:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Porta:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Utilizador:" #: qt/settingsdialog.py:120 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\" nas paginas man para detalhes." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Gerir Perfis" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Editar" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Adicionar" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Remover" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Geral" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modo:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Onde guardar as snapshots" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Pasta" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Definições de SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Caminho:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Cifra:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Chave privada:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Escolha um ficheiro de chave privada existente (normalmente designado por " "\"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Crie uma nova chave SSH sem palavra-passe (não permitido se um ficheiro de " "chave privada já estiver selecionado)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Palavra-passe" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Guardar a Palavra-passe num Keyring" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Palavra-passe de cache para Cron (problema de segurança: o root consegue ler" " a password)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Avançado" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Caminho completo da cópia de segurança:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Agenda" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Desactivado" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "A cada arranque/reinício" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "A cada {n} minuto" msgstr[1] "A cada {n} minutos" #: qt/settingsdialog.py:448 #, 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:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "A cada {n} hora" msgstr[1] "A cada {n} horas" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "A Horas Definidas" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Diariamente" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Repetidamente (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Quando o dispositivo for conectado (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Semanalmente" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Mensalmente" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Anualmente" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Dia:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Dia da semana:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Hora:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Hora(s):" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Correr Atrás no Tempo repetidamente. Isto é útil se o computador não estiver" " a funcionar regularmente." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "A cada:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Hora(s)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dia(s)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Semana(s)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Mês/Meses" #: qt/settingsdialog.py:555 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 "" "Execute o Atrás no Tempo assim que a unidade estiver ligada (apenas uma vez a cada X dias).\n" "Será solicitada a sua senha sudo." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Ativar registo de mensagens de debug" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" "Escreve mensagens de nível de debug no registo do sistema através de \"--" "debug\"." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Cuidado: Utilize-o apenas temporariamente para diagnóstico, pois gera uma " "grande quantidade de resultados." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Incluir" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Incluir ficheiros e pastas" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Adicionar um ficheiro" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Adicionar pasta" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Excluir" #: qt/settingsdialog.py:625 #, 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 (por exemplo, {example2}). Outros tipos de " "caracteres coringa e padrões serão ignorados (por exemplo, {example1}). Os " "nomes de ficheiros são imprevisíveis neste modo devido à encriptação por " "EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Excluir padrões, ficheiros ou pastas" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Adicionar padrão" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Excluir ficheiros maiores que:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Excluir ficheiros maiores do que o valor em {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Com o 'modo rsync completo' desativado, isto apenas afetará os novos " "ficheiros, uma vez que para o rsync esta é uma opção de transferência, não " "uma opção de eliminação. Portanto, os ficheiros grandes dos quais foi feito " "backup anteriormente persistirão nos instantâneos, mesmo que tenham sido " "modificados." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Auto-remoção" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Mais antigo do que:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Ano(s)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Se espaço livre é inferior a:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Se os inodes livres forem inferiores a:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Remoção inteligente:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Executar em segundo plano no host remoto." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Guarde todas copias de segurança para o último" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dia(s)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Mantenha uma cópia de segurança por dia para o último" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Mantenha uma cópia de segurança por semana para o último" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "semana(s)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Mantenha uma cópia de segurança por mês para o último" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mês/meses." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Guarde uma cópia de segurança por ano para todos os anos." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Não remover snapshots nomeados." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opções" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Activar as notificações" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Desativar cópias de segurança quando usa a bateria" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Estado de energia não disponível no sistema" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Execute uma cópia de segurança de cada vez" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Outras cópias de segurança serão bloqueadas até que a cópia de segurança " "atual esteja concluída. Esta é uma opção global. Portanto, isto afetará " "todos os perfis deste utilizador. Mas também precisa de ativar isto para " "todos os outros utilizadores." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Cópia de segurança de ficheiros substituídos na restauração" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "As versões mais recentes dos ficheiros serão renomeadas com o sufixo " "{suffix} antes de serem restauradas. Se já não precisa delas, pode removê-" "las com {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuar com erros (manter instantâneos incompletos)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Use a soma de verificação para detectar mudanças" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" "Tire um novo instantâneo, independentemente de haver alterações ou não." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Nível de Log:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nenhum" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "O&pções Avançadas" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Cuidado: Altere estas opções apenas se souber realmente o que está a fazer." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Execute 'rsync' com '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "como cron job" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "no host remoto" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "ao tirar um instantâneo manual" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Por favor instale o 'nocache' para activar esta opção)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "na máquina local" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirecione stdout para /dev/null em cronjobs." #: qt/settingsdialog.py:1005 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:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirecione stderr para /dev/null em cronjobs." #: qt/settingsdialog.py:1016 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:1024 msgid "Limit rsync bandwidth usage:" msgstr "Limite a utilização da largura de banda rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/seg" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Preservar ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Preservar atributos estendidos (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Copiar links inseguros (funciona apenas com links absolutos)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Restringir a um sistema de ficheiros" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "As opções devem ser citadas, p. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Cole opções adicionais para rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Adicionar prefixo aos comandos SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefixo a ser executado antes de cada comando no host remoto." #: qt/settingsdialog.py:1188 #, 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 "" "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:1196 msgid "default" msgstr "padrão" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Verificar se o host remoto está online" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Aviso: Se estiver desativado e o host remoto não estiver disponível, isto " "poderá levar a alguns erros estranhos." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Verifique se o host remoto suporta todos os comandos necessários." #: qt/settingsdialog.py:1221 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 host remoto não suportar todos os comandos " "necessários, poderá levar a alguns erros estranhos." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Restaurar Configuração" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Editar o retorno de chamada do utilizador" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "O suporte para o EncFS será descontinuado num futuro próximo. A decisão " "sobre a substituição do suporte contínuo de cópias de segurança encriptadas " "ainda está pendente, dependendo dos recursos do projeto e da disponibilidade" " do colaborador. Mais detalhes estão disponíveis neste {whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Novo perfil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Renomear perfil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Tem a certeza que deseja remover este perfil \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Altamente recomendado{ENDBOLD}: (todas as recomendações já incluídas.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Altamente recomendado{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "As horas personalizadas só podem ser uma lista de horas separadas por " "vírgulas (por exemplo, 8,12,18,23) ou */3 para cópias de segurança " "periódicas de 3 em 3 horas." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Não escolheu um ficheiro de chave privada para o SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Gostaria de gerar um novo par de chaves pública/privada sem palavra-passe?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "O ficheiro de chave privada \"{file}\" não existe." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Gostaria de copiar a sua chave SSH pública para o host remoto para permitir " "o login sem palavra-passe?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "A autenticidade do host {host} não pode ser estabelecida." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "A impressão digital da chave {keytype} é:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Verifique esta impressão digital. Gostaria de adicioná-lo ao seu ficheiro " "'known_hosts'?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Excluir padrão" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Excluir ficheiro" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Excluir pasta" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Incluir ficheiro" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" é um link simbólico. Não será feito backup do destino ligado até que o inclua também.\n" "Gostaria de incluir o destino do link simbólico?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Incluir pasta" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Tem a certeza que deseja remover este perfil?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Falha ao criar nova chave SSH em {path}." #: qt/settingsdialog.py:2310 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:2369 msgid "(default: {})" msgstr "(padrão: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "desativado" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "ativado" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importar configuração" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Nenhuma configuração encontrada" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importar" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Selecione a pasta de instantâneo a partir da qual o ficheiro de configuração" " deve ser importado. O caminho pode ser semelhante a: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder 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/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opções sobre a comparação de snapshots" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Comando:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parâmetros:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Utilize %1 e %2 para os parâmetros do caminho" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Por favor, defina um comando diff ou prima Cancelar." #: qt/snapshotsdialog.py:90 #, 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 " "outra coisa ou prima Cancelar." #: qt/snapshotsdialog.py:98 #, 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:134 msgid "Differing snapshots only" msgstr "Apenas snapshots diferentes" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Liste apenas instantâneos que sejam iguais a:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Verificação profunda (mais precisa, mas lenta)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Eliminar" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Selecionar Todos" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Comparar" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Ir para" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opções" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Não pode comparar um instantâneo consigo mesmo." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Deseja realmente eliminar {file} no instantâneo {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Deseja realmente excluir {file} em {count} instantâneos?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "AVISO: Isto não pode ser revogado." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Excluir {path} dos snapshots futuros?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Não é possível encontrar o crontab. Tem a certeza de que o cron está " #~ "instalado? Se não estiver, deve desativar todas as cópias de segurança " #~ "automáticas." #~ msgid "Profile" #~ msgstr "Perfil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Perfil '{profile}': Introduza a palavra-passe para {mode}: " #~ msgid "WARNING" #~ msgstr "CUIDADO" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "A versão 1.7.2 e anteriores do encfs têm um bug com a opção --reverse. Por " #~ "favor, atualize o encfs." backintime-1.5.2/common/po/pt_BR.po000066400000000000000000001534351465446530500171300ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-02 13:39+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Aviso" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "criptografado localmente" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH criptografado" #: common/config.py:309 msgid "Local" msgstr "Local" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Chave privada SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "criptografado localmente" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Criptografia" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH criptografado" #: common/config.py:327 msgid "Default" msgstr "Padrão" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Pasta para Snapshots não é válida!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Você deve selecionar pelo menos uma pasta para backup!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Pasta backup não pode ser incluída." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Sub-pasta backup não pode ser incluída." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opção inválida. {path} não é uma pasta." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/Usuário/Perfil não devem estar vazios." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Não é possível escrever em: {path}\n" "Você tem certeza de que possui permissão de escrita?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} 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}' está formatado com FAT, que não" " suporta hard-links. Por favor, utilize um sistema de arquivos nativo do " "Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "O sistema de arquivos de destino em '{path}' é um compartilhamento SMB " "montado. Por favor, tenha certeza que o servidor SMB remoto suporta symlinks" " ou ative a opção '{copyLinks}' em '{expertOptions}'." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Copiar links (desreferenciar links simbólicos)" #: common/config.py:497 msgid "Expert Options" msgstr "Opções avançadas" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "O sistema de arquivos de destino em '{path}' é um compartilhamento sshfs " "montado. O sshfs não suporta hard-links. Por favor, utilize o modo 'SSH' " "como alternativa." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Falha ao escrever um novo crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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 está disponível" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Agendamento udev não funciona com o modo {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Não foi possível encontrar UUID para {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Falha ao salvar a configuração" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Falha ao carregar a configuração" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "O perfil \"{name}\" já existe." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "O último perfil não pode ser removido." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Não foi possível montar '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Configuração para pasta encriptada não encontrada." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Criar uma nova pasta encriptada?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Cancelar" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Por favor, confirme a senha." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Senha incorreta." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Criar snapshot" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Não é possível desmontar {mountprocess} de {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} não encontrado. Por favor, instale-o (p.ex através de " "\"{installcommand}\"" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "O ponto de montagem {mntpoint} não está vazio." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Insira a senha para o perfil {mode} \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "FALHOU" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Permissões de restauração" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Pronto" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Adiando o backup enquanto estiver na bateria" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Não é possível localizar a pasta de snapshots.\n" "Se ele estiver em uma unidade removível, por favor insira a unidade removível (usb)." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Aguardando %s segundo." msgstr[1] "Aguardando %s segundos." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Falha ao criar snapshot {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Finalizando" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Não é possível criar a pasta" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Salvando arquivo de configuração…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Salvando permissões…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Encontrado sobras de {snapshot_id} que podem ser continuadas." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Removendo a pasta remanescente do {snapshot_id} da última execução" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Não é possível remover a pasta" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Criando snapshot" #: common/snapshots.py:1417 msgid "Success" msgstr "Sucesso" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Transferência parcial devido a um erro" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Transferência parcial devido a arquivos de origem ausentes (consulte 'man " "rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "O 'rsync' finalizou com o código de saída {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Veja 'man rsync' para mais detalhes" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Códigos de saída negativos rsync são números de sinal, veja 'kill -l' e 'man" " kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Nada mudou, nenhum snapshot novo necessário" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Não é possível renomear {new_path} para {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Remoção Inteligente" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Removendo snapshots antigos" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Tentando manter o mínimo de espaço livre" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Tentando manter o mínimo de inodes {perc} livres" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Agora" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Não é possível montar {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" "ssh-agent não encontrado. Por favor, certifique-se de que está instalado." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Não é possível destravar a chave privada ssh. A senha está errada ou não " "está disponível para o cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cifra {cipher} falhou para {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "O caminho remoto existe, mas não é um diretório." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "O caminho remoto não é gravável." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "O caminho remoto não é executável." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Não foi possível criar o caminho remoto." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "O host remoto {host} não suporta {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Consulte 'man backintime' para instruções adicionais" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Os comandos de verificação no host {host} retornaram um erro desconhecido" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "O host remoto {host} não suporta hardlinks" #: common/sshtools.py:1164 #, 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:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Por favor, digite a senha para \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Sobre" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autores" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Traduções" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licença" #: qt/app.py:169 msgid "Shortcuts" msgstr "Atalhos" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Essa pasta não existe\n" "no snapshot atualmente selecionado." #: qt/app.py:256 msgid "Add to Include" msgstr "Adicione à Lista de Inclusões" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Adicione à Lista de Exclusões" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} parece estar em execução pela primeira vez, pois nenhuma " "configuração foi encontrada." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Importar uma configuração existente (de uma pasta de destino de backup ou de" " outro computador)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Não foi possível encontrar a pasta de snapshots." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "Se não estiver em um drive removível por favor insira-o e aperte OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Criar snapshot" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Utilizar o horário de modificação e o tamanho para a detecção de alterações " "em arquivos." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Criar snapshot (modo checksum)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Utilizar checksums para a detecção de alterações em arquivos." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pausar processo de snapshot" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Resumir processo de snapshot" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Parar processo de snapshot" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Atualizar lista de snapshots" #: qt/app.py:508 msgid "Name snapshot" msgstr "Nomear snapshot" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Remover snapshot" #: qt/app.py:516 msgid "View snapshot log" msgstr "Visualizar log dos snapshots" #: qt/app.py:520 msgid "View last log" msgstr "Visualizar último log" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Gerenciar perfis…" #: qt/app.py:528 msgid "Shutdown" msgstr "Desligar" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Desligar o sistema após a finalização do snapshot." #: qt/app.py:532 msgid "Setup language…" msgstr "Configurar idioma…" #: qt/app.py:536 msgid "Exit" msgstr "Sair" #: qt/app.py:540 msgid "Help" msgstr "Ajuda" #: qt/app.py:544 msgid "Profiles config file" msgstr "Arquivo de configuração de perfis" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Website" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Registro de alterações" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ (perguntas frequentes)" #: qt/app.py:556 msgid "Ask a question" msgstr "Faça uma pergunta" #: qt/app.py:559 msgid "Report a bug" msgstr "Relatar um problema (bug)" #: qt/app.py:562 msgid "Translation" msgstr "Traduções" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Restaurar" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Restaurar os arquivos ou pastas selecionadas para o destino original." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Restaurar para …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Restaurar os arquivos ou pastas selecionadas para um novo destino." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Restaurar a pasta atualmente exibida e todos os seus conteúdos para o " "destino original." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Restaurar a pasta atualmente exibida e todos os seus conteúdos para um novo " "destino." #: qt/app.py:595 msgid "Up" msgstr "Acima" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Mostrar arquivos ocultos" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Comparar snapshots…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Backup" #: qt/app.py:676 msgid "&Restore" msgstr "&Restaurar" #: qt/app.py:682 msgid "&Help" msgstr "&Ajuda" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Se você fechar esta janela, Back In Time não será capaz de desligar seu " "sistema quando a snapshot estiver terminada." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Você realmente quer fechá-lo?" #: qt/app.py:987 msgid "Working:" msgstr "Trabalhando:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Concluído, backup não necessário" #: qt/app.py:1044 msgid "Working" msgstr "Trabalhando" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Erro" #: qt/app.py:1076 msgid "Sent" msgstr "Enviado" #: qt/app.py:1077 msgid "Speed" msgstr "Velocidade" #: qt/app.py:1078 msgid "ETA" msgstr "ETA (tempo estimado)" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Raiz" #: qt/app.py:1142 msgid "Home" msgstr "Home" #: qt/app.py:1170 msgid "Backup folders" msgstr "Pastas de backup" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nome do Snapshot" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Tem certeza de que deseja remover esse snapshot?" msgstr[1] "Tem certeza de que deseja remover esses snapshots?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Criar cópias de backup com o sufixo {suffix}\n" "antes de sobrescrever ou remover elementos locais." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Versões mais recentes de arquivos serão renomeadas com {suffix} à direita " "antes da restauração. Se não precisar mais deles, poderá removê-los com " "{cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Apenas restaurar elementos que não existem ou\n" "são mais recentes do que os existentes no destino.\n" "Utilizando a opção \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Remover elementos mais recentes na pasta original." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Restaure os arquivos ou pastas selecionados para o destino original e exclua" " os arquivos ou pastas que não estão no snapshot. Tenha muito cuidado porque" " isso excluirá arquivos e pastas que foram excluídos durante o snapshot." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Você realmente deseja restaurar este elemento na nova pasta\n" "{path}?" msgstr[1] "" "Você realmente deseja restaurar estes elementos na nova pasta\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Você realmente deseja restaurar este elemento?" msgstr[1] "Você realmente deseja restaurar estes elementos?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "Você tem certeza que deseja remover todos os arquivos mais recentes em " "{path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Você tem certeza que deseja remover todos os arquivos mais recentes em sua " "pasta original?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}Aviso{BOLDEND}: Apagar arquivos no sistema de arquivos raiz (root) " "pode danificar seu sistema." #: qt/app.py:1750 msgid "Snapshot" msgstr "Snapshot" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Restaurar {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurar {path} para …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "As configurações de idioma terão efeito apenas após reiniciar o Back in " "Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 #, fuzzy msgid "Your Back In Time Team" msgstr "Back In &Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Configurar idioma" #: qt/languagedialog.py:92 msgid "System default" msgstr "Padrão do sistema" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Use o idioma do sistema operacional." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Traduzido: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Olá\n" "Você utilizou 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 idioma {language} está {perc} completa. Independentemente do seu nível de expertise técnica, você pode contribuir para a tradução e, assim, para o Back In Time em si.\n" "Visite a {translation_platform_url} se desejar contribuir. Para assistência adicional e dúvidas, acesse o site do projeto Back In Time em {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 através do menu de ajuda.\n" "Sua equipe Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "plataforma de tradução" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Sua tradução" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Visualização do Último Log" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Visualização do Log do Snapshot" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Perfil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Snapshots:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtrar:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Tudo" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Alterações" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Erros" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informação" msgstr[1] "Informações" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "Falhas na transferência do rsync (experimental)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Erro, [I] Informação, [C] Alteração" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "decodificar caminhos" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Pergunta" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Perfil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Visualizar Último Log" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Inicializar {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Trabalhando…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Enviado:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Velocidade:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Snapshots" #: qt/qttools.py:427 msgid "Today" msgstr "Hoje" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Ontem" #: qt/qttools.py:443 msgid "This week" msgstr "Esta semana" #: qt/qttools.py:450 msgid "Last week" msgstr "Semana passada" #: qt/qttools.py:596 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/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Última verificação {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Mostrar Log Completo" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Proxy SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Host:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Porta:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Usuário:" #: qt/settingsdialog.py:120 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 "" "Conecte ao host de destino por meio desse proxy (também conhecido como um " "host de salto) Veja \"-J\" na documentação do comando \"ssh\" ou " "\"ProxyJump\" na página \"ssh_config\" no manual para detalhes." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Gerenciar perfis" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Editar" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Adicionar" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Remover" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Geral" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Modo:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Onde salvar os snapshots" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Pasta" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Configurações do SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Caminho:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Cifra:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Chave Privada:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Escolha um arquivo de chave privada existente (normalmente chamado de " "\"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Criar uma nova chave SSH sem senha (não permitido se um arquivo de chave " "privada já está selecionado)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Senha" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Salvar a Senha no Chaveiro" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Armazenar a Senha para o Cron (Problema de segurança: o root poderá ler a " "senha)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Avançado" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 #, fuzzy msgid "Full snapshot path:" msgstr "Caminho completo do snapshot:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Agendar" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Desabilitado" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Em cada início/reinício" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "A cada {n} minuto" msgstr[1] "A cada {n} minutos" #: qt/settingsdialog.py:448 #, 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:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "A cada {n} hora" msgstr[1] "A cada {n} horas" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Horas personalizadas" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Todo dia" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Repetidamente (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Quando o drive for conectado (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Toda semana" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Todo mês" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Todo ano" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Dia:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Dia da Semana:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Hora:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Horas:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Executar o Back In Time repetidamente. Isto é útil se o computador não é " "executado regularmente." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Todo:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Hora(s)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dia(s)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Semana(s)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Mês(es)" #: qt/settingsdialog.py:555 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 "" "Executar o Back In Time assim que o dispositivo for conectado (apenas uma vez a cada X dias).\n" "Você será solicitado a fornecer sua senha de sudo." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Habilitar registro de mensagens de depuração" #: qt/settingsdialog.py:568 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/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Cuidado: Use-o apenas temporariamente para diagnóstico por gerar um excesso " "de resultados." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Incluir" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Incluir arquivos e pastas" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Adicionar arquivo" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Adicionar pasta" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Excluir" #: qt/settingsdialog.py:625 #, 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}: No modo 'SSH encrypted' (ssh criptografado), apenas um " "asterisco ou dois asteriscos são funcionais (p.ex {example2}). Outros tipos " "de wildcards e padrões serão ignorados (p.ex {example1}). Nome de arquivos " "são imprevisíveis nesse modo graças à encriptação por EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Instruções, arquivos ou pastas a serem excluídos" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Adicionar padrão" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Excluir arquivos maiores que:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Exclua arquivos maiores que o valor em {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Com o 'modo rsync completo' desativado, isso afetará apenas os novos " "arquivos, pois para o rsync, esta é uma opção de transferência, não uma " "opção de exclusão. Portanto, arquivos grandes dos quais foi feito backup " "anteriormente persistirão nos snapshots, mesmo que tenham sido modificados." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Remoção automática" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Mais antigo que:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Ano(s)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Se o espaço livre for menor que:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Se o número de inodes livres é menor que:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Remoção inteligente:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Executar em segundo plano no host remoto." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Manter todos os snapshots pelos últimos" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dia(s)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Manter um snapshot por dia pelos últimos" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Manter um snapshot por semana pelas últimas" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "semana(s)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Manter um snapshot por mês pelos últimos" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mês(es)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Manter um snapshot por ano por todos os anos." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Não remover os snapshots com nome." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opções" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Habilitar notificações" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Desativar snapshot quando estiver na bateria" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Status de energia não disponível no sistema" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Executar apenas um snapshot por vez" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Outros snapshots serão bloqueados até que o snapshot atual seja concluído. " "Esta é uma opção global. Portanto, isso afetará todos os perfis deste " "usuário. Mas também precisa ativar isso para todos os outros usuários." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Backup de arquivos substituídos na restauração" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Versões mais recentes de arquivos serão renomeadas com {suffix} à direita " "antes da restauração. Se não precisar mais deles, poderá removê-los com " "{cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuar em caso de erros (manter snapshots incompletos)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Usar checksum para detectar alterações" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" "Criar um novo snapshot independentemente de existir ou não alterações." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Nível de Log:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nenhum" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "O&pções Avançadas" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Cuidado: Altere essas opções apenas se você souber o que está fazendo." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Executar 'rsync' com '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "como uma tarefa cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "no host remoto" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "quando for criar um snapshot manual" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Por favor, instale 'nocache' para habilitar esta opção)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "na máquina local" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirecionar o stdout para /dev/null nas tarefas cron." #: qt/settingsdialog.py:1005 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" " um MTA estiver instalado." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirecionar o stderr para /dev/null nas tarefas cron." #: qt/settingsdialog.py:1016 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:1024 msgid "Limit rsync bandwidth usage:" msgstr "Limite o uso da largura de banda rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/seg" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Preservar ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Preservar atributos extendidos (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Copiar links inseguros (funciona apenas com links absolutos)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Restringir a um sistema de arquivos" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "As opções devem estar entre aspas, por exemplo: {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Colar opções adicionais para o rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Adicionar prefixo aos comandos SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefixo para ser executado antes de cada comando no host remoto." #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "As variáveis precisam ser escapadas com \\$FOO. Isso não afeta o rsync. " "Portanto, para adicionar um prefixo para rsync, use \"{example_value}\" com " "{rsync_options_value}" #: qt/settingsdialog.py:1196 msgid "default" msgstr "padrão" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Verificar se host remoto está online" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Aviso: Se o host remoto estiver desabilitado e não disponível , isso pode " "levar a alguns erros estranhos." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Verifique se o host remoto suporta todos os comandos necessários." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Aviso: Se o host remoto estiver desabilitado e não suportar todos os " "comandos necessários, isso pode levar a erros esquisitos." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Restaurar Configuração" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Editar user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Novo perfil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Renomear perfil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Você tem certeza que deseja excluir o perfil \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Altamente recomendado{ENDBOLD}: (Todas as recomendações já inclusas.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Altamente recomendado{ENDBOLD}:{files}" #: qt/settingsdialog.py:1634 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 "" "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:1681 msgid "You did not choose a private key file for SSH." msgstr "" "Você não escolheu um arquivo de chave privada (private key) para o SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Você gostaria de gerar um novo par de chaves públicas/privadas sem senha?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Arquivo de chave privada \"{file}\" não existe." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Você gostaria de copiar sua chave pública SSH para o host remoto para " "habilitar o login sem senha?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "A autenticidade do host {host} não pôde ser estabelecida." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} chave de impressão digital é:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Por favor verifique essa impressão digital. Você gostaria de adicioná-la no " "seu arquivo 'known_hosts' (hosts conhecidos)?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Excluir padrão" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Excluir arquivo" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Excluir pasta" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Incluir arquivo" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" é um symlink. O alvo conectado não será salvo até você incluí-lo também.\n" "Você gostaria de incluir o alvo do symlink em seu lugar?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Incluir pasta" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Deseja realmente mudar a pasta de snapshots ?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Falha em criar nova chave SSH em {path}" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" "Desativado porque este padrão não funciona no modo 'SSH criptografado'." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(padrão: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "desativado" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "ativado" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Configuração de importação" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Nenhum arquivo de configuração encontrado" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importar" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Selecione a pasta de snapshot da qual o arquivo de configuração deve ser " "importado. O caminho pode ser semelhante a: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Se a pasta estiver localizada em uma unidade externa ou remota, ela deverá " "ser montada manualmente antes." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opções sobre comparação de snapshots" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Comando:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parâmetros:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Use %1 e %2 para os parâmetros de caminho" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Por favor, defina um comando diff ou pressione Cancelar." #: qt/snapshotsdialog.py:90 #, 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 pôde ser encontrado neste sistema. Por favor, tente " "outra coisa ou pressione Cancelar." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Nenhum parâmetro definido para o comando diff. Usando o valor padrão " "\"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Apenas snapshots diferentes" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Listar apenas snapshots iguais a: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Verificação profunda (mais preciso, mas lento)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Excluir" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Selecionar Tudo" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Comparar" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Ir para" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opções" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Você não pode comparar um snapshot com ele mesmo." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Você realmente deseja apagar {file} no snapshot {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Você realmente deseja apagar {file} em {count} snapshots?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "Aviso: Isto não pode ser revogado." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Excluir {path} de snapshots futuros?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Não foi possível achar o crontab. Tem certeza que cron está instalado? Se " #~ "não, você deve desabilitar todos os backups automáticos." #~ msgid "Full snapshot path" #~ msgstr "Caminho completo do snapshot" #~ msgid "Info" #~ msgstr "Informações" #~ msgid "Mode" #~ msgstr "Modo" #~ msgid "Profile" #~ msgstr "Perfil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Perfil '{profile}': Digite a senha para {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang no script do user-callback não é executável." #~ msgid "WARNING" #~ msgstr "AVISO" #~ 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." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "script de user-callback não tem linha de shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} utiliza EncFS para criptografia. Uma auditoria recente de segurança " #~ "revelou vários possíveis vetores de ataque para isso. Por favor, dê uma " #~ "olhada em \"UMA OBSERVAÇÃO SOBRE SEGURANÇA\" em \"man backintime\"." backintime-1.5.2/common/po/ro.po000066400000000000000000001610721465446530500165360ustar00rootroot00000000000000# Romanian 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-19 11:10+0000\n" "Last-Translator: espresso_nightingale \n" "Language-Team: Romanian \n" "Language: ro\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==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Avertisment" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Profil principal" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Local (criptat EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (criptat EncFS)" #: common/config.py:309 msgid "Local" msgstr "Local" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Cheie privată SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Local criptat" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Criptare" #: common/config.py:320 msgid "SSH encrypted" msgstr "Criptat SSH" #: common/config.py:327 msgid "Default" msgstr "Implicit" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: „{name}”" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Dosarul de instantanee nu este valid!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "" "Trebuie să selectați cel puțin un dosar pentru a efectua copia de rezervă!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Dosarul copiei de rezervă nu poate fi inclus." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Sub-dosarul copiei de rezervă nu poate fi inclus." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opțiune nevalidă. {path} nu este un dosar." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Gazda/Utilizatorul/ID-ul de profil nu trebuie să fie gol." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nu se poate scrie la: {path}\n" "Sigur aveți acces la scriere?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Sistemul de fișiere destinație pentru {path} este formatat cu FAT care nu " "suportă legăturile tari. Utilizați un sistem de fișiere nativ Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Sistemul de fișiere destinație pentru {path} este o partajare montată pe " "SMB. Asigurați-vă că serverul SMB de la distanță suportă legăturile " "simbolice sau activați {copyLinks} în {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Copiază legăturile (dereferențiază legăturile simbolice)" #: common/config.py:497 msgid "Expert Options" msgstr "Opțiuni expert" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Sistemul de fișiere destinație pentru {path} este o partajare montată pe " "sshfs. Sshfs nu oferă suport pentru legăturile permanente (hard-link). " "Utilizați în schimb modul „SSH”." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Nu s-a putut scrie un nou crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron nu rulează deși comanda crontab este disponibilă. Sarcinile de copiere " "de rezervă programate nu vor rula. Cron ar putea fi instalat, dar nu este " "activat. Încercați comanda „systemctl enable cron” sau consultați canalele " "de asistență ale distribuției GNU Linux pe care o aveți." #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Nu s-a putut instala regula Udev pentru profilul {profile_id}. Serviciul " "DBus „{dbus_interface}” nu a fost disponibil" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Udev-ul programului nu funcționează cu modul {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Nu s-a putut găsi UUID-ul pentru {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Nu s-a putut salva configurația" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Nu s-a putut încărca configurația" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilul „{name}” există deja." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Ultimul profil nu poate fi eliminat." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Nu se poate monta „{command}”" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Nu s-a găsit configurația pentru dosarul criptat." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Creați un nou dosar criptat?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Anulează" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Confirmați parola." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Parola nu se potrivește." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Ia instantaneu" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Nu se poate demonta {mountprocess} de la {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} nu a fost găsită. Instalați-o (de ex. prin „{installcommand}”)" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Punctul de montare {mntpoint} nu este liber." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Introduceți parola pentru profilul {mode} „{profile}”:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "EȘUAT" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Restabilește permisiunile" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Terminat" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Se amână copierea de rezervă în timp ce se utilizează bateria" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Nu se poate găsi dosarul de instantanee.\n" "Dacă este pe o unitate detașabilă, conectați-o." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Se așteaptă %s secundă." msgstr[1] "Se așteaptă %s secunde." msgstr[2] "Se așteaptă %s de secunde." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Nu s-a putut realiza instantaneul {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Se finalizează" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Nu se poate crea dosarul" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Se salvează fișierul de configurație…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Se salvează permisiunile…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "A fost găsit {snapshot_id} rămas care poate fi continuat." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Se elimină dosarul {snapshot_id} rămas din ultima rulare" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Nu se poate elimina dosarul" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Se ia instantaneul" #: common/snapshots.py:1417 msgid "Success" msgstr "Succes" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Transfer parțial din cauza unei erori" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Transfer parțial din cauza fișierelor sursă dispărute (consultați „man " "rsync”)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "„rsync” s-a încheiat cu codul de ieșire {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Consultați „man rsync” pentru mai multe detalii" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Codurile de ieșire rsync negative sunt numere de semnal, consultați „kill " "-l” și „man kill”" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Nimic nu s-a schimbat, nu este necesar un instantaneu nou" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Nu se poate redenumi {new_path} la {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Înlăturare inteligentă" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Se elimină instantaneele vechi" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Se încearcă păstrarea minimului de spațiu liber" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Se încearcă păstrarea a minim {perc} inode-uri libere" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Acum" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Nu se poate monta {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent nu a fost găsit. Asigurați-vă că este instalat." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Nu s-a putut debloca cheia privată ssh. Parolă greșită sau parola nu este " "disponibilă pentru cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cifrul {cipher} a eșuat pentru {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Calea de la distanță există dar nu este un director." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Calea de la distanță nu este inscriptibilă." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Calea de la distanță nu este executabilă." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Nu s-a putut crea calea de la distanță." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Gazda de la distanță {host} nu suportă {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Consultați „man backintime” pentru instrucțiuni suplimentare" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Comenzile de verificare de pe gazda {host} au returnat o eroare necunoscută" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Gazda de la distanță {host} nu suportă legăturile tari" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" "Copiază cheia publică ssh „{pubkey}” la gazda de la distanță „{host}”." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Introduceți o parolă pentru „{user}”." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Despre" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autori" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Traduceri" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licență" #: qt/app.py:169 msgid "Shortcuts" msgstr "Scurtături" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Acest dosar nu există\n" "în instantaneul selectat curent." #: qt/app.py:256 msgid "Add to Include" msgstr "Adaugă la includeri" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Adaugă la excluderi" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} pare să ruleze pentru prima dată întrucât nu s-a găsit nicio " "configurație." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Importați o configurație existentă (dintr-un dosar țintă de copie de rezervă" " sau de pe un alt calculator)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Nu s-a putut găsi dosarul cu instantanee." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "Dacă se află pe o unitate detașabilă, conectați-o și apoi apăsați OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Ia un instantaneu" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Utilizează timpul și dimensiunea modificării pentru detectarea modificărilor" " fișierelor." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Ia un instantaneu (modul checksum)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Utilizează checksum-uri pentru detectarea modificărilor fișierelor." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Suspendă procesul de instantaneu" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Reia procesul de instantaneu" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Oprește procesul de instantaneu" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Reîmprospătează lista de instantanee" #: qt/app.py:508 msgid "Name snapshot" msgstr "Denumește instantaneul" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Elimină instantaneul" #: qt/app.py:516 msgid "View snapshot log" msgstr "Vizualizează jurnalul instantaneului" #: qt/app.py:520 msgid "View last log" msgstr "Vizualizează ultima înregistrare în jurnal" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Gestionează profilurile…" #: qt/app.py:528 msgid "Shutdown" msgstr "Oprește" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Oprește sistemul după finalizarea instantaneului." #: qt/app.py:532 msgid "Setup language…" msgstr "Configurează limba…" #: qt/app.py:536 msgid "Exit" msgstr "Ieșire" #: qt/app.py:540 msgid "Help" msgstr "Ajutor" #: qt/app.py:544 msgid "Profiles config file" msgstr "Fișierul de configurare al profilului" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Pagină web" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Jurnal de modificări" #: qt/app.py:553 msgid "FAQ" msgstr "Întrebări frecvente" #: qt/app.py:556 msgid "Ask a question" msgstr "Pune o întrebare" #: qt/app.py:559 msgid "Report a bug" msgstr "Raportează o defecțiune" #: qt/app.py:562 msgid "Translation" msgstr "Traducere" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Afișează din nou mesajul despre participarea la traducere." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Tranziție de criptare (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Afișează din nou mesajul despre înlăturarea EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Restaurează" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Restaurează fișierele sau dosarele selectate la destinația originală." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Restaurează la …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Restaurează fișierele sau dosarele selectate la o nouă destinație." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Restaurează dosarul afișat în prezent și tot conținutul acestuia la " "destinația originală." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Restaurează dosarul afișat în prezent și tot conținutul acestuia la o nouă " "destinație." #: qt/app.py:595 msgid "Up" msgstr "Sus" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Arată fișierele ascunse" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Compară instantaneele…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Copie de rezervă" #: qt/app.py:676 msgid "&Restore" msgstr "&Restaurează" #: qt/app.py:682 msgid "&Help" msgstr "&Ajutor" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Dacă închideți această fereastră, Back In Time nu vă va putea opri sistemul " "când instantaneul este finalizat." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Sigur doriți să o închideți?" #: qt/app.py:987 msgid "Working:" msgstr "Se lucrează:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Terminat, nicio copie de rezervă necesară" #: qt/app.py:1044 msgid "Working" msgstr "Se lucrează" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Eroare" #: qt/app.py:1076 msgid "Sent" msgstr "Trimis" #: qt/app.py:1077 msgid "Speed" msgstr "Viteză" #: qt/app.py:1078 msgid "ETA" msgstr "Timp rămas" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Home" #: qt/app.py:1170 msgid "Backup folders" msgstr "Dosare copie de rezervă" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Nume instantaneu" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Sigur doriți să eliminați acest instantaneu?" msgstr[1] "Sigur doriți să eliminați aceste instantanee?" msgstr[2] "Sigur doriți să eliminați aceste instantanee?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Crează copii de rezervă cu sufixul {suffix}\n" "înainte de a suprascrie sau de a elimina elementele locale." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Noile versiuni ale fișierelor vor fi redenumite cu sufixul {suffix} înainte " "de restaurare. Dacă nu mai aveți nevoie de ele, puteți să le eliminați cu " "următoarea comandă:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Restaurează doar elementele care nu există sau\n" "care sunt mai noi decât cele de la destinație.\n" "Se utilizează opțiunea „rsync --update”." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Elimină elementele mai noi din dosarul original." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Restaurează fișierele sau dosarele selectate la destinația originală și " "șterge fișiere sau dosare care nu se află în instantaneu. Fiți extrem de " "atent pentru că acest lucru va șterge fișiere și dosare care au fost excluse" " în timpul realizării instantaneului." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Sigur doriți să restaurați acest element în dosarul nou\n" "{path}?" msgstr[1] "" "Sigur doriți să restaurați aceste elemente în dosarul nou\n" "{path}?" msgstr[2] "" "Sigur doriți să restaurați aceste elemente în dosarul nou\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Sigur doriți să restaurați acest element?" msgstr[1] "Sigur doriți să restaurați aceste elemente?" msgstr[2] "Sigur doriți să restaurați aceste elemente?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Sigur doriți să eliminați toate fisierele mai noi din {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Sigur doriți să eliminați toate fișierele mai noi din dosarul original?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}Avertisment{BOLDEND}: Ștergerea fișierelor din root-ul sistemului de " "fișiere vă poate distruge întregul sistem." #: qt/app.py:1750 msgid "Snapshot" msgstr "Instantaneu" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Restaurează {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurează {path} la …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Configurările de limbă intră în vigoare doar după repornirea Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Suportul pentru EncFS va fi întrerupt în viitorul apropiat. Nu este " "recomandat să utilizați acel mod pentru un profil de acum încolo." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "O decizie cu privire la înlocuirea suportului continuu pentru copiile de " "rezervă criptate este încă în așteptare, în funcție de resursele proiectului" " și de disponibilitatea contribuitorilor. Mai multe detalii sunt disponibile" " în acest {whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "raport" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Suportul pentru profilurile de instantanee criptate suferă modificări " "semnificative și EncFS va fi eliminat în viitorul apropiat." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Următorul profil(uri) utilizează criptare cu EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "O decizie cu privire la înlocuirea suportului continuu al copiilor de " "rezervă criptate este încă în așteptare, în funcție de resursele proiectului" " și disponibilitatea contribuitorilor. Utilizatorii sunt invitați să se " "alăture acestei discuții. Detalii actualizate despre următorii pași sunt " "disponibile în acest {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Acest mesaj nu va fi afișat din nou. Acest dialog este disponibil în orice " "moment prin meniul de ajutor." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Echipa Back In Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Configurează limba" #: qt/languagedialog.py:92 msgid "System default" msgstr "Implicit de sistem" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Utilizează limba sistemului de operare." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Tradus: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Salut\n" "Ați utilizat Back In Time în limba {language} de câteva ori până acum.\n" "Traducerea versiunii instalate de Back In Time în {language} este {perc} completă. Indiferent de nivelul de expertiză tehnică, puteți contribui la traducere și, astfel, la Back In Time în sine.\n" "Vizitați {translation_platform_url} dacă doriți să contribuiți. Pentru asistență suplimentară și întrebări, vizitați {back_in_time_project_website}.\n" "Ne cerem scuze pentru întrerupere, iar acest mesaj nu va mai fi afișat. Acest dialog este disponibil în orice moment prin meniul de ajutor.\n" "Echipa ta Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "platforma de traducere" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Traducerea ta" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Ultima vizualizare a jurnalului" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Vizualizare jurnal de instantaneu" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Instantanee:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtrează:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Toate" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Modificări" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Erori" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informație" msgstr[1] "Informații" msgstr[2] "Informații" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "eșecuri de transfer rsync (experimental)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Eroare, [I] Informații, [C] Modificare" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "decodează căile" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Întrebare" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Vizualizează ultima înregistrare" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Pornește {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Se lucrează…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Trimis:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Viteză:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Instantanee" #: qt/qttools.py:427 msgid "Today" msgstr "Astăzi" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Ieri" #: qt/qttools.py:443 msgid "This week" msgstr "Săptămâna aceasta" #: qt/qttools.py:450 msgid "Last week" msgstr "Săptămâna trecută" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Aceasta NU este un instantaneu ci o vizualizare live a fișierelor locale" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Ultima verificare la {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Arată jurnalul complet" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Proxy SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Gazdă:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Utilizator:" #: qt/settingsdialog.py:120 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 " "man „ssh_config” pentru detalii." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Gestionează profilurile" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Editează" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Adaugă" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Elimină" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Generale" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Mod:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Unde să se salveze instantaneele" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Dosar" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Configurări SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Cale:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Cifru:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Cheie privată:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Alegeți un fisier cheie privată existent (de obicei denumit „id_rsa”)" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Creează o nouă cheie SSH fără parolă (nu este permis dacă un fișier cheie " "privată este deja selectat)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Parola" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Salvează parola la inelul de chei" #: qt/settingsdialog.py:378 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/settingsdialog.py:390 msgid "Advanced" msgstr "Avansat" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Cale completă instantaneu:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Program" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Dezactivat" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "La fiecare pornire/restart" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "La fiecare {n} minut" msgstr[1] "La fiecare {n} minute" msgstr[2] "La fiecare {n} de minute" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "La fiecare oră" msgstr[1] "La fiecare {n} ore" msgstr[2] "La fiecare {n} de ore" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "La fiecare {n} oră" msgstr[1] "La fiecare {n} ore" msgstr[2] "La fiecare {n} de ore" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Ore personalizate" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Zilnic" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "În mod repetat (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Când unitatea este conectată (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Săptămânal" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Lunar" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Anual" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Zi:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Zi lucrătoare:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Oră:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Ore:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Rulează Back In Time în mod repetat. Acest lucru este util dacă calculatorul" " nu rulează în mod regulat." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "La fiecare:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Oră (Ore)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Zi (Zile)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Săptămână (Săptămâni)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Lună (Luni)" #: qt/settingsdialog.py:555 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 "" "Rulează Back In Time imediat după conectarea unității (doar o dată la X zile).\n" "Vi se va solicita parola sudo." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Activează înregistarea mesajelor de depanare" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" "Scrie mesaje la nivel de depanare în jurnalul de sistem prin „--debug”." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Atenție: Folosiți-l doar temporar pentru diagnosticare, deoarece generează o" " cantitate mare de rezultate." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Include" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Include fișierele și dosarele" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Adaugă un fișier" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Adaugă un dosar" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Exclude" #: qt/settingsdialog.py:625 #, 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 „criptat SSH”, doar asterisk-urile " "simple sau duble sunt funcționale (de ex. {example2}). Alte tipuri de " "wildcard-uri și modele vor fi ignorate (de ex. {example1}). Numele " "fișierelor sunt imprevizibile în acest mod din cauza criptării cu EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Exclude modele, fișiere sau dosare" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Adaugă implicit" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Exclude fișierele mai mari decât:" #: qt/settingsdialog.py:698 #, 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/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Cu „Modul rsync complet” dezactivat, acest lucru va afecta doar fișierele " "noi, deoarece pentru rsync aceasta este o opțiune de transfer, nu o opțiune " "de excludere. Prin urmare, fișierele mari care au fost copiate de rezervă " "anterior vor persista în instantanee chiar dacă au fost modificate." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Eliminare &automată" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Mai vechi de:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "An (Ani)" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Dacă spațiul liber este mai mic decât:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Dacă inode-urile libere sunt mai puține decât:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Înlăturare inteligentă:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Rulează pe fundal pe gazda de la distanță." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Păstrează toate instantaneele pentru ultimele" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "zi(zile)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Păstrează un instantaneu pe zi pentru ultimele" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Păstrează un instantaneu pe săptămână pentru ultimele" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "săptămână(săptămâni)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Păstrează un instantaneu pe lună pentru ultimele" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "lună(luni)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Păstrează un instantaneu pe an pentru toți anii." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Nu elimina instantaneurile denumite." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opțiuni" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Activează înștiințările" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Dezactivează instantaneele când se utilizează bateria" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Starea alimentării nu este disponibilă din sistem" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Rulează doar un instantaneu la un moment dat" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Alte instantanee vor fi blocate până la finalizarea instantaneului curent. " "Aceasta este o opțiune globală. Deci, va afecta toate profilurile pentru " "acest utilizator. Dar trebuie să activați acest lucru și pentru toți " "ceilalți utilizatori." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Copiază de rezervă fișierele înlocuite la restaurare" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Noile versiuni ale fișierelor vor fi redenumite cu sufixul {suffix} înainte " "de restaurare. Dacă nu mai aveți nevoie de ele, puteți să le eliminați cu " "{cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuă la erori (păstrează instantanee incomplete)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Utilizează checksum pentru a detecta modificările" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Ia un instantaneu nou indiferent dacă au existat modificări sau nu." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Nivel de jurnalizare:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nimic" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Opțiuni e&xpert" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Atenție: modificați aceste opțiuni doar dacă știți cu adevărat ce faceți." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Rulează „rsync” cu „{cmd}”:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "ca sarcină cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "pe gazda de la distanță" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "când se ia un instantaneu manual" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Instalați „nocache” pentru a activa această opțiune)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "pe mașina locală" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirecționează stdout către /dev/null în sarcinile cron." #: qt/settingsdialog.py:1005 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 atașat al cronjob-urilor dacă" " un MTA este instalat." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirecționează stderr către /dev/null în sarcinile cron." #: qt/settingsdialog.py:1016 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 atașate ale cronjob-urilor dacă " "un MTA este instalat." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Limitează utilizarea lățimii de bandă a rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KO/sec" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Păstrează ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Păstrează atributele extinse (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Copiază legăturile nesigure (funcționează doar cu legăturile absolute)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Restricționează la un singur sistem de fișiere" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opțiunile trebuie citate, de ex. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Lipește opțiunile adiționale la rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Adaugă un prefix la comenzile SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefix de rulat înainte de fiecare comandă pe gazda de la distanță." #: qt/settingsdialog.py:1188 #, 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 "" "Variabilele trebuie să fie escapate cu \\$FOO. Acest lucru nu atinge rsync. " "Deci, pentru a adăuga un prefix pentru rsync utilizați „{example_value}” cu " "{rsync_options_value}." #: qt/settingsdialog.py:1196 msgid "default" msgstr "implicit" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Verifică dacă gazda de la distanță este conectată" #: qt/settingsdialog.py:1214 msgid "" "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:1218 msgid "Check if remote host supports all necessary commands." msgstr "Verifică dacă gazda de la distanță suportă toate comenzile necesare." #: qt/settingsdialog.py:1221 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 suportă toate " "comenzile necesare, acest lucru ar putea duce la unele erori ciudate." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Restaurează configurația" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Editează user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "Suportul pentru EncFS va fi întrerupt în viitorul apropiat. O decizie cu " "privire la înlocuirea suportului continuu pentru copiile de rezervă criptate" " este încă în așteptare, în funcție de resursele proiectului și " "disponibilitatea contribuitorilor. Mai multe detalii sunt disponibile în " "acest {whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Profil nou" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Redenumește profilul" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Sigur doriți să ștergeți profilul „{name}”?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Foarte recomandat{ENDBOLD}: (Toate recomandările sunt deja incluse.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Foarte recomandat{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "Orele personalizate pot fi doar o listă de ore separată prin virgulă (de ex." " 8,12,18,23) sau */3 pentru copii de rezervă periodice la fiecare 3 ore." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Nu ați ales un fișier cheie privată pentru SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Doriți să generați o nouă pereche de chei publice/private fără parolă?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Fișierul cheie privată „{file}” nu există." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Doriți să copiați cheia publică SSH la gazda de la distanță pentru a activa " "autentificarea fără parolă?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Autenticitatea gazdei {host} nu poate fi stabilită." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "Amprenta cheii {keytype} este:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Verificați această amprentă. Doriți să o adăugați la fișierul „known_hosts”?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Exclude modelul" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Exclude fișierul" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Exclude dosarul" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Include fișierul" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "„{path}” este o legătură simbolică. Ținta legată nu se va copia de rezervă până când nu o și includeți.\n" "Doriți să includeți în schimb ținta legăturii simbolice?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Include dosarul" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Sigur doriți să modificați dosarul de instantanee?" #: qt/settingsdialog.py:2194 #, 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:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" "Dezactivat pentru că acest model nu este funcțional în modul „criptat SSH”." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(implicit: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "dezactivat" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "activat" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Importă configurația" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Nu s-a găsit nicio configurație" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Importă" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Selectați dosarul de instantaneu din care ar trebui să fie importat fișierul" " de configurație. Calea poate arăta astfel: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Dacă dosarul se află pe o unitate externă sau de la distanță, aceasta " "trebuie montată manual în prealabil." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opțiuni despre compararea instantaneelor" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Comandă:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametri:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Utilizați %1 și %2 pentru parametrii căii" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Stabiliți o comandă diff sau apăsați Anulează." #: qt/snapshotsdialog.py:90 #, 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 Anulează." #: qt/snapshotsdialog.py:98 #, 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:134 msgid "Differing snapshots only" msgstr "Doar instantaneele care diferă" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Listează doar instantaneele care sunt egale cu:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Verificare profundă (mai precis, dar încet)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Șterge" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Selectează toate" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Compară" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Navighează la" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opțiuni" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Nu puteți compara un instantaneu cu el însuși." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Sigur doriți să ștergeți {file} în instantaneul {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Sigur doriți să ștergeți {file} în {count} instantanee?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "AVERTISMENT: Acest lucru nu poate fi revocat." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Excludeți {path} din instantaneele viitoare?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Nu se poate găsi crontab.\n" #~ "Sigur este instalat cron?\n" #~ "Dacă nu, ar trebui să dezactivați toate copiile de rezervă automate." #~ msgid "Full snapshot path" #~ msgstr "Calea completă a instantaneului" #~ msgid "Mode" #~ msgstr "Mod" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil „{profile}”: Introduceți parola pentru {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang în scriptul user-callback nu este executabilă." #~ msgid "WARNING" #~ msgstr "AVERTISMENT" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs versiunea 1.7.2 și anterioară are o defecțiune cu opțiunea --reverse. " #~ "Actualizați encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "scriptul user-callback nu are nicio linie shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} utilizează EncFS pentru criptare. Un audit de securitate recent a " #~ "dezvăluit mai mulți vectori de atac posibili pentru acesta. Aruncați o " #~ "privire la „A NOTE ON SECURITY” în „man backintime”." backintime-1.5.2/common/po/ru.po000066400000000000000000001754451465446530500165550ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Предупреждение" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Основной профиль" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "зашифрованный" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH зашифровано" #: common/config.py:309 msgid "Local" msgstr "Локально" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH приватный ключ" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "зашифрованный" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Шифрование" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH зашифровано" #: common/config.py:327 msgid "Default" msgstr "По-умолчанию" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Профиль: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Каталог снимков задан неверно!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Вы должны выбрать хотя бы одну папку для резервного копирования!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Папка резервного копирования не должна быть включена." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Содержимое папки резервного копирования не должно быть включено." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} - не папка." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Сервер/Пользователь/Профиль-ID не должны быть пустыми." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Не удаётся произвести запись в: {path}\n" "Вы уверены, что имеете право на запись?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Файловая система назначения {path} отформатирована в FAT, не поддерживающая " "\"жесткие\" ссылки. Используйте нативные файловые системы Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Файловая система назначения {path} является SMB-монтированным ресурсом. " "Убедитесь, что удаленный SMB-сервер поддерживает симлинки, или активируйте " "{copyLinks} в {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Копировать ссылки (разыменование символических ссылок)" #: common/config.py:497 msgid "Expert Options" msgstr "Расширенные настройки" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Файловая система назначения для {path} является sshfs-монтированным " "ресурсом. sshfs не поддерживает жесткие ссылки. Пожалуйста, используйте " "режим 'SSH'." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Не удалось записать новый crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Расписание udev неработает в режиме {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Не удалось найти UUID для {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Не удалось сохранить конфигурацию" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Не удалось загрузить конфигурацию" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Профиль \"{name}\" уже существует." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Последний профиль не может быть удален." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Невозможно смонтировать '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Не найдена конфигурация для зашифрованной директории." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Создать новую зашифрованную папку?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Отмена" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Пожалуйста, подтвердите пароль." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Пароль не подходит." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Сделать снимок" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Не удалось размонтировать {mountprocess} из {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} не найдено. Пожалуйста установите (например через " "\"{installcommand}\"" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Точка монтирования {mntpoint} не пустая." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Введите пароль для {mode} профиля \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "НЕУСПЕШНО" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Восстановить права" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Готово" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Отсрочка резервного копирования при работе от батареи" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Невозможно найти каталог снимков.\n" "Если он находится на съёмном диске, пожалуйста, подключите его." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Ждать %s секунду." msgstr[1] "Ждать %s секунды." msgstr[2] "Ждать %s секунд." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Не удалось создать снимок {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Окончание" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Невозможно создать каталог" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Сохранение файла конфигурации…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Сохранение разрешений…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Найдены остатки {snapshot_id}, с которыми можно продолжить." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" "Удаление остатков папки {snapshot_id}, оставшиеся после последнего запуска" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Невозможно удалить каталог" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Создание снимка" #: common/snapshots.py:1417 msgid "Success" msgstr "Успешно" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Неполная передача из-за ошибки" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Частичный перенос из-за исчезновения исходных файлов (см. «man rsync»)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "rsync завершился c кодом {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Подробнее см. «man rsync»" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Неудачные коды выхода из rsync - это номера сигналов, см. 'kill -l' и 'man " "kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Ничего не изменилось, нет необходимости в резервной копии" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Невозможно переименовать {new_path} в {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Умное удаление" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Удалить старые резервные копии" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Стараться сохранять минимальное свободное место" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Попытка сохранить минимум {perc} свободных инодов" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Сейчас" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Не удалось смонтировать {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-агент не найден. Убедитесь, что он установлен." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Не удалось разблокировать приватный ключ ssh. Неверный пароль или пароль не " "подходит для cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Пароль {cipher} не верен для {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Удаленный путь существует, но не является каталогом." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Удаленный путь недоступен для записи." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Удаленный путь не доступен для записи." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Не удалось создать удаленный путь." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Удаленный узел {host} не поддерживает{command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Дополнительные инструкции см. в man backintime" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Команды проверки на хосте {host} вернули неизвестную ошибку" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Удаленный хост {host} не поддерживает жесткие ссылки" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Скопировать открытый ssh-ключ \"{pubkey}\" на удаленный хост \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Пожалуйста, подтвердите пароль для \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "О программе" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Разработчики" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Переводы" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Лицензия" #: qt/app.py:169 msgid "Shortcuts" msgstr "Ярлыки" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Этой папки не существует\n" "в выбранном снимке." #: qt/app.py:256 msgid "Add to Include" msgstr "Включить" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Добавить в исключения" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "Похоже, что {app_name} запускается впервые, так как конфигурация не найдена." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Импортировать существующую конфигурацию (из резервной целевой папки или с " "другого компьютера)?" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Невозможно создать каталог" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Невозможно найти каталог снимков.\n" "Если он находится на съёмном диске, пожалуйста, подключите его и нажмите ОК." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Сделать снимок" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Используйте время модификации и размер для обнаружения изменений файла." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Сделать снимок (режим контрольной суммы)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Использовать контрольную сумму для обнаружения изменений." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Приостановить процесс создания снимка" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Продолжить процесс создания снимка" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Остановить процесс создания снимка" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Обновить список моментальных снимков" #: qt/app.py:508 msgid "Name snapshot" msgstr "Имя снимка" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Удалить снимок" #: qt/app.py:516 msgid "View snapshot log" msgstr "Смотреть log снимка" #: qt/app.py:520 msgid "View last log" msgstr "Смотреть последний log" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Управление профилями…" #: qt/app.py:528 msgid "Shutdown" msgstr "Выключить" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Выключить систему после завершения создания снимка." #: qt/app.py:532 #, fuzzy msgid "Setup language…" msgstr "Изменить язык…" #: qt/app.py:536 msgid "Exit" msgstr "Выйти" #: qt/app.py:540 msgid "Help" msgstr "Справка" #: qt/app.py:544 msgid "Profiles config file" msgstr "config файл профиля" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Сайт" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Список изменений" #: qt/app.py:553 msgid "FAQ" msgstr "Часто задаваемые вопросы" #: qt/app.py:556 msgid "Ask a question" msgstr "Задать вопрос" #: qt/app.py:559 msgid "Report a bug" msgstr "Сообщить об ошибке" #: qt/app.py:562 msgid "Translation" msgstr "Перевод" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Восстановить" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Восстановление выбранных файлов или каталогов в исходное место назначения." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Восстановить в…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" "Восстановление выбранных файлов или каталогов в новое место назначения." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Восстановление текущей показанной папки и всего ее содержимого в исходное " "место назначения." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Восстановление текущей показанной папки и всего ее содержимого в новое место" " назначения." #: qt/app.py:595 msgid "Up" msgstr "Вверх" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Показать скрытые файлы" #: qt/app.py:601 #, fuzzy msgid "Compare snapshots…" msgstr "Сравнить снимки…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "&Резервное копирование" #: qt/app.py:676 msgid "&Restore" msgstr "&Восстановление" #: qt/app.py:682 msgid "&Help" msgstr "&Справка" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Если вы закроете это окно - Back In Time не сможет выключить систему после " "завершения создания снимка." #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Вы действительно хотите восстановить этот файл?" #: qt/app.py:987 msgid "Working:" msgstr "Работаю:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Всё сделано, сохранять ничего не надо" #: qt/app.py:1044 msgid "Working" msgstr "Работаю" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Ошибка" #: qt/app.py:1076 msgid "Sent" msgstr "Отправлено" #: qt/app.py:1077 msgid "Speed" msgstr "Скорость" #: qt/app.py:1078 msgid "ETA" msgstr "Приблизительно оставшееся время" #: qt/app.py:1140 msgid "Global" msgstr "Общее" #: qt/app.py:1141 msgid "Root" msgstr "Корневой каталог" #: qt/app.py:1142 msgid "Home" msgstr "Домашний каталог" #: qt/app.py:1170 msgid "Backup folders" msgstr "Резервные директории" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Название резервной копии" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Вы точно хотите удалить этот снимок?" msgstr[1] "Вы точно хотите удалить эти снимки?" msgstr[2] "Вы точно хотите удалить эти снимки?" #: qt/app.py:1408 #, fuzzy, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Создать резервные копии с последующим {suffix} перед\n" "перезаписью или удалением локальных файлов." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Более новые версии файлов перед восстановлением будут переименованы с " "трейлингом {suffix}. Если они вам больше не нужны, то можете удалить их с " "помощью команды:" #: qt/app.py:1432 #, fuzzy msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Восстанавливать только несуществующие элементы\n" "или элементы новее, чем имеющиеся.\n" "Использует опцию \"rsync --update\"." #: qt/app.py:1467 #, fuzzy msgid "Remove newer elements in original folder." msgstr "Удалять более новые файлы в исходной папке." #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Восстановление выбранных файлов/папок в исходное место назначения и\n" "удаление файлов/папок, которых нет в снимке.\n" "Будьте предельно осторожны, потому что при этом\n" "будут удалены файлы/папки,\n" "исключенные при создании снимка." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Вы действительно хотите восстановить этот файл в новую папку\n" "{path}?" msgstr[1] "" "Вы действительно хотите восстановить эти файлы в новую папку\n" "{path}?" msgstr[2] "" "Вы действительно хотите восстановить эти файлы в новую папку\n" "{path}?" #: qt/app.py:1490 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Вы действительно хотите восстановить этот файл?" msgstr[1] "Вы действительно хотите восстановить эти файлы?" msgstr[2] "Вы действительно хотите восстановить эти файлы?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Вы уверены, что хотите удалить все новые файлы в {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "Вы уверены, что хотите удалить все новые файлы в исходной папке?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}ВНИМАНИЕ{BOLDEND}: Удаление файлов в корне файловой системы может " "сломать всю вашу систему." #: qt/app.py:1750 msgid "Snapshot" msgstr "Снимок" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Восстановить {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Восстановить {path} в…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "Смена языка произойдет только после перезапуска Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Изменить язык" #: qt/languagedialog.py:92 msgid "System default" msgstr "Язык системы" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Использовать язык операционных систеы." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Переведено: {percent}" #: qt/languagedialog.py:188 #, 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/languagedialog.py:217 msgid "translation platform" msgstr "платформы для перевода" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Ваш перевод" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Просмотр последнего лога" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Просмотр журнала снимка" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Профиль:" #: qt/logviewdialog.py:84 #, fuzzy msgid "Snapshots:" msgstr "Резервные копии" #: qt/logviewdialog.py:99 #, fuzzy msgid "Filter:" msgstr "Фильтр" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Все" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Изменения" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Ошибки" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Информация" msgstr[1] "Информация" msgstr[2] "Информация" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Ошибка, [I] Информация, [C] Изменение" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "декодирование путей" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Вопрос" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Профиль: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Просмотреть последний журнал" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Запустить {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Работаю…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Отправлено:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Скорость:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Резервные копии" #: qt/qttools.py:427 msgid "Today" msgstr "Сегодня" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Вчера" #: qt/qttools.py:443 msgid "This week" msgstr "Эта неделя" #: qt/qttools.py:450 msgid "Last week" msgstr "На прошлой неделе" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Это НЕ снимок, а живой вид ваших локальных файлов" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Последняя проверка {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Показать весь лог" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH-прокси" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Хост:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Порт:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 #, fuzzy msgid "User:" msgstr "Пользователь" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Управление профилями" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Изменить" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Добавить" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Удалить" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Общие" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Режим:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Место для сохранения резервных копий" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Папка" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Найстроки SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Путь:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "шифр:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Приватный ключ:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Выберите существующий файл приватного ключа (обычно с именем \"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Создать новый SSH-ключ без пароля (не допускается, если файл приватного " "ключа уже выбран)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Пароль" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Сохранить пароль в связку ключей" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "Закешировать пароль для Cron (Внимание: root может прочитать пароль)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Дополнительно" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Полный путь снимка:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Расписание" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Отключено" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "При каждой загрузке системы" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Каждую {n} минуту" msgstr[1] "Каждые {n} минут" msgstr[2] "Каждые {n} минут" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Каждый {n} час" msgstr[1] "Каждые {n} часа" msgstr[2] "Каждые {n} часов" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Каждый {n} час" msgstr[1] "Каждые {n} часа" msgstr[2] "Каждые {n} часов" #: qt/settingsdialog.py:457 #, fuzzy msgid "Custom hours" msgstr "Другой период (в часах)" #: qt/settingsdialog.py:458 #, fuzzy msgid "Every day" msgstr "Каждый день" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Периодически (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "При подключении носителя (udev)" #: qt/settingsdialog.py:461 #, fuzzy msgid "Every week" msgstr "Каждую неделю" #: qt/settingsdialog.py:462 #, fuzzy msgid "Every month" msgstr "Каждый месяц" #: qt/settingsdialog.py:463 #, fuzzy msgid "Every year" msgstr "Ежегодно" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Число:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "День недели:" #: qt/settingsdialog.py:495 #, fuzzy msgid "Hour:" msgstr "Час" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Часа:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Многократный запуск программы Back In Time. Это удобно если компьютер " "работает нерегулярно." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Каждые:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Час(ов)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Дней (Дня)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Недель" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Месяцев" #: qt/settingsdialog.py:555 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 "" "Запускать Back In Time как только устройство подключено (только раз в несколько дней).\n" "Будет запрошен sudo-пароль." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Включить" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Включить файлы и папки" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Добавить файл" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Добавить каталог" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Исключить" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Исключить шаблоны, файлы или папки" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Добавить по умолчанию" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Исключить файлы размером более:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Исключить файлы размером более: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Исключить файлы, превышающие значение в %(prefix)s.\n" "При отключенном 'Full rsync mode' это будет влиять только на новые файлы\n" "поскольку для rsync это опция передачи, а не исключения.\n" "Таким образом, большие файлы, резервные копии которых были созданы ранее, останутся в снимках\n" "даже если они изменились." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Автоудаление" #: qt/settingsdialog.py:726 #, fuzzy msgid "Older than:" msgstr "Старше чем" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Лет (Года)" #: qt/settingsdialog.py:748 #, fuzzy msgid "If free space is less than:" msgstr "Если свободного места меньше, чем" #: qt/settingsdialog.py:768 #, fuzzy msgid "If free inodes is less than:" msgstr "Если inode меньше, чем" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Умное удаление:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Запустить в фоновом режиме на удаленном хосте." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "ЭКСПЕРИМЕНТАЛЬНО" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Хранить все копии за последние" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 #, fuzzy msgid "day(s)." msgstr "дней." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Хранить дневную копию за последние" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Хранить недельную копию за последние" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "неделя(ль)." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Хранить месячную копию за последние" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "месяц(ев)." #: qt/settingsdialog.py:828 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Хранить один снимок в год за все время." #: qt/settingsdialog.py:837 #, fuzzy msgid "Don't remove named snapshots." msgstr "Не удалять переименованные снимки." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Параметры" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Включить уведомления" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Отключить создание резервных копий при работе от батареи" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Статус питания недоступен" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Одновременный запуск только одного снимка" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Другие снимки будут блокироваться до тех пор, пока не завершится текущий снимок.\n" "Это глобальная опция. Поэтому она будет влиять на все профили данного пользователя.\n" "Но необходимо активировать ее и для всех остальных пользователей." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Резервное копирование замененных файлов при восстановлении" #: qt/settingsdialog.py:879 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Более новые версии файлов перед восстановлением будут переименованы с трейлингом {suffix}.\n" "Если они вам больше не нужны, то можете удалить их с помощью команды {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Игнорировать ошибки (сохранять незавершенные копии)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Использовать контрольную сумму для обнаружения изменений" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Сделать новый снимок независимо от наличия изменений." #: qt/settingsdialog.py:906 #, fuzzy msgid "Log Level:" msgstr "Уровень логирования" #: qt/settingsdialog.py:911 msgid "None" msgstr "Нет" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Р&асширенные параметры" #: qt/settingsdialog.py:936 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Изменяйте эти параметры, только если вы точно знаете, что делаете." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Запустить 'rsync' с '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "как задача cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "на удаленном сервере" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "при создании снимков вручную" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Чтобы включить эту опцию, установите 'nocache')" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "на локальной машине" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Перенаправление stdout в /dev/null в cronjobs." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Перенаправление stderr в /dev/null в заданиях cronjobs." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "Ограничение использования полосы пропускания rsync" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "КБ/сек" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Сохранять ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Сохранять дополнительные атрибуты (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Копировать небезопасные ссылки (работает только с абсолютными ссылками)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Параметры должны быть заключены в кавычки, например: {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Вставьте дополнительные опции в rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Добавить префикс для SSH команд" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Префикс для запуска перед каждой командой на удаленном хосте.\n" "Переменные должны быть экранированы с помощью \\$FOO.\n" "Это не касается rsync. Поэтому для добавления префикса\n" "для rsync используйте \"%(cbRsyncOptions)s\" с\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "по умолчанию" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Проверка наличия удаленного узла в сети" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Предупреждение: если отключено и удаленный хост\n" "недоступен, это может привести к возникновению\n" "странных ошибок." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Проверьте, поддерживает ли удаленный хост все необходимые команды." #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Внимание: если команда отключена и удаленный хост\n" "не поддерживает все необходимые команды,\n" "это может привести к возникновению странных ошибок." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Восстановить конфигурацию" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Редактирование обратного вызова пользователя" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Новый профиль" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Переименовать профиль" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Вы действительно хотите удалить профиль \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Настоятельно рекомендуется{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Вы не выбрали файл приватного ключа для SSH.\n" "Хотите ли вы сгенерировать новую пару открытый/приватный ключ без пароля?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Файл приватного ключа \"{file}\" не существует." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Вы хотите скопировать свой открытый SSH-ключ на удаленный \n" "узел, чтобы обеспечить вход в систему без пароля?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Подлинность хоста {host} не может быть установлена." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Пожалуйста, проверьте этот отпечаток! Вы хотите добавить его в файл " "'known_hosts'?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Исключать папку по шаблону" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Исключая файл" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Исключить каталог" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Включить файл" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" - это символическая ссылка. Резервное копирование связанной цели не будет выполняться до тех пор, пока вы не включите и ее.\n" "Хотите ли вы включить вместо этого цель по символической ссылке?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Включить каталог" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Вы уверены, что хотите изменить каталог для хранения резервных копий?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Не удалось создать новый SSH-ключ в {path}" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "по умолчанию" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "отключено" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "включено" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Не найдено конфигурации" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Параметры сравнения снимков" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Комманда" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters:" msgstr "Параметры" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Использовать %1 и %2 для комманды" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Только различающиеся снимки" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Перечислить только одинаковые снимки: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Глубокая проверка (более точная, но медленная)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Удалить" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Выделить всё" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Сравнение" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Перейти к" #: qt/snapshotsdialog.py:204 #, fuzzy msgid "Options" msgstr "Параметры" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Нельзя сравнивать резервную копию саму с собой." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Вы действительно хотите удалить файл {file} в снимке {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Вы действительно хотите удалить \"{file}\" в {count} снимках?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "ВНИМАНИЕ: Это не может быть отменено." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Исключить {path} из будущих снимков?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Невозможно найти crontab\n" #~ "Вы уверены, что cron установлен?\n" #~ "Если не установлен, то вы должны отключить автоматическое резервное копирование." #~ msgid "Full snapshot path" #~ msgstr "Полный путь к снимку" #~ msgid "Mode" #~ msgstr "Режим" #~ msgid "Profile" #~ msgstr "Профиль" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Профиль '{profile}': Введите пароль для {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "" #~ "Shebang в пользовательском сценарии обратного вызова не является " #~ "исполняемым." #~ msgid "WARNING" #~ msgstr "ПРЕДУПРЕЖДЕНИЕ" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs версии ≤ 1.7.2 имеют баг с опцией --reverse. Пожалуйста, обновите " #~ "encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "в скрипте user-callback отсутствует строка shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "В приложении {app} для шифрования используется EncFS. Недавний аудит " #~ "безопасности выявил несколько возможных векторов атаки. Пожалуйста, обратите" #~ " внимание на \"A NOTE ON SECURITY\" в \"man backintime\"." backintime-1.5.2/common/po/sk.po000066400000000000000000001271311465446530500165310ustar00rootroot00000000000000# Slovak translation for backintime # Copyright (c) 2009 Rosetta Contributors and Canonical Ltd 2009 # This file is distributed under the same license as the backintime package. # Tomáš Vadina , 2009. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \n" "Language-Team: Slovak \n" "Language: sk\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) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" "X-Poedit-Country: SLOVAKIA\n" "X-Poedit-Language: Slovak\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Varovanie" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Hlavný profil" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "šifrovaný" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "šifrovanie" #: common/config.py:309 msgid "Local" msgstr "Lokálny" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH privátny kľúč" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "šifrovaný" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Šifrovanie" #: common/config.py:320 msgid "SSH encrypted" msgstr "šifrovanie" #: common/config.py:327 msgid "Default" msgstr "Predvolené" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Neplatný priečinok na snímky!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Musíte vybrať aspoň jeden priečinok na zálohovanie!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Priečinok Zálohy nemože byť zahrnutý." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Pod-priečinok Zálohy nemože byť zahrnutý." #: common/config.py:447 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} nie je priečinok." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Hosť/Používateľ/Profil musia byť vyplnené." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nie je možné zapisovať do: {path}\n" "Určite máte právo na zápis ?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Cieľový systém súborov pre {path} je formatovaný na FAT, ktorý nepodporuje " "tvrdé odkazy. Použijte prosím natívny systém súborov pre Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Cieľový systém súborov for {path} je zdieľaný cez SMB. Uistite sa prosím, že" " vzdialený SMB server podporuje symbolické odkazy alebo aktivujte " "{copyLinks} v {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopírovať odkazy (znefunkční symbolické odkazy)" #: common/config.py:497 msgid "Expert Options" msgstr "Pokročilé nastavenia" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Cieľový systém súborov pre {path} je zdieľaný cez sshfs. sshfs nepodporuje " "pevné odkazy. Použijte prosím 'SSH'." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Zapisanie nového crontabu zlyhalo." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Uden pravidlo pre profil {profile_id} nebolo nastavené. Služba DBus pre " "'{dbus_interface}' nie je dostupná" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Pri móde {mode} nie je dostupný udev scheduler" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Nepodarilo sa nájsť UUID pre {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Nepodarilo sa uložiť konfiguráciu" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Nepodarilo sa načitať konfiguráciu" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" už existuje." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Nieje možné zmazať posledný profil." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Neviem primontovať '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Konfigurácia pre šifrovaný priečinok nebola nájdena." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Vytvoriť nový šifrovaný priečinok?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Zrušiť" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Prosím, potvrďte heslo." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Heslo sa nezhoduje." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Urobiť snímku" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Nemožem odpojiť {mountprocess} z {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "{} nenájdene. Prosím nainštaluje e.g. {}" #: common/mount.py:720 #, fuzzy, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Priečinok na pripojenie {} nieje prázdny." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "ZLYHANIE" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Obnoviť práva" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Hotovo" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Zálohovanie odložené pri behu na batériu" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Nie je možné nájsť priečinok snímok.\n" "Ak je na prenosnom médiu, prosím vložte ho." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Čakám %s sekúnd." msgstr[1] "Čakám %s sekundu." msgstr[2] "Čakám %s sekundy." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Zlyhalo vytváranie snímky {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Dokončovanie" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Nemožno vytvoriť priečinok" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Uložiť konfiguračný súbor…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Ukladám práva…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Nájdený ponechaný snímok {snapshot_id}, ktory može pokračovať." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Odstraňujem ponechaný {snapshot_id} priečinok z posledného spustenia" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Nemožno odstrániť priečinok" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Robím snímok" #: common/snapshots.py:1417 msgid "Success" msgstr "Hotovo" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Čiastočny prenos kvôli zmiznutému zdrojovému súboru (pozri 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' ukončený s kódom {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Pozri 'man rsync' pre viac detailov" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Nie je možné premenovať {new_path} na {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Šikovné odstránenie" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Odstraňujem staré snímky" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Pracujem na minimálnom voľnom mieste" #: common/snapshots.py:1929 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Snažiť sa udržať minimálne voĺné miesto" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Teraz" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:697 #, fuzzy msgid "Couldn't create remote path." msgstr "Nemožno vytvoriť priečinok." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Profil '{profile}': Zadajte heslo pre {mode}: " #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "O programe" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "" #: qt/app.py:169 msgid "Shortcuts" msgstr "Skratky" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:256 msgid "Add to Include" msgstr "" #: qt/app.py:258 msgid "Add to Exclude" msgstr "" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Nemožno vytvoriť priečinok" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Nie je možné nájsť priečinok snímok.\n" "Ak je na prenosnom médiu prosím vložte ho a potom stlačte OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Urobiť snímku" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Použiť kontrolný súčet pre zistenie zmien." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Obnoviť zoznam snímok" #: qt/app.py:508 msgid "Name snapshot" msgstr "Urobiť snímku" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Odstrániť snímku" #: qt/app.py:516 msgid "View snapshot log" msgstr "Zobraziť záznam snímok" #: qt/app.py:520 msgid "View last log" msgstr "Zobraziť posledný log" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Hlavný profil…" #: qt/app.py:528 msgid "Shutdown" msgstr "" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:532 msgid "Setup language…" msgstr "" #: qt/app.py:536 msgid "Exit" msgstr "Koniec" #: qt/app.py:540 msgid "Help" msgstr "Pomocník" #: qt/app.py:544 #, fuzzy msgid "Profiles config file" msgstr "Uložiť konfiguračný súbor" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Webová stránka" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "" #: qt/app.py:553 msgid "FAQ" msgstr "Často kladené otázky (FAQ)" #: qt/app.py:556 msgid "Ask a question" msgstr "" #: qt/app.py:559 msgid "Report a bug" msgstr "" #: qt/app.py:562 msgid "Translation" msgstr "" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Obnoviť" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Obnoviť …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:595 msgid "Up" msgstr "Hore" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Ukázať skryté súbory" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Urobiť snímku…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 msgid "&Restore" msgstr "&Obnoviť" #: qt/app.py:682 msgid "&Help" msgstr "&Pomocník" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Ste si istí, že chcete odstrániť snímku" #: qt/app.py:987 msgid "Working:" msgstr "Pracujem:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Hotovo, záloha nie je potrebná" #: qt/app.py:1044 msgid "Working" msgstr "Prebieha" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Chyba" #: qt/app.py:1076 msgid "Sent" msgstr "" #: qt/app.py:1077 msgid "Speed" msgstr "" #: qt/app.py:1078 msgid "ETA" msgstr "" #: qt/app.py:1140 msgid "Global" msgstr "Globálne" #: qt/app.py:1141 msgid "Root" msgstr "Koreňový priečinok" #: qt/app.py:1142 msgid "Home" msgstr "Domovský priečinok" #: qt/app.py:1170 msgid "Backup folders" msgstr "Priečinky zálohy" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Názov snímky" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Ste si istí, že chcete odstrániť snímku" msgstr[1] "Ste si istí, že chcete odstrániť snímku" msgstr[2] "Ste si istí, že chcete odstrániť snímku" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "Zlyhalo vytváranie snímky {snapshot_id} !!!" msgstr[1] "Zlyhalo vytváranie snímky {snapshot_id} !!!" msgstr[2] "Zlyhalo vytváranie snímky {snapshot_id} !!!" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Ste si istí, že chcete odstrániť snímku" msgstr[1] "Ste si istí, že chcete odstrániť snímku" msgstr[2] "Ste si istí, že chcete odstrániť snímku" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" #: qt/app.py:1750 #, fuzzy msgid "Snapshot" msgstr "Snímky" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Obnoviť {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Obnoviť {path} …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "Nastavenie jazyka sa zmení po reštartovaní aplikácie Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 msgid "System default" msgstr "" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Preložené na: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Dobrý deň,\n" "aplikáciu Back In Time v jazyku: {language} ste otvorili už viackrát.\n" "Preklad nainštalovanej verzie aplikácie Back In Time do jazyka: {language} je hotový na {perc}. Bez ohľadu na úroveň vašich technických znalostí môžete pomôcť s prekladom aplikácie, čím podporíte aj samotný projekt Back In Time.\n" "Ak chcete pomôcť, navštívte webovú stránku {translation_platform_url}. Viac informácií nájdete na webovej stránke {back_in_time_project_website}.\n" "Za vyrušenie sa ospravedlňujeme, táto správa sa už znovu nezobrazí. Toto dialógové okno nájdete kedykoľvek v menu Pomoc.\n" "Back In Time tím" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Váš preklad" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Snímky:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Všetko" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Zmeny" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Chyby" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Informácie" msgstr[1] "Informácie" msgstr[2] "Informácie" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Chyba, [I] Informácia, [C] Zmena" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Zobraziť posledný log" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Spustiť {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Prebieha…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Snímky" #: qt/qttools.py:427 msgid "Today" msgstr "Dnes" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Včera" #: qt/qttools.py:443 msgid "This week" msgstr "Tento týždeň" #: qt/qttools.py:450 msgid "Last week" msgstr "Minulý týždeň" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Posledná kontrola {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Server:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Používateľ:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Hlavný profil" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Zmeň" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Odstráň" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Hlavné" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Kam sa uložia snímky" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "SSH privátny kľúč:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Detailné nastavenie" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Plánovanie" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Zakázané" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Pri každom štarte/reštarte" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Každých {n} minút" msgstr[1] "Každých {n} minút" msgstr[2] "Každých {n} minút" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "" msgstr[1] "" msgstr[2] "" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Každých {n} hodín" msgstr[1] "Každých {n} hodín" msgstr[2] "Každých {n} hodín" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Každý deň" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Každý týždeň" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Každý mesiac" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Každý rok" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Hodina:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Hodina:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Každých:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dni" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Týždne" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:555 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:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Zahrnúť" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Zahrňuje súbory a priečinky" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Pridať súbor" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Pridať priečinok" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Vylúčiť" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Nazahrňovať vzory, súbory alebo priečinky" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Vylúčiť súbor:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "" #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Automatické odstránenie" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Staršie ako:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Roky" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Ak je voľné miesto menšie než:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Ak je voľné miesto menšie než:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Šikovné odstránenie:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Počet dní." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Týždne." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Neodstraňovať pomenované snímky." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Možnosti" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Povoliť upozornenia" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Nevytvárať snímky pokiaľ systém beží na batérie" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Nie je možné zistiť stav napájania" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Pokračovať pri chybách (zachovať nekompletné snímky)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Použiť kontrolný súčet pre zistenie zmien" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Úroveň záznamu:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Žiadne" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Možnosti e&xpertov" #: qt/settingsdialog.py:936 #, fuzzy msgid "" "Caution: 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/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Zachovať ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Zachovať rozšírené atribúty (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopírovať nebezpečné odkazy (funguje len s absolútnymi odkazmi)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, 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/settingsdialog.py:1196 msgid "default" msgstr "" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nový profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Premenovať profil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Určite chcete vymazať profil \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Vysoko odporúčané" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Vylúčiť vzor" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Vylúčiť súbor" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Vylúčiť priečinok" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Zahrnúť súbor" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Zahrnúť priečinok" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Ste si istí, že chcete zmeniť priečinok snímok?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Zapisanie nového crontabu zlyhalo." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "Predvolené" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Príkaz" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametre:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Použiť %1 a %2 parametre cesty" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Hĺbková kontrola (presnejšia, ale pomalšia)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Vymazať" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Porovnať" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Ísť na" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Možnosti" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Nemôžete porovnávať snímku s ňou samou." #: qt/snapshotsdialog.py:398 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Zlyhalo vytváranie snímky {snapshot_id} !!!" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Nemožno nájsť crontab.\n" #~ "Ste si istí, že je nainštalovaný ?\n" #~ "Ak nie, mali by ste vypnúť všetky automatické zálohovania." #~ msgid "Profile" #~ msgstr "Profil" #~ msgid "WARNING" #~ msgstr "UPOZORNENIE" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs verzia 1.7.2 a staršia, ma chybu pri používani voľby --reverse. " #~ "Prosím, aktualizuje si encfs." backintime-1.5.2/common/po/sl.po000066400000000000000000001512041465446530500165300ustar00rootroot00000000000000# msgid "" msgstr "" "Project-Id-Version: Back In Time 0.9.10\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-02 13:39+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-Generator: Weblate 5.6.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" "X-Poedit-Country: SLOVENIA\n" "X-Poedit-Language: Slovenian\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Opozorilo" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Glavni profil" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "Lokalno šifrirano" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH šifrirano" #: common/config.py:309 msgid "Local" msgstr "Lokalno" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Privatni ključ SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Lokalno šifrirano" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Šifriranje" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH šifrirano" #: common/config.py:327 msgid "Default" msgstr "Privzeto" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Mapa s posnetki ni veljavna!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Izbrati morate vsaj eno mapo, da lahko ustvarite varnostno kopijo!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Mape za varnostne kopije ni mogoče vključiti." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Podmape za varnostne kopije ni mogoče vključiti." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Neveljavna možnost. {path} ni mapa." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID ne sme biti prazen." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Ni mogoče pisati v: {path}\n" "Ali ste prepričani, da imate dostop za pisanje?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native 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:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. 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:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopiraj povezave (dereferenciraj simbolne povezave)" #: common/config.py:497 msgid "Expert Options" msgstr "Napredne možnosti" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. 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:1658 msgid "Failed to write new crontab." msgstr "Novega crontaba ni mogoče zapisati." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Razpored udev ne deluje z načinom {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Ni bilo mogoče najti UUID za {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Konfiguracije ni bilo mogoče shraniti" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Konfiguracije ni bilo mogoče naložiti" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" že obstaja." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Zadnjega profila ni mogoče odstraniti." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Ni mogoče namestiti '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Ni mogoče najti konfiguracije za šifrirano mapo." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Želite ustvariti novo šifrirano mapo?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Prekliči" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Prosim potrdite geslo." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Geslo se ne ujema." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Ustvari posnetek" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "{mountprocess} ni bilo mogoče odklopiti iz {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} ni mogoče najti. Namestite ga prosim (npr. z \"{installcommand}\"" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Točka priklopa {mntpoint} ni prazna." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Vpišite geslo za profil {mode} \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "SPODLETELO" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Obnovi dovoljenja" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Končano" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Odložitev varnostnega kopiranja med delovanjem na bateriji" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Mapa posnetkov ni na razpolago.\n" "V primeru, da je na izmenljivem pogonu, ga priključite." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Čakanje %s sekund." msgstr[1] "Čakanje %s sekundo." msgstr[2] "Čakanje %s sekundi." msgstr[3] "Čakanje %s sekunde." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Posnetek {snapshot_id} ni uspel." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Zaključevanje" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Mape ni mogoče ustvariti" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Shranjevanje konfiguracijske datoteke…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Shranjevanje dovoljenj…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Najden ostanek {snapshot_id}, ki ga je mogoče nadaljevati." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Odstranjevanje ostankov mape {snapshot_id} od zadnjega zagona" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Mape ni mogoče odstraniti" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Ustvarjanje posnetka" #: common/snapshots.py:1417 msgid "Success" msgstr "Uspeh" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Delni prenos zaradi napake" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Delni prenos zaradi izginulih izvornih datotek (glej 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' se je končal z izhodno kodo {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Za več informacij glej 'man rsync'" #: common/snapshots.py:1445 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/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Nič se ni spremenilo, nov posnetek ni potreben" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{new_path} ni mogoče preimenovati v {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Pametno odstranjevanje" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Odstranjevanje starih posnetkov" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Poskus ohranjanja minimalnega prostora" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Poskus ohranjanja najmanj {perc} prostih inodov" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Zdaj" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs} ni bilo mogoče namestiti" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent ni bil najden. Prepričajte se, da je nameščen." #: common/sshtools.py:444 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:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Šifra {cipher} ni uspela za {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Oddaljena pot obstaja, ampak ni mapa." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Oddaljena pot ni zapisljiva." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Oddaljena pot ni izvršljiva." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Oddaljene poti ni bilo mogoče ustvariti." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Oddaljeni gostitelj {host} ne podpira {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Za več navodil, glej 'man backintime'" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Ukazi za preverjanje na gostitelju {host} so vrnili neznano napako" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Oddaljeni gostitelj {host} ne podpira trdih povezav" #: common/sshtools.py:1164 #, 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:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Prosim vpišite geslo za \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "O programu" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Avtorji" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Prevodi" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licenca" #: qt/app.py:169 msgid "Shortcuts" msgstr "Bližnjice" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Ta mapa ne obstaja\n" "v trenutno izbranem posnetku." #: qt/app.py:256 msgid "Add to Include" msgstr "Dodaj v Vključi" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Dodaj v Izključi" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "Zgleda, da ste prvič zagnali {app_name}, ker ni mogoče najti konfiguracije." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Uvozi obstoječo konfiguracijo (iz imenika varnostne kopije ali iz drugega " "računalnika)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Imenik za posnetke ni na razpolago." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "Če je na izmenljivem pogonu, ga priključite in pritisnite OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Ustvari posnetek" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Za zaznavanje datotečnih sprememb uporabite čas in velikost." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Ustvari posnetek (način checksum)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Za zaznavanje datotečnih sprememb uporabite checksum." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Začasno ustavi postopek posnetka" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Nadaljuj postopek posnetka" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Ustavi postopek posnetka" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Osveži seznam posnetkov" #: qt/app.py:508 msgid "Name snapshot" msgstr "Imenuj posnetek" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Odstrani posnetek" #: qt/app.py:516 msgid "View snapshot log" msgstr "Ogled dnevnika posnetkov" #: qt/app.py:520 msgid "View last log" msgstr "Ogled zadnjega dnevnika" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Upravljanje profilov…" #: qt/app.py:528 msgid "Shutdown" msgstr "Zaustavi" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Zaustavite sistem, ko je posnetek končan." #: qt/app.py:532 msgid "Setup language…" msgstr "Nastavitev jezika…" #: qt/app.py:536 msgid "Exit" msgstr "Izhod" #: qt/app.py:540 msgid "Help" msgstr "Pomoč" #: qt/app.py:544 msgid "Profiles config file" msgstr "Konfiguracijska datoteka profilov" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Spletna stran" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Dnevnik sprememb" #: qt/app.py:553 msgid "FAQ" msgstr "Pogosta vprašanja" #: qt/app.py:556 msgid "Ask a question" msgstr "Postavite vprašanje" #: qt/app.py:559 msgid "Report a bug" msgstr "Prijavite napako" #: qt/app.py:562 msgid "Translation" msgstr "Prevod" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Obnovi" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Obnovi izbrane datoteke ali mape v prvotni cilj." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Obnovi v …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Obnovi izbrane datoteke ali mape v nov cilj." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "Obnovi trenutno prikazano mapo in vso njeno vsebino v prvotni cilj." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Obnovi trenutno prikazano mapo in vso njeno vsebino v nov cilj." #: qt/app.py:595 msgid "Up" msgstr "Gor" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Pokaži skrite datoteke" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Primerjava posnetkov…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&VarnostnaKopija" #: qt/app.py:676 msgid "&Restore" msgstr "&Obnovi" #: qt/app.py:682 msgid "&Help" msgstr "&Pomoč" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Če zaprete to okno, Back In Time ne bo mogel ugasniti vašega sistema, ko se " "posnetek stanja zaključi." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Ali res želite zapreti okno?" #: qt/app.py:987 msgid "Working:" msgstr "V obdelavi:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Narejeno, varnostna kopija ni potrebna" #: qt/app.py:1044 msgid "Working" msgstr "V obdelavi" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Napaka" #: qt/app.py:1076 msgid "Sent" msgstr "Poslano" #: qt/app.py:1077 msgid "Speed" msgstr "Hitrost" #: qt/app.py:1078 msgid "ETA" msgstr "Predvideni čas" #: qt/app.py:1140 msgid "Global" msgstr "Splošno" #: qt/app.py:1141 msgid "Root" msgstr "Koren" #: qt/app.py:1142 msgid "Home" msgstr "Domov" #: qt/app.py:1170 msgid "Backup folders" msgstr "Mape za varnostno kopijo" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Ime posnetka" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Ali res želite odstraniti te posneteke?" msgstr[1] "Ali res želite odstraniti ta posnetek?" msgstr[2] "Ali res želite odstraniti ta posnetka?" msgstr[3] "Ali res želite odstraniti te posnetke?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Preden prepišete ali odstranite lokalne datoteke,\n" "ustvarite varnostne kopije s pripono {suffix}." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them 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 ukazom {cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Obnovi le datoteke, ki ne obstajajo ali\n" "so novejše od tistih na ciljni lokaciji.\n" "Uporablja možnost \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Odstrani novejše datoteke v prvotni mapi." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." 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/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Ali res želite obnoviti te elemente v novo mapo\n" "{path}?" msgstr[1] "" "Ali res želite obnoviti ta element v novo mapo\n" "{path}?" msgstr[2] "" "Ali res želite obnoviti ta elementa v novo mapo\n" "{path}?" msgstr[3] "" "Ali res želite obnoviti te elemente v novo mapo\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to 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/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Ali res želite zbrisati vse novejše datoteke v {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Ali res želite odstraniti vse novejše datoteke v svoji originalni mapi?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}POZOR{BOLDEND}: Brisanje datotek v korenskem datotečnem sistemu lahko " "pokvari ves sistem." #: qt/app.py:1750 msgid "Snapshot" msgstr "Posnetek" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Obnovi {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Obnovi {path} v …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "Nastavitve jezika se uveljavijo po ponovnem zagonu Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 #, fuzzy msgid "Your Back In Time Team" msgstr "Back In &Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Nastavitev jezika" #: qt/languagedialog.py:92 msgid "System default" msgstr "Sistemsko privzeto" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Uporabi jezik operacijskega sistema." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Prevedeno: {percent}" #: qt/languagedialog.py:188 #, 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/languagedialog.py:217 msgid "translation platform" msgstr "prevajalna platforma" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Vaš prevod" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Ogled zadnjega dnevnika" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Ogled dnevnika posnetkov" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Posnetki:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Vse" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Spremembe" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Napake" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informacij" msgstr[1] "Informacija" msgstr[2] "Informaciji" msgstr[3] "Informacije" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "Neuspešni prenosi rsync (eksperimentalno)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Napake, [I] Informacije, [C] Spremembe" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "dešifriranje poti" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Vprašanje" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Ogled zadnjega dnevnika" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Zaženi {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "V obdelavi…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Poslano:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Hitrost:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Posnetki" #: qt/qttools.py:427 msgid "Today" msgstr "Danes" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Včeraj" #: qt/qttools.py:443 msgid "This week" msgstr "Ta teden" #: qt/qttools.py:450 msgid "Last week" msgstr "Prejšnji teden" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "To NI posnetek, ampak ogled vaših trenutnih lokalnih datotek" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Zadnj pregled {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Prikaži celoten dnevnik" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH Proxy" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Gostitelj:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Vrata:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Uporabnik:" #: qt/settingsdialog.py:120 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 "" "Poveži se do gostitelja preko tega strežnika (imenovan tudi jump host). " "Preberite podrobnosti glede \"-J\" v dokumentaciji ukaza \"ssh\" ali " "\"ProxyJump\" v strani priročnika man za \"ssh_config\"." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Upravljanje profilov" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Uredi" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Dodaj" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Odstrani" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Splošno" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Način:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Kam naj se shranijo posnetki" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Mapa" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Nastavitve SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Pot:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Šifriranje:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Zasebni ključ:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Izberite obstoječo datoteko z zasebnim ključem (običajno imenovano " "\"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Ustvari nov ključ SSH brez gesla (ni dovoljeno, če je datoteka z zasebnim " "ključem že izbrana)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Geslo" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Shrani geslo v zbirko ključev" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Začasno shrani geslo za Cron (Varnostno tveganje: root uporabnik lahko " "prebere geslo)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Napredno" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Celotna pot posnetka:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Časovni razpored" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Onemogočeno" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Ob vsakem zagonu/ponovnem zagonu" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} 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/settingsdialog.py:448 #, 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/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Vsakih {n} ur" msgstr[1] "Vsako {n} uro" msgstr[2] "Vsake {n} ure" msgstr[3] "Vsake {n} ure" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Ure po meri" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Vsak dan" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Ponavljajoče (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Ko se priključi disk (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Vsak teden" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Vsak mesec" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Vsako leto" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Dan:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Dan v tednu:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Ura:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Ur:" #: qt/settingsdialog.py:521 msgid "" "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/settingsdialog.py:528 msgid "Every:" msgstr "Vsakih:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Ura/ur" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dan/dni" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Teden/tednov" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Mesec/mesecev" #: qt/settingsdialog.py:555 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 "" "Zaženi Back In Time takoj, ko se disk poveže (zgolj enkrat na X dni).\n" "Od vas bo zahtevan vpis korenskega gesla." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Omogoči beleženje razhroščevalnih sporočil" #: qt/settingsdialog.py:568 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/settingsdialog.py:570 msgid "" "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/settingsdialog.py:583 msgid "&Include" msgstr "&Vključitev" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Vključi datoteke in mape" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Dodaj datoteko" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Dodaj mapo" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Izključitev" #: qt/settingsdialog.py:625 #, 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}: V načinu 'SSH Šifrirano' so delujoče zamo enojne ali " "dvojne zvezdice (npr. {example2}). Drugi tipi znakov in vzorcev bodo " "ignorirani (npr. {example1}). Imena datotek so v tem načinu nepredvidljiva " "zaradi šifriranja EncFS." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Izključi vzorce, datoteke ali mape" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Dodaj privzeto" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Izključi datoteke večje kot:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Izključi datoteke večje kot {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Če je 'Full rsync mode' izklopljen, bo to vplivalo le na nove datoteke, ker " "je za rsync to prenosna opcija, ne izjema. Tako bodo velike datoteke, ki so " "bile varnostno kopiranje že prej, ostale v posnetkih, tudi če so se " "spremenile." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Samo-&odstrani" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Starejše kot:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Leto/Let" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Če je prostega prostora manj od:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Če je prostih inod manj kot:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Pametno odstranjevanje:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Zagon v ozadju na oddaljenem gostitelju." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTALNO" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Obdrži vse posnetke za zadnjih" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dan/dni." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Obdrži en posnetek na dan za zadnjih" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Obdrži en posnetek na teden za zadnjih" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "teden/tednov." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Obdrži en posnetek na mesec za zadnjih" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mesec/mesecev." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Obdrži en posnetek na leto za vsa leta." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ne odstranjuj posnetkov z imenom." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Možnosti" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Omogoči prikaz obvestil" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Med porabo baterije onemogoči zajemanje posnetkov" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Stanje o porabi energije ni na voljo" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Poženi le en zajem posnetka naenkrat" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Drugi zajemi posnetkov bodo blokirani do konca trenutnega zajema. To je " "globalna opcija, zato bo vplivala na vse profile tega uporabnika. Toda to " "možnost morate vklopiti tudi za druge uporabnike." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Pri obnovitvi naredi kopijo zamenjanih datotek" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Imenom novejših različic datotek bo pred obnovitvijo dodana pripona " "{suffix}. Če jih ne potrebujete več, jih lahko odstranite z ukazom {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Nadaljuj klub napakam (obdrži nepopolne posnetke)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Za zaznavanje sprememb uporabite checksum" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Ustvari nov posnetek, tudi če ni bilo sprememb." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Nivo beleženja:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Brez" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "&Napredne nastavitve" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "POZOR: Spremenite te nastavitve le, če res veste, kaj počnete." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Poženi 'rsync' z ukazom '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "kot nalogo cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "na oddaljenem gostitelju" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "pri ročnem ustvarjanju posnetka" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Da lahko omogočite to možnost morate namestiti 'nocache')" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "na lokalnem računalniku" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "V periodičnih opravilih preusmeri stdout na /dev/null." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Če je MTA nameščen, bo Cron samodejno poslal e-pošto s priloženimi cronjobi." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "V periodičnih opravilih preusmeri stderr na /dev/null." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Če je MTA nameščen, bo Cron samodejno poslal e-pošto s priloženimi napakami " "cronjobov." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Omeji pasovno širino za rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Ohrani ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Ohrani razširjene atribute (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopiraj ne varne povezave (deluje samo z absolutnimi povezavami)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Omejitev na en datotečni sistem" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Možnosti morajo biti v narekovajih, npr. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Prilepite dodatne možnosti za rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Dodaj predpono ukazom SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Predpona za zagon pred vsemi ukazi na oddaljenem gostitelju." #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Spremenljivke je treba ubežati z \\$FOO. To se ne dotika rsync. Za dodajanje" " predpone za rsync uporabite \"{example_value}\" z {rsync_options_value}" #: qt/settingsdialog.py:1196 msgid "default" msgstr "privzeto" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Preveri ali je oddaljeni gostitelj povezan" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Opozorilo: v primeru, da je ta možnost onemogočena in oddaljeni gostitelj ni" " dosegljiv se lahko pojavijo nenavadne napake." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Preverite ali oddaljeni gostitelj podpira vse potrebne ukaze." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Opozorilo: v primeru, da je ta možnost onemogočena in oddaljeni gostitelj ne" " podpira vseh potrebnih ukazov, se lahko pojavijo nenavadne napake." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Obnovi konfiguracijo" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Uredi user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Nov profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Preimenuj profil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Ali ste prepričani, da želite izbrisati profil \"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Močno priporočljivo{ENDBOLD}: (Vsa priporočila so že vključena.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Zelo priporočljivo{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "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/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Niste izbrali zasebnega ključa za SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "Ali želite ustvariti nov par javnih/zasebnih ključev brez gesla?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Datoteka z zasebnim ključem \"{file}\" ne obstaja." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Ali bi želeli kopirati svoj javni SSH ključ na oddaljenega gostitelja za " "vključitev brezgeselne prijave?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Pristnosti gostitelja {host} ni bilo mogoče vspostaviti." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "Prstni odtis ključa {keytype} je:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Prosimo, preverite ta prstni odtis! Ali bi ga želeli dodati v vašo " "'known_hosts' datoteko?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Izključitveni vzorec" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Izključi datoteko" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Izključi mapo" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Vključi datoteko" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" je symlink. Povezani cilj ne bo varnostno kopiran, dokler ga ne vključite.\n" "Ali namesto tega želite vključiti cilj symlinka?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Vključi mapo" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Ali res želite zamenjati mapo s posnetki?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Novega SSH ključa v {path} ni bilo mogoče ustvariti" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "Onemogočeno, ker ta vzorec ni delujoč v načinu 'SSH Šifrirano'." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(privzeto: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "onemogočeno" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "omogočeno" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Uvoz konfiguracije" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Konfiguracija ni najdena" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Uvoz" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Izberite mapo s posnetki, iz katere bo uvožena konfiguracijska datoteka. Pot" " lahko izgleda tako: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Če se mapa nahaja na zunanjem ali oddaljenem disku, se mora predhodno " "vmestiti." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Nastavitve primerjave posnetkov" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Ukaz:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametri:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Uporabi %1 in %2 kot vrednosti za pot" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Prosimo nastavite diff ukaz ali izberite Prekliči." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "Ukaz \"{cmd}\" ni bil našen na tem sistemu. Prosimo poskusite nekaj drugega " "ali izberite Prekliči." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Ni nastavljenih parametrov za diff ukaz. Uporabljena bo privzeta vrednost " "\"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Samo različni posnetki" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Prikaži samo enake posnetke za: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Globinsko preverjanje (bolj natančno, a počasno)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Izbriši" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Izberi vse" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Primerjaj" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Pojdi na" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Nastavitve" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Posnetka ni mogoče primerjati s samim sabo." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Ali res želite odstraniti {file} v {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Ali res želite odstraniti {file} v {count} posnetkov?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "POZOR: Tega ni mogoče razveljaviti." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Želite izključiti {path} iz prihodnjih posnetkov?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Crontab ni mogoče najti. Ste prepričani, da je cron nameščen? V primeru, da " #~ "ni, morate onemogočiti vse samodejne varnostne kopije." #~ msgid "Full snapshot path" #~ msgstr "Cela pot posnetka" #~ msgid "Mode" #~ msgstr "Način" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil '{profile}': Vnesite geslo za {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang v user-callback skripti ni zagonska datoteka." #~ msgid "WARNING" #~ msgstr "POZOR" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs verzije 1.7.2 in prej, imajo napako z opcijo --reverse. Prosimo, da " #~ "posodobite encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "user-callback skripta nima shebang (#!/bin/sh) vrstice." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} za šifriranje uporablja EncFS. Nedavni varnostni pregled je za EncFS " #~ "razkril več različnih ranljivosti. Prosimo, preberite si razdelek \"A NOTE " #~ "ON SECURITY\" v izpisu ukaza \"man backintime\"." backintime-1.5.2/common/po/sr.po000066400000000000000000001605151465446530500165430ustar00rootroot00000000000000# Serbian 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-27 16:54+0000\n" "Last-Translator: nvedran \n" "Language-Team: Serbian \n" "Language: sr\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.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Upozorenje" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Glavni profil" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Lokalno (EncFS šifrovano)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (EncFS šifrovano)" #: common/config.py:309 msgid "Local" msgstr "Lokalno" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH privatni ključ" #: common/config.py:314 msgid "Local encrypted" msgstr "Lokalno šifrovano" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Enkripcija" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH enkriptovano" #: common/config.py:327 msgid "Default" msgstr "Podrazumevano" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Direktorijum za snimak nije važeći!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "" "Morate odabrati bar jedan direktorijum da bi napravili rezervnu kopiju!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Direktorijum za rezervne kopije ne može biti uključen." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Pod-direktorijum za rezervne kopije ne moze biti uključen." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Nevažeća postavka. {path} nije direktorijum." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/Korisnik/ID-Profila moraju biti popunjeni." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Ne mogu da upisujem u: {path}\n" "Da li ste sigurni da imate dozvolu za upisivanje?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Odredište sistema datoteka za {path} je formatiran sa FAT koji ne podržava " "čvrste veze. Molim Vas da koristite sistemske datoteke koje Linuks izvorno " "podržava." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Destinacija sistema datoteke za {path} je SMB-montirano deljeno mesto. Molim" " Vas da potvrdite da udaljeni SMB server podržava symlinks ili uključite " "{copyLinks} u {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopiraj linkove (prati simbolične linkove)" #: common/config.py:497 msgid "Expert Options" msgstr "Napredne Postavke" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Odredište sistema datoteka za {path} je sshfs-montirano deljeno mesto. Sshfs" " ne podržava čvrste veze. Molimo vas da koristite 'SSH' umesto toga." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Неуспешно чување новог crontab-а." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Pozadinski proces (cron) se ne pokreće, uprkos tome što je komanda dostupna " "(crontab command). Snimanje rezervnih kopija koje su zakazane se neće " "pokrenuti. Pozadinski proces (cron) je možda instaliran ali nije omogućen. " "Isprobajte komandu \"systemctl enable cron\" ili kontaktirajte podršku za " "GNU Linux distribuciju." #: common/config.py:1746 #, 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/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Raspored udev ne radi sa režimom {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Nije moguće naći UUID za {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Неуспешно чување конфигурације" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Неуспешно учитавање конфигурације" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Профил \"{name}\" већ постоји." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Poslednji profil se ne može ukloniti." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Неуспешно монтирање '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Подешавања за енкриптовани фолдер нису пронађена." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Направите нови енкриптовани фолдер?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Otkazati" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Молимо потврдите лозинку." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Lozinka se ne poklapa." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Направи снимак" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Nije moguće demontirati {mountprocess} iz {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "{command} nije pronađen. Instalirajte npr. {installcommand}" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Tačka montiranja {mntpoint} nije prazna." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Unesi šifru za {mode} profil \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "НЕУСПЕШНО" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Поврати дозволе" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Готово" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Одлагање backup-а док је уређај на батерији" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Nije moguće pronaći direktorijum za snimke.\n" "Ako je na prenosivom disku, molim vas da ga priključite." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Чекамо %s секунд." msgstr[1] "Чекамо %s секунде." msgstr[2] "Чекамо %s секунди." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Neuspešno čuvanje snimka {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Завршавање" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Неуспешно креирање фолдера" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Чување конфигурационог фајла…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Чување дозвола…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Пронађен преостали {snapshot_id} који може бити настављен." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Уклањање преосталог {snapshot_id} фолдера од прошлог покретања" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Неуспешно уклањање фолдера" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Прављење снимка" #: common/snapshots.py:1417 msgid "Success" msgstr "Uspeh" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Delimičan prenos zbog greške" #: common/snapshots.py:1421 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:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' se završio sa izlaznim kodom {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Više detalja možete naći na 'man rsync'" #: common/snapshots.py:1445 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:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Нишата није промењено, није потребан нови снимак" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Неуспешно преименовање {new_path} у {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Pametno uklanjanje" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Уклањање старог снимка" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Покушавамо сачувати минимални слободан простор" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Pokušavamo ostvariti minimum {perc} slobodnih inode-ova" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Сада" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Nemoguće montirati {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent nije pronađen. Molim vas da se uvjerite da je instaliran." #: common/sshtools.py:444 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:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Šifrovanje {cipher} nije uspelo za {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Udaljena putanja postoji, ali nije direktorijum." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Udaljena putanja nije dostupan za pisanje." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Udaljena putanja nije izvršna." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Nije moguće kreirati udaljenu putanju." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Udaljeni domaćin {host} ne podržava {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Pogledajte 'man backintime' za dalja uputstva" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Komande provjere na domaćinu {host} su vratile nepoznatu grešku" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Udaljeni domaćin {host} ne podržava tvrde veze" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Копирај јавни ssh кључ \"{pubkey}\" на удаљени хост \"{host}\"." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Молим вас унесите лозинку за \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "О апликацији" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Autori" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Prevodi" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licenca" #: qt/app.py:169 msgid "Shortcuts" msgstr "Пречице" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Ovaj folder ne postoji\n" "u trenutno izabranom snimku." #: qt/app.py:256 msgid "Add to Include" msgstr "Додај у листу инклузија" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Додај у Изузетке" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "Izgleda da se {app_name} pokreće prvi put pošto konfiguracija nije " "pronađena." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Uvezite postojeću konfiguraciju (iz direktorijum sa rezervnim kopijama ili " "sa drugog računara)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Ne mogu da pronađem direktorijum sa snimcima." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Ako se nalazi na prenosivom disku, molim vas priključite ga i pritisnete OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Napravi snimak" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Koristi vreme i veličinu za detekciju promene datoteka." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Napravi snimak (checksum režim)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Koristi checksum-ove za detekciju promene datoteka." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Паузирај процес снимања" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Настави процес снимања" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Заустави процес снимања" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Osveži listu snimaka" #: qt/app.py:508 msgid "Name snapshot" msgstr "Imenuj snimak" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Ukloni snimak" #: qt/app.py:516 msgid "View snapshot log" msgstr "Pogledaj izveštaj snimka" #: qt/app.py:520 msgid "View last log" msgstr "Prikaži poslednji izveštaj" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Главни профил…" #: qt/app.py:528 msgid "Shutdown" msgstr "Искључивање" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Isključi sistem nakon završetka snimka." #: qt/app.py:532 msgid "Setup language…" msgstr "Podesi jezik…" #: qt/app.py:536 msgid "Exit" msgstr "Излаз" #: qt/app.py:540 msgid "Help" msgstr "Помоћ" #: qt/app.py:544 msgid "Profiles config file" msgstr "Konfiguracioni fajl profila" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Веб сајт" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Извештај промена" #: qt/app.py:553 msgid "FAQ" msgstr "Честа питања" #: qt/app.py:556 msgid "Ask a question" msgstr "Постави питање" #: qt/app.py:559 msgid "Report a bug" msgstr "Пријави грешку" #: qt/app.py:562 msgid "Translation" msgstr "Prevod" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Ponovno prikaži poruku o učestvovanju u prevodu." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Prevod enkripcije (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Ponovo prikaži poruku o uklanjanju EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Поврати" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Повратак одабраних фајлова или фолдера на њихову првобитну локацију." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Povrati na …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Повратак одабраних фајлова или фолдера на нову локацију." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Vrati trenutno prikazan folder i sav njegov sadržaj na prvobitno odredište." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Vrati trenutno prikazan folder i sav njegov sadržaj na novo odredište." #: qt/app.py:595 msgid "Up" msgstr "Горе" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Прикажи скривене фајлове" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Направи снимак…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Rezervna kopija" #: qt/app.py:676 msgid "&Restore" msgstr "Повратак" #: qt/app.py:682 msgid "&Help" msgstr "Помоћ" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot 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:821 msgid "Do you really want to close it?" msgstr "Da li sigurno želite da ga zatvorite?" #: qt/app.py:987 msgid "Working:" msgstr "Радим:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Завршено, снимање није потребно" #: qt/app.py:1044 msgid "Working" msgstr "Obrada" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Грешка" #: qt/app.py:1076 msgid "Sent" msgstr "Poslato" #: qt/app.py:1077 msgid "Speed" msgstr "Brzina" #: qt/app.py:1078 msgid "ETA" msgstr "ETA" #: qt/app.py:1140 msgid "Global" msgstr "Глобално" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Home" #: qt/app.py:1170 msgid "Backup folders" msgstr "Direktorijumi za rezervne kopije" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Име Снимка" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Da li ste sigurni da želite da uklonite ovaj snimak?" msgstr[1] "Da li ste sigurni da želite da uklonite ove snimke?" msgstr[2] "Da li ste sigurni da želite da uklonite ove snimke?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Napravite rezervne kopije sa pratećim {suffix}\n" "pre pisanja preko ili uklanjanja lokalnih elemenata." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Novije verzije fajlova će se preimenovati sa pretećim {suffix} prije " "vraćanja. Ako vam više ne trebaju, možete ih ukloniti pomoću komande:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Vratite samo elemente koji nepostoje ili\n" "su noviji od onih u odredištu.\n" "Korišćenje opcije \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Ukloni novije elemente u originalnom folderu." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Vratite odabrane datoteke ili foldere na originalno odredište i izbrišite datoteke ili foldere koji se ne nalaze u snimku. Budite izuzetno oprezni\n" "jer će ovo obrisati datoteka i foldere koji su isključeni tokom kreacije snimka." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Da li zaista želite da vratite ovaj element u novi folder\n" "{path}?" msgstr[1] "" "Da li zaista želite da vratite ove elemente u novi folder\n" "{path}?" msgstr[2] "" "Da li zaista želite da vratite ove elemente u novi folder\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to 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/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Da li ste sigurni da želite da uklonite sve novije fajlove u {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Da li ste sigurni da želite da uklonite sve novije fajlove u originalnom " "folderu?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}Upozorenje{BOLDEND}: Brisanje fajlova u root sistemu fajlova može " "slomiti vaš cijeli sistem." #: qt/app.py:1750 msgid "Snapshot" msgstr "Snimak" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Povrati {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Povrati {path} …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Jezička podešavanja stupaju na snagu tek nakon ponovnog pokretanja Back In " "Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Podrška za EncFS će biti uklonjena u doglednoj budućnosti. Osim toga ne " "preporučuje se dalje korišćenje istog za profil." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Odluka o zameni kontinuirane podrške za šifriranje sigurnosne kopije još " "uvek je na čekanju, zavisi od resursa projekta i dostupnosti saradnika. Više" " detalja dostupno je u ovim {whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "beli papiri" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Podrška za šifrovane profile snimka prolazi kroz značajne promene, i EncFS " "će biti uklonjeni u dogledno vreme." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Sledeći profil(i) koristi enkripciju sa EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Odluka o zameni kontinuirane podrške za šifriranje sigurnosne kopije još " "uvek je na čekanju, zavisi od resursa projekta i dostupnosti saradnika. " "Korisnici su pozvani da se priključe diskusiji. Ažurirani detalji o naredim " "koracima dostupni su u ovim {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Ova poruka se više neće prikazati. Ovaj dijalog je dostupan u svakom " "trenutku preko menija za pomoć." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Vaš Back In Time tim" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Podesite jezik" #: qt/languagedialog.py:92 msgid "System default" msgstr "Sistemski podrazumevano" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Koristi jezik operativnog sistema." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Prevedeno: {percent}" #: qt/languagedialog.py:188 #, 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-a у {language} је готова {perc}. Без обзира на ваш ниво техничког знања, можете помоћи преводу, и тиме помоћи Back In Time-u.\n" "Молимо вас да посетите {translation_platform_url} уколико желите да помогнете. За даљу помоћ и питања, молимо вас посетите {back_in_time_project_website}.\n" "Жао нам је што вас ометамо, а ова се порука више неће приказивати. Овај диалог је доступан у било ком тренутку у менију за помоћ.\n" "Ваш Back In Time тим" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "platforma za prevod" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Vaš prevod" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Приказ Последњег Дневника" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Приказ Дневника Снимка" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Снимци:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Све" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Измене" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Грешке" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Informacija" msgstr[1] "Informacije" msgstr[2] "Informacija" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync neuspjesi prenosa (eksperimentalno)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Грешка, [I] Информација, [C] Измена" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "декодирај путање" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Питање" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Прикажи последњи извештај" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Pokreni {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Radim…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Poslato:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Brzina:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Снимци" #: qt/qttools.py:427 msgid "Today" msgstr "Данас" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Јуче" #: qt/qttools.py:443 msgid "This week" msgstr "Ове недеље" #: qt/qttools.py:450 msgid "Last week" msgstr "Претходна седмица" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Ово НИЈЕ снимак, већ ужив приказ ваших локалних фајлова" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Zadnja provera {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Прикажи целокупни Дневник" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH Proxy" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Domaćin:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Korisnik:" #: qt/settingsdialog.py:120 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 "" "Nastavite do ženjenog hosta preko ovog proksija (poznatijeg kao jump host). " "Za detalje pogledaj \"-J\" u dokumentaciji za \"ssh\" komandu ili " "\"ProxyJump\" na \"ssh_config\" man stranici." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Upravljaj profilima" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Uredi" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Dodaj" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Ukloni" #: qt/settingsdialog.py:210 msgid "&General" msgstr "Opšt&e" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Mod:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Gde da čuvam snimke" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Direktorijum" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH Podešavanja" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Putanja:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Šifra:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Privatni Ključ:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Izaberite postojeću datoteku privatnog ključa (obično se zove \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Kreirajte novi SSH ključ bez lozinke (nije dozvoljeno ako je fajl privatnog " "ključa već odabran)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Lozinka" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Sačuvaj lozinku u Keyring" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Keširaj lozinku za Cron (Sigurnosni problem: root može čitati lozinku)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Napredno" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Puna putanja snimka:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Plan rada" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Онемогућено" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "При сваком покретању/рестарту" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Svakih {n} minut" msgstr[1] "Svakih {n} minuta" msgstr[2] "Svakih {n} minuta" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Svakih {n} sat vremena" msgstr[1] "Svakih {n} sata vremena" msgstr[2] "Svakih {n} sati vremena" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Svakih {n} sat" msgstr[1] "Svakih {n} sati" msgstr[2] "Svakih {n} sati" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Prilagođeni sati" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Svakog dana" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Учестало (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Kada se disk poveže (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Svake nedelje" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Svakog meseca" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Svake godine" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Dan:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Radni dan:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Sat:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Sati:" #: qt/settingsdialog.py:521 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/settingsdialog.py:528 msgid "Every:" msgstr "Svakih:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Сат(и)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Дан(а)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Седмица" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Месец(и)" #: qt/settingsdialog.py:555 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 "" "Pokreni Back In Time čim se disk poveže (samo jednom svakih X dana)\n" "Od vas će biti traženo da unesete svoju sudo lozinku." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Omogućite evidentiranje poruka o greškama" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "Zapiši poruke debug nivoa u sistemski log pomoću \"--debug\"." #: qt/settingsdialog.py:570 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 izlaza." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Priključi" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Uključi fajlove i direktorijume" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Dodaj fajl" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Dodaj direktorijum" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Izostavi" #: qt/settingsdialog.py:625 #, 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}: U 'SSH šifrovanom' načinu rada, mogu funkcionisati samo" " jedna ili dve zvezdica (primer {example2}). Ostali tipovi džokerskih " "znakova i obrazaca će biti ignorisani (primer {example1}). Imena datoteka su" " nepredvidiva u ovom načinu rada zbog EncFS šifrovanja." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Izostavi šablone, fajlove, ili direktorijume" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Dodaj podrazumevano" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Izostavi fajlove veće od:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Izostavi fajlove veće od {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Kada je 'Full rsync mode' onemogućen, ovo će uticati samo na nove datoteke " "jer za rsync ovo je opcija prenosa, a ne opcija isključivanja. Tako da će " "veliki fajlovi za koje je ranije napravljena rezervna kopija ostati u " "snimcima čak i ako su se promenili." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Automatsko uklanjanje" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Stariji od:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Година" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Ako je slobodan prostor manji od:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Ako je broj slobodnih inode-ova manji od:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Pametno uklanjanje:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Pokreni u pozadini na udaljenom domaćinu." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTALNO" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Zadrži sve snimke za poslednji" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "Dan(a)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Zadrži jedan snimak dnevno za poslednji" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Zadrži jedan snimak nedeljno za poslednji" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "Nedelja/e." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Zadrži jedan snimak mesečno za poslednji" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "mesec(i)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Zadrži jedan snimak godišnje za sve godine." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Nemoj ukloniti imenovane snimke." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Opcije" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Omogući obaveštenja" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Onemogući snimke kada se računar napaja iz baterije" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Informacija o status napajanja nije dostupna od sistema" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Pokreni samo jedan snimak u isto vreme" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Ostali snimci će biti blokirani dok se trenutni snimak ne završi. Ovo je " "globalna opcije. Tako da će uticati na sve profile za ovog korisnika. Ali " "ovo morate aktivirati i za sve ostale korisnike takođe." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Rezervna kopija je zamenila fajlove prilikom vraćanja" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Novije verzije fajlova će se preimenovati sa pretećim {suffix} prije " "vraćanja. Ako vam više ne trebaju, možete ih ukloniti pomoću {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Nastavi nakon greške (zadrži nekompletan snimak)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Користи контролни број за примећивање промена" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Napravi novi snimak bez obzira da li je bilo promena ili ne." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Nivo detalja u izveštaju:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Nijedan" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "&Napredne opcije" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Oprez: Promenite ove opcija samo ako znate šta radite." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Pokrenite 'rsync' sa '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "kao cron posao" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "na udaljenom domaćinu" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "kada se kreira ručni snimak" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Molim vas instalirajte 'nocache' kako bi ste omogućili ovu opciju)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "na lokalnoj mašini" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Preusmerite stdout ka /dev/null u cron poslovima." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Cron će automatski poslati email sa priloženim izlazom cronjob-ova ukoliko " "je MTA instaliran." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Preusmerite stderr prema /dev/null u cron poslovima." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Cron će automatski poslati email sa priloženim greškama od cronjob-ova " "ukoliko je MTA instaliran." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Ograniči upotrebu propusnog opsega rsync-a:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/sek" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Sačuvaj ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Sačuvaj proširene atribute (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopiraj nebezbedni link (funkcioniše samo sa apsolutnim linkovima)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Ograniči na samo jedan fajl sistem" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opcije moraju biti pod navodnicima npr. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Nalepi dodatne opcije za rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Dodaj prefiks SSH komandama" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Prefiks koji će se pokretati pre svake komande na udaljenom domaćinu." #: qt/settingsdialog.py:1188 #, 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 "" "Promenjive treba iskočiti sa \\$FOO. Ovo ne dotiče rsync. Tako da bi dodali " "prefix za rsync koristite \"{example_value}\" sa {rsync_options_value}." #: qt/settingsdialog.py:1196 msgid "default" msgstr "podrazumevano" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Proveri da li je udaljeni domaćin onlajn" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Upozorenje: ukoliko je onemogućeno i udaljeno domaćin nije dostupan, ovo " "može dovesti do nekih čudnih grešaka." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Proveri da li udaljeni domaćin podržava sve neophodne komande." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Upozorenje: ukoliko je onemogućeno i udaljeni domaćin ne podržava sve " "neophodne komande, ovo može dovesti do nekih čudnih grešaka." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Povrati konfiguraciju" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Promeni user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "Podrška za EncFS će biti uklonjena u doglednoj budućnosti. Odluka o zameni " "kontinuirane podrške za šifriranje sigurnosne kopije još uvek je na čekanju," " zavisi od resursa projekta i dostupnosti saradnika. Više detalja dostupno " "je u ovim {whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Novi profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Preimenuj profil" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Da li ste sigurni da želite da uklonite profil \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "{BOLD}Toplo preporučeno{ENDBOLD}: (Sve preporuke su već uključene.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Toplo preporučeno{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "Niste izabrali datoteku privatnog ključa za SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Da li želite da generišete novi par javnih/privatnih ključeva bez lozinke?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Datoteka privatnog ključa u \"{file}\" ne postoji." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Da li želite da kopirate vaš javni SSH ključ na udaljenom domaćinu za " "omogućavanje prijave bez lozinke?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Autentičnost domaćine {host} se ne može utvrditi." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} otisak prsta ključa je:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Molimo proverite ovaj otisak prsta! Da li želite da ga dodate u svoju " "datoteku 'known_hosts'?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Izostavi šablon" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Izostavi fajl" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Izostavi direktorijum" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Uključi fajl" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" je simbolična veza. Povezana meta neće biti sigurnosno kopiran dok ga ne uključite.\n" "Da li želite da uključite metu simboličku veze?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Uključi direktorijum" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Da li stvarno želite da promenite direktorijum za snimke?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Kreiranje novog SSH ključa u {path} nije uspelo." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "Deaktivirano zato što šabloni ne funkcionišu u 'SSH šifrovanom' modu." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(podrazumevano: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "onemogućeno" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "omogućeno" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Uvezi sertifikat" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Konfiguracije nije pronađena" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Uvezi" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Odaberi folder za snimak iz koga konfiguracionu fajl treba da bude uvezen. " "Ova putanja može da izgleda kao: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Ukoliko se datoteka nalazi na vanjskom ili udaljenom disku, unapred je " "potrebno ručno montira disk." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opcije o upoređivanju snimaka" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Komanda:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametri:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Користи %1 и %2 као параметре за путање" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Molim postavite diff komandu ili pritisnite otkaži." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "Komanda \"{cmd}\" nije pronađena na ovom sistemu. Molimo probajte nešto " "drugo ili pritisnite otkaži." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Parametri nisu postavljeni za diff komandu. Koristiće se podrazumevane " "vrednosti \"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Samo različiti snimci" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Излистај само снимке једнаке следећим:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Дубока провера (прецизнија али спора)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Избриши" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Одабери Све" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Uporedi" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Иди На" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Opcije" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Nije moguće uporediti snimak sa samim sobom." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Da li stvarno želite da izbrišete {file} u snimku {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Da li stvarno želite da izbrišete {file} u {count} snimka?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "Upozorenja: Ovo se ne može opožvati." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Isključi {path} iz budućih snimaka?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Ne mogu da pronađem crontab.\n" #~ "Da li ste sigurni da je crontab inastaliran?\n" #~ "Ako nije, treba da onemogućite automatsko kreiranje rezervnih kopija." #~ msgid "Full snapshot path" #~ msgstr "Puna putanja snimka" #~ msgid "Mode" #~ msgstr "Mod" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Профил '{profile}': Унесите лозинку за {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang u user-callback skripti nije izvršan." #~ msgid "WARNING" #~ msgstr "UPOZORENJE" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "u encfs verzijama 1.7.2 i ranije postoji greška kod opcije --reverse. Molim " #~ "vas ažurirajte encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "user-callback skripte ne sadrži shebang (#!/bin/sh) liniju." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} koristi EncFS za enkripciju. Nedavna sigurnosna revizija otkrila je " #~ "nekoliko mogućih vektora napada za ovo. Molimo pogledajte \"A NOT ON " #~ "SECURITY\" u \"man backintime\"." backintime-1.5.2/common/po/sv.po000066400000000000000000001477761465446530500165650ustar00rootroot00000000000000# Swedish 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:12+0000\n" "Last-Translator: buhtz \n" "Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Varning" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Huvudprofil" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "krypterad" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH krypterad" #: common/config.py:309 msgid "Local" msgstr "Lokal" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Privat SSH-nyckel" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "krypterad" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Kryptering" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH krypterad" #: common/config.py:327 msgid "Default" msgstr "Standard" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 #, fuzzy msgid "Snapshots folder is not valid!" msgstr "Mappen för ögonblicksbilder är ogiltig!" #: common/config.py:371 #, fuzzy msgid "You must select at least one folder to back up!" msgstr "Du måste välja minst en mapp att säkerhetskopiera!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Mappen för säkerhetskopior kan inte inkluderas." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Undermapp till mappen för säkerhetskopior kan inte inkluderas." #: common/config.py:447 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} är inte en mapp." #: common/config.py:456 #, fuzzy msgid "Host/User/Profile-ID must not be empty." msgstr "Värd/användare/profil-ID får inte vara tomt." #: common/config.py:466 common/config.py:513 #, fuzzy, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Kan inte skriva till: {path}\n" "Är du säker på att du har skrivrättighet?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Destinationsfilsystemet för \"{path}\" är formaterat med FAT som inte stöder" " hårda länkar. Använd ett inbyggt Linux-filsystem." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Destinationsfilsystem för \"{path}\" är en SMB-monterad delning. Kontrollera" " att fjärr-SMB-servern stöder symboliska länkar eller aktivera '{copyLinks}'" " i \"{expertOptions}\"." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Kopiera länkar (följ symboliska länkar)" #: common/config.py:497 msgid "Expert Options" msgstr "Expertinställningar" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Destinationsfilsystem för \"{path}\" är en sshfs-monterad delning. sshfs " "stöder inte hårda länkar. Vänligen använd läget \"SSH\" istället." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Misslyckades med att skriva ny crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" "Kunde inte installera Udev-regel för profil {profile_id}. DBus-tjänst " "\"{dbus_interface}\" var inte tillgänglig" #: common/config.py:1761 #, fuzzy, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Schema udev fungerar inte med läget {mode}" #: common/config.py:1772 #, fuzzy, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Kunde inte hitta UUID för \"{path}\"" #: common/configfile.py:107 #, fuzzy msgid "Failed to save config" msgstr "Misslyckades med att spara konfiguration" #: common/configfile.py:143 #, fuzzy msgid "Failed to load config" msgstr "Misslyckades med att ladda konfiguration" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilen \"{name}\" finns redan." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Den sista profilen kan inte tas bort." #: common/encfstools.py:92 #, fuzzy, python-brace-format msgid "Can't mount '{command}'" msgstr "Kan inte montera \"{command}\"" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Konfiguration för krypterad mapp hittades inte." #: common/encfstools.py:147 #, fuzzy msgid "Create a new encrypted folder?" msgstr "Skapa en ny krypterad mapp?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Avbryt" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Bekräfta lösenord." #: common/encfstools.py:160 #, fuzzy msgid "Password doesn't match." msgstr "Lösenord matchar inte." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Ta ögonblicksbild" #: common/mount.py:609 #, fuzzy, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan inte avmontera {mountprocess} från {mountpoint}." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "%(proc)s hittades inte. Vänligen installera t.ex. %(install_command)s" #: common/mount.py:720 #, fuzzy, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "monteringspunkten %s är inte tom." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "MISSLYCKADES" #: common/snapshots.py:560 common/snapshots.py:622 #, fuzzy msgid "Restore permissions" msgstr "Återställ behörigheter" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Färdig" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Uppskjuter säkerhetskopiering vid batteridrift" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Det går inte att hitta mappen för ögonblicksbilder.\n" "Om den är på en flyttbar enhet, anslut den." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Väntar %s sekund." msgstr[1] "Väntar %s sekunder." #: common/snapshots.py:907 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Misslyckades att skapa ögonblicksbild {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Färdigställer" #: common/snapshots.py:1069 #, fuzzy msgid "Can't create folder" msgstr "Kan inte skapa mapp" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Sparar konfigurationsfil…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Sparar behörigheter…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Hittade överbliven \"{snapshot_id}\" som kan återupptas." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Tar bort överbliven \"{snapshot_id}\"-mapp från den senaste körningen" #: common/snapshots.py:1312 #, fuzzy msgid "Can't remove folder" msgstr "Kan inte ta bort mapp" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Tar ögonblicksbild" #: common/snapshots.py:1417 msgid "Success" msgstr "" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Ofullständig överföring på grund av källfiler som försvann (se \"man " "rsync\")" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "\"rsync\" avslutades med felkod {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Se \"man rsync\" för fler detaljer" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "Negativa felkoder från rsync är signalnummer, se \"kill -l\" och \"man kill\"" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Inget har förändrats, ingen ny ögonblicksbild behövs" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Kan inte byta namn från {new_path} till {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Smart borttagning" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Tar bort gamla ögonblicksbilder" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Försöker att behålla minimum ledigt utrymme" #: common/snapshots.py:1929 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Försöker behålla minst {perc} fria inoder" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Nu" #: common/sshtools.py:239 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan inte montera {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent hittades inte. Kontrollera att den är installerad." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Kunde inte låsa upp privat ssh-nyckel. Fel lösenord eller lösenordet är inte" " tillgängligt för cron." #: common/sshtools.py:535 #, fuzzy, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Chiffer {cipher} misslyckades för {host}." #: common/sshtools.py:682 #, fuzzy msgid "Remote path exists but is not a directory." msgstr "Fjärrsökväg finns men är inte en katalog." #: common/sshtools.py:687 #, fuzzy msgid "Remote path is not writable." msgstr "Fjärrsökväg är inte skrivbar." #: common/sshtools.py:692 #, fuzzy msgid "Remote path is not executable." msgstr "Fjärrsökväg är inte körbar." #: common/sshtools.py:697 #, fuzzy msgid "Couldn't create remote path." msgstr "Kunde inte skapa fjärrsökväg." #: common/sshtools.py:981 #, fuzzy, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Fjärrvärd {host} stöder inte {command}" #: common/sshtools.py:985 common/sshtools.py:994 #, fuzzy msgid "Look at 'man backintime' for further instructions" msgstr "Se \"man backintime\" för ytterligare instruktioner" #: common/sshtools.py:989 #, fuzzy, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Kontrollera kommandon på värd {host} gav okänt fel" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Fjärrvärd {host} stöder inte hårda länkar" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Kopiera offentlig ssh-nyckel \"{pubkey}\" till fjärrvärd \"{host}\"" #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Ange lösenord för \"{user}\"." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Om" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Utvecklare" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Översättningar" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Licens" #: qt/app.py:169 msgid "Shortcuts" msgstr "Genvägar" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Denna mapp finns inte\n" "i den nu valda ögonblicksbilden." #: qt/app.py:256 msgid "Add to Include" msgstr "Lägg till i inkludera" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Lägg till i exkludera" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Kan inte skapa mapp." #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Kan inte hitta mappen för ögonblicksbilder.\n" "Anslut enheten om den finns på en flyttbar enhet och tryck på OK." #: qt/app.py:481 #, fuzzy msgid "Take a snapshot" msgstr "Ta ögonblicksbild" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" "Använd tid för senaste ändring & storlek för att upptäcka förändringar." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Ta ögonblicksbild (med checksumma)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Använd checksumma för att detektera ändringar." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Pausa process för ögonblicksbilder" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Återuppta process för ögonblicksbilder" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Stoppa process för ögonblicksbilder" #: qt/app.py:504 #, fuzzy msgid "Refresh snapshot list" msgstr "Uppdatera listan över ögonblicksbilder" #: qt/app.py:508 msgid "Name snapshot" msgstr "Namnge ögonblicksbild" #: qt/app.py:512 #, fuzzy msgid "Remove snapshot" msgstr "Ta bort ögonblicksbild" #: qt/app.py:516 #, fuzzy msgid "View snapshot log" msgstr "Visa logg för ögonblicksbild" #: qt/app.py:520 #, fuzzy msgid "View last log" msgstr "Visa senaste logg" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Hantera profiler…" #: qt/app.py:528 msgid "Shutdown" msgstr "Stäng av" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Stäng av systemet när ögonblicksbild har slutförts." #: qt/app.py:532 msgid "Setup language…" msgstr "Välj språk…" #: qt/app.py:536 msgid "Exit" msgstr "Avsluta" #: qt/app.py:540 msgid "Help" msgstr "Hjälp" #: qt/app.py:544 msgid "Profiles config file" msgstr "Konfigurationsfil för profiler" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Webbsida" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Ändringslogg" #: qt/app.py:553 msgid "FAQ" msgstr "Vanliga frågor" #: qt/app.py:556 msgid "Ask a question" msgstr "Ställ en fråga" #: qt/app.py:559 msgid "Report a bug" msgstr "Rapportera ett fel" #: qt/app.py:562 #, fuzzy msgid "Translation" msgstr "Översättningar" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Återskapa" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "" "Återskapa de valda filerna eller mapparna på den ursprungliga platsen." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 #, fuzzy msgid "Restore to …" msgstr "Återskapa till…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Återskapa de valda filerna eller mapparna på en ny plats." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Återskapa denna mappen och hela dess innehåll på den ursprungliga platsen." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Återskapa denna mappen och hela dess innehåll på en ny plats." #: qt/app.py:595 msgid "Up" msgstr "Upp" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Visa dolda filer" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Jämför ögonblicksbilder…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 #, fuzzy msgid "&Restore" msgstr "Å&terskapa" #: qt/app.py:682 #, fuzzy msgid "&Help" msgstr "&Hjälp" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Om du stänger detta fönster kan Back In Time inte stänga av ditt system när ögonblicksbilden är färdig.\n" "Vill du verkligen stänga?" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "Vill du verkligen återställa den här filen?" #: qt/app.py:987 msgid "Working:" msgstr "Arbetar:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Färdig, ingen säkerhetskopiering behövdes" #: qt/app.py:1044 #, fuzzy msgid "Working" msgstr "Arbetar" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Fel" #: qt/app.py:1076 #, fuzzy msgid "Sent" msgstr "Skickat" #: qt/app.py:1077 #, fuzzy msgid "Speed" msgstr "Hastighet" #: qt/app.py:1078 #, fuzzy msgid "ETA" msgstr "Tid kvar" #: qt/app.py:1140 msgid "Global" msgstr "Global" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Hem" #: qt/app.py:1170 msgid "Backup folders" msgstr "Mappar för säkerhetskopior" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Namn på ögonblicksbild" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Är du säker på att du vill ta bort denna ögonblicksbild?" msgstr[1] "Är du säker på att du vill ta bort de här ögonblicksbilderna?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Skapa säkerhetskopior med efterföljande \"{suffix}\"\n" "innan lokala filer skrivs över eller tas bort ." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Nyare versioner av filer kommer att omdöpas med efterföljande \"{suffix}\" innan de återställs.\n" "Om du inte behöver dem längre kan du ta bort dem med \"{cmd}\"" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Återställ endast filer som inte existerar eller\n" "är nyare än de i destinationen.\n" "Använder alternativet \"rsync --update\"." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Ta bort nyare filer i originalmappen." #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Återställ valda filer eller mappar till den ursprungliga platsen och\n" "ta bort filer och mappar som inte finns i ögonblicksbilden.\n" "Detta kommer att ta bort filer och mappar som var exkluderade när ögonblicksbilden togs!\n" "Var extremt försiktig!!!" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Vill du verkligen återställa den här filen till den nya mappen\n" "{path}?" msgstr[1] "" "Vill du verkligen återställa de här filerna till den nya mappen\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Vill du verkligen återställa den här filen?" msgstr[1] "Vill du verkligen återställa de här filerna?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Är du säker på att du vill ta bort alla nyare filer i {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Är du säker på att du vill ta bort alla nyare filer i din ursprungliga mapp?" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "VARNING: Att ta bort filer i filsystemets rot kan förstöra hela ditt " "system!!!" #: qt/app.py:1750 #, fuzzy msgid "Snapshot" msgstr "Ögonblicksbilder" #: qt/app.py:1787 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Återskapa {path}" #: qt/app.py:1789 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Återskapa {path} till…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "Språkinställningarna aktiveras inte förrän Back in Time startas om." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Välj språk" #: qt/languagedialog.py:92 msgid "System default" msgstr "Standardval för systemet" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Använd operativsystemets språk." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Översatt: {percent}" #: qt/languagedialog.py:188 #, 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 "" "Hej\n" "Du har använt Back In Time på språket {language} några gånger nu.\n" "Översättningen av din installerade version av Back In Time till {language} är {perc} färdig. Oavsett dina tekniska färdigheter så kan du bidra till översättningen och därmed till Back In Time självt.\n" "Besök {translation_platform_url} om du vill bidra. Besök {back_in_time_project_website} för vidare hjälp och frågor.\n" "Vi ber om ursäkt för avbrottet, detta meddelande kommer inte visas igen. Denna dialogruta finns tillgänglig när som helst från hjälpmenyn.\n" "Back In Time-gruppen" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "översättningsplattformen" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Din översättning" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Visning av senaste logg" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Visning av logg för ögonblicksbild" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Ögonblicksbilder:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filter:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Alla" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Ändringar" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Fel" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Information" msgstr[1] "Information" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Fel, [I] Information, [C] Ändring" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "avkoda sökvägar" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Fråga" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Visa senaste logg" #: qt/qtsystrayicon.py:108 #, fuzzy, python-brace-format msgid "Start {appname}" msgstr "Starta {appname}" #: qt/qtsystrayicon.py:174 #, fuzzy msgid "Working…" msgstr "Arbetar…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Skickat:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Hastighet:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Ögonblicksbilder" #: qt/qttools.py:427 msgid "Today" msgstr "Idag" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Igår" #: qt/qttools.py:443 msgid "This week" msgstr "Denna vecka" #: qt/qttools.py:450 msgid "Last week" msgstr "Förra veckan" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Detta är INTE en ögonblicksbild utan en levande visning av dina lokala filer" #: qt/qttools.py:601 #, fuzzy, python-brace-format msgid "Last check {time}" msgstr "Senaste kontroll {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Visa fullständig logg" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Värd:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Användare:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Hantera profiler" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Redigera" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Lägg till" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Ta bort" #: qt/settingsdialog.py:210 #, fuzzy msgid "&General" msgstr "&Allmänt" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Läge:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Var ögonblicksbilder ska sparas" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Mapp" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH-inställningar" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Sökväg:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Chiffer:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Privat nyckel:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Välj en existerande privat nyckel (normalt filen \"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Skapa en ny SSH-nyckel utan lösenord (tillåts inte om en privat nyckel redan" " är vald)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Lösenord" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Spara lösenord till nyckelring" #: qt/settingsdialog.py:378 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/settingsdialog.py:390 msgid "Advanced" msgstr "Avancerat" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Fullständig sökväg för ögonblicksbild:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Schema" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Inaktiverad" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Vid varje start/omstart" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Varje minut" msgstr[1] "Med {n} minuters mellanrum" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Varje timme" msgstr[1] "Varje timme" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Varje timme" msgstr[1] "Med {n} timmars mellanrum" #: qt/settingsdialog.py:457 #, fuzzy msgid "Custom hours" msgstr "Anpassade timmar" #: qt/settingsdialog.py:458 #, fuzzy msgid "Every day" msgstr "Varje dag" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Upprepad (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "När enhet ansluts (udev)" #: qt/settingsdialog.py:461 #, fuzzy msgid "Every week" msgstr "Varje vecka" #: qt/settingsdialog.py:462 #, fuzzy msgid "Every month" msgstr "Varje månad" #: qt/settingsdialog.py:463 #, fuzzy msgid "Every year" msgstr "Varje år" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Veckodag:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Timme:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Timmar:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Kör Back In Time upprepade gånger. Detta är användbart om datorn inte körs " "regelbundet." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Varje:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Timme(-ar)" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Dag(ar)" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Vecka(-or)" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Månad(er)" #: qt/settingsdialog.py:555 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 "" "Kör Back In Time så snart enheten ansluts (endast en gång var X dag).\n" "Du kommer att bli tillfrågad om ditt sudo-lösenord." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 #, fuzzy msgid "&Include" msgstr "&Inkludera" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Inkludera filer och mappar" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Lägg till fil" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Lägg till mapp" #: qt/settingsdialog.py:621 #, fuzzy msgid "&Exclude" msgstr "&Exkludera" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Exkludera mönster, filer eller mappar" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Lägg till standard" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Exkludera filer större än:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Exkludera filer större än: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Exkludera filer större än värdet i %(prefix)s.\n" "Med \"Full rsync-läge\" inaktiverat påverkas endast nya filer\n" "eftersom detta är ett överföringsalternativ för rsync, inte ett exkluderingsalternativ.\n" "Så stora filer som har säkerhetskopierats tidigare kommer att behållas i överblicksbilder\n" "även om de hade förändrats." #: qt/settingsdialog.py:720 #, fuzzy msgid "&Auto-remove" msgstr "Automatisk &borttagning" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Äldre än:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "År" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Om ledigt utrymme är mindre än:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Om lediga inoder är mindre än:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Smart borttagning:" #: qt/settingsdialog.py:793 #, fuzzy msgid "Run in background on remote host." msgstr "Kör i bakgrunden på fjärrvärden." #: qt/settingsdialog.py:794 #, fuzzy msgid "EXPERIMENTAL" msgstr "EXPERIMENTELL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Behåll alla ögonblicksbilder för de senaste" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "dag(ar)." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Behåll en ögonblicksbild per dag de senaste" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Behåll en ögonblicksbild per vecka för de senaste" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "vecka/veckor." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Behåll en ögonblicksbild per månad de senaste" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "månad(er)." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Behåll en ögonblicksbild per år för alla år." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Ta inte bort namngivna ögonblicksbilder." #: qt/settingsdialog.py:849 #, fuzzy msgid "&Options" msgstr "I&nställningar" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Aktivera aviseringar" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Inaktivera ögonblicksbilder vid batteridrift" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Strömstatus inte tillgängligt från system" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Gör bara en ögonblicksbild i taget" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." 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å." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Säkerhetskopiera ersatta filer vid återställning" #: qt/settingsdialog.py:879 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Nyare versioner av filer kommer att omdöpas med efterföljande \"{suffix}\" innan de återställs.\n" "Om du inte behöver dem längre kan du ta bort dem med \"{cmd}\"" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Fortsätt vid fel (behåll icke kompletta ögonblicksbilder)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Använd checksummor för att detektera ändringar" #: qt/settingsdialog.py:899 #, fuzzy msgid "Take a new snapshot whether there were changes or not." msgstr "Ta en ny ögonblicksbild oavsett om det fanns ändringar eller inte." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Loggnivå:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Inga" #: qt/settingsdialog.py:931 #, fuzzy msgid "E&xpert Options" msgstr "E&xpertinställningar" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Varning: Ändra dessa inställningar endast om du vet vad du gör." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, fuzzy, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Kör \"rsync\" med \"{cmd}\":" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "som cronjobb" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "på fjärrvärd" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "vid tagning av en manuell ögonblicksbild" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Installera \"nocache\" för att aktivera detta alternativ)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "på lokal maskin" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Omdirigera stdout till /dev/null i cronjobb." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Omdirigera stderr till /dev/null i cronjobb." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Bandbreddsbegränsning för rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Behåll ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Behåll utökade attribut (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopiera osäkra länkar (fungerar endast med absoluta länkar)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Alternativ måste skrivas inom citationstecken t.ex. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Klistra in ytterligare alternativ till rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Lägg till prefix till SSH-kommandon" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Prefix att köra före varje kommando på fjärrvärd.\n" "Variabler behöver skyddas 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:1196 msgid "default" msgstr "standard" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Kontrollera om fjärrvärd är uppkopplad" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Varning: om detta alternativ stängs av och fjärrvärden\n" "inte är tillgänglig, kan det leda till några\n" "konstiga fel." #: qt/settingsdialog.py:1218 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:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Varning: om detta alternativ stängs av och fjärrvärden\n" "inte stöder alla nödvändiga kommandon,\n" "kan detta leda till några konstiga fel." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Återställ konfiguration" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Redigera user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Ny profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Byt namn på profil" #: qt/settingsdialog.py:1318 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Är du säker på att du vill ta bort profilen \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Starkt rekommenderade" #: qt/settingsdialog.py:1634 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 "" "Anpassade timmar kan endast vara en kommaseparerad lista av timmar (t.ex. " "8,12,18,23) eller */3 för periodiska säkerhetskopior var 3:e timme." #: qt/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Du har inte valt en privat SSH-nyckel.\n" "Vill du skapa ett nytt lösenordsfritt offentligt/privat nyckelpar?" #: qt/settingsdialog.py:1692 #, fuzzy, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Privat nyckelfil \"{file}\" finns inte." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Vill du kopiera din offentliga SSH-nyckel till\n" "fjärrvärden för att aktivera lösenordslös inloggning?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "Autenticiteten för värd \"{host}\" kan inte fastställas.\n" "\n" "Fingeravtryck för nyckel av typ {keytype} är:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Kontrollera detta fingeravtryck! Vill du lägga till det i din " "\"known_hosts\"-fil?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Exkludera mönster" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Exkludera fil" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Exkludera mapp" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Inkludera fil" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" är en symbolisk länk. Det länkade målet kommer inte att säkerhetskopieras förrän du inkluderar det också.\n" "Vill du inkludera länkens mål istället?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Inkludera mapp" #: qt/settingsdialog.py:2169 #, fuzzy 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/settingsdialog.py:2194 #, 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:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "standard" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "inaktiverad" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "aktiverad" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Ingen konfiguration hittades" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Alternativ för att jämföra ögonblicksbilder" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Kommando:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametrar:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Använd %1 och %2 för sökvägsparametrar" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Endast ögonblicksbilder med olikheter" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Lista endast lika ögonblicksbilder till: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Djupkontroll (mer noggrann, men långsam)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Ta bort" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Välj alla" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Jämför" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Gå till" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Alternativ" #: qt/snapshotsdialog.py:355 #, fuzzy 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:398 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Vill du verkligen ta bort \"{file}\" i ögonblicksbild \"{snapshot_id}\"?" #: qt/snapshotsdialog.py:404 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Vill du verkligen ta bort \"{file}\" i {count} ögonblicksbilder?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "VARNING: Det här kan inte återkallas." #: qt/snapshotsdialog.py:426 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Exkludera \"{path}\" från framtida ögonblicksbilder?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Kan inte hitta crontab.\n" #~ "Är du säker på att cron är installerat?\n" #~ "Om inte så måste du inaktivera alla automatiska säkerhetskopior." #~ msgid "Full snapshot path" #~ msgstr "Full sökväg för ögonblicksbild" #, fuzzy #~ msgid "Mode" #~ msgstr "Läge" #, fuzzy #~ msgid "Profile" #~ msgstr "Profil" #, fuzzy, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil \"{profile}\": Ange lösenord för {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang i user-callback skript är inte körbart." #~ msgid "WARNING" #~ msgstr "VARNING" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs version 1.7.2 och tidigare har ett fel som påverkar alternativet " #~ "--reverse. Vänligen uppdatera encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "user-callback skript har ingen shebang (#!/bin/sh) rad." #, fuzzy, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} använder EncFS för kryptering. En ny säkerhetsrevision avslöjade flera" #~ " möjliga angreppsvektorer för detta. Ta en titt på \"A NOTE ON SECURITY\" i " #~ "\"man backintime\"." backintime-1.5.2/common/po/th.po000066400000000000000000001743721465446530500165400ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 12:37+0000\n" "Last-Translator: buhtz \n" "Language-Team: Thai \n" "Language: th\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.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "คำเตือน" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "โปรไฟล์หลัก" #: common/config.py:297 #, fuzzy msgid "Local (EncFS encrypted)" msgstr "SSH ที่เข้ารหัสแล้ว" #: common/config.py:298 #, fuzzy msgid "SSH (EncFS encrypted)" msgstr "SSH ที่เข้ารหัสแล้ว" #: common/config.py:309 msgid "Local" msgstr "ท้องถิ่น" #: common/config.py:311 msgid "SSH" msgstr "" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "คีย์ส่วนตัว SSH" #: common/config.py:314 #, fuzzy msgid "Local encrypted" msgstr "SSH ที่เข้ารหัสแล้ว" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "การเข้ารหัส" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH ที่เข้ารหัสแล้ว" #: common/config.py:327 msgid "Default" msgstr "ค่าเริ่มต้น" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "โปรไฟล์: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "โฟลเดอร์ Snapshots ไม่ถูกต้อง!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "คุณต้องเลือกโฟลเดอร์อย่างน้อยหนึ่งโฟลเดอร์เพื่อทำการสำรองข้อมูล!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:447 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} ไม่ใช่โฟลเดอร์" #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID ต้องไม่เป็นค่าว่าง" #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "ไม่สามารถเขียนไปยัง: {path} \n" "ได้ คุณแน่ใจหรือว่าคุณมีสิทธิ์ในการเขียน?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "ระบบไฟล์ปลายทางสำหรับ {path} ถูกฟอร์แมตด้วย FAT ซึ่งไม่รองรับ hard-links " "โปรดใช้ระบบไฟล์ของ Linux ที่เป็นธรรมชาติ" #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "ระบบไฟล์ปลายทางสำหรับ {path} เป็นการแชร์ที่เชื่อมต่อด้วย SMB " "โปรดตรวจสอบให้แน่ใจว่าเซิร์ฟเวอร์ SMB ระยะไกลรองรับ symlink หรือเปิดใช้งาน " "{copyLinks} ใน {expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "คัดลอกลิงก์ (เคลียร์ลิงก์สัญลักษณ์)" #: common/config.py:497 msgid "Expert Options" msgstr "ตัวเลือกขั้นสูง" #: common/config.py:501 #, fuzzy, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "ระบบไฟล์ปลายทางสำหรับ {path} เป็นการแชร์ที่เชื่อมต่อด้วย sshfs ซึ่งไม่รองรับ" " hard-links โปรดใช้โหมด 'SSH' แทน" #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "ไม่สามารถเขียน crontab ใหม่ได้" #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" #: common/config.py:1746 #, 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 Service " "'{dbus_interface}' ไม่พร้อมใช้งาน" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "ตารางงานของ udev ไม่ทำงานกับโหมด {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "ไม่พบ UUID สำหรับ \"{path}\"" #: common/configfile.py:107 msgid "Failed to save config" msgstr "ไม่สามารถบันทึกการตั้งค่าได้" #: common/configfile.py:143 msgid "Failed to load config" msgstr "ไม่สามารถโหลดการตั้งค่าได้" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "โปรไฟล์ \"{name}\" มีอยู่แล้ว" #: common/configfile.py:735 #, fuzzy msgid "The last profile cannot be removed." msgstr "ไม่สามารถเพิกถอนสิ่งนี้ได้" #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "ไม่สามารถเชื่อมต่อ '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "ไม่พบการตั้งค่าสำหรับโฟลเดอร์ที่เข้ารหัส" #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "ต้องการสร้างโฟลเดอร์ที่เข้ารหัสใหม่หรือไม่?" #: common/encfstools.py:151 msgid "Cancel" msgstr "" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "โปรดยืนยันรหัสผ่าน." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "สร้างสแนปช็อต" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "" #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "ล้มเหลว" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "เรียกคืนสิทธิ์" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "ทำเสร็จ" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "การเลื่อนการสำรองข้อมูลขณะใช้แบตเตอรี่" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "ไม่พบโฟลเดอร์สแนปช็อต\n" "หากมันอยู่บนไดรฟ์ที่เปลี่ยนได้กรุณาเสียบเข้ากับเครื่อง" #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "รอ %s วินาที." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "ไม่สามารถสร้างสแนปช็อต {snapshot_id} ได้" #: common/snapshots.py:937 msgid "Finalizing" msgstr "กำลังประมวลผลล่าสุด" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "ไม่สามารถสร้างโฟลเดอร์ได้" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "กำลังบันทึกไฟล์กำหนดค่า…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "กำลังบันทึกสิทธิ์การเข้าถึง…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "พบส่วนที่เหลืออยู่ {snapshot_id} ซึ่งสามารถดำเนินการต่อได้" #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "พบส่วนที่เหลืออยู่ {snapshot_id} ซึ่งสามารถทำต่อได้" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "ไม่สามารถลบโฟลเดอร์ได้" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "กำลังสร้างสแนปช็อต" #: common/snapshots.py:1417 msgid "Success" msgstr "" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "ไม่มีการเปลี่ยนแปลงใด ๆ ไม่จำเป็นต้องสร้างสแนปช็อตใหม่" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "ไม่สามารถเปลี่ยนชื่อ {new_path} เป็น {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "การลบอย่างฉลาด" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "กำลังลบสแนปช็อตเก่า" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "พยายามรักษาพื้นที่ว่างขั้นต่ำ" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "พยายามรักษาเครื่องหมายเชิงว่างขั้นต่ำที่ {perc}" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "เดี๋ยวนี้" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "ไม่สามารถเชื่อมต่อ {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "ไม่สามารถสร้างโฟลเดอร์ได้" #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1164 #, fuzzy, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "คัดลอก SSH public key \"{pubkey}\" ไปยังโฮสต์ระยะไกล \"{host}\"" #: common/sshtools.py:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "โปรดป้อนรหัสผ่านสำหรับ \"{user}\"" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "เกี่ยวกับ" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "ผู้เขียน" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "การแปล" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "ใบอนุญาต" #: qt/app.py:169 msgid "Shortcuts" msgstr "ทางลัด" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "โฟลเดอร์นี้ไม่มีอยู่\n" "ในสแนปช็อตที่เลือกในปัจจุบัน" #: qt/app.py:256 msgid "Add to Include" msgstr "เพิ่มในรายการรว" #: qt/app.py:258 msgid "Add to Exclude" msgstr "เพิ่มไปยังรายการยกเว้น" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" #: qt/app.py:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "ไม่สามารถสร้างโฟลเดอร์ได้" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "ไม่พบโฟลเดอร์สแนปช็อต\n" "หากอยู่บนไดรฟ์ที่เปลี่ยนได้ โปรดเสียบเข้าแล้วกด OK" #: qt/app.py:481 msgid "Take a snapshot" msgstr "สร้างสแนปช็อต" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "ทำสแนปช็อตพร้อมแสดงผลการตรวจสอบสำหรับ checksum" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "ใช้ checksum เพื่อตรวจจับการเปลี่ยนแปลง" #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "หยุดกระบวนการสร้างสแนปช็อตชั่วคราว" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "ดำเนินกระบวนการสร้างสแนปช็อตต่อ" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "หยุดกระบวนการสร้างสแนปช็อต" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "รีเฟรชรายการสแนปช็อต" #: qt/app.py:508 msgid "Name snapshot" msgstr "สร้างสแนปช็อต" #: qt/app.py:512 msgid "Remove snapshot" msgstr "ลบสแนปช็อต" #: qt/app.py:516 msgid "View snapshot log" msgstr "ดูบันทึกสแนปช็อต" #: qt/app.py:520 msgid "View last log" msgstr "ดูบันทึกล่าสุด" #: qt/app.py:524 msgid "Manage profiles…" msgstr "โปรไฟล์หลัก…" #: qt/app.py:528 msgid "Shutdown" msgstr "ปิดเครื่อง" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "ปิดเครื่องหลังสแนปช็อตเสร็จสิ้น" #: qt/app.py:532 msgid "Setup language…" msgstr "" #: qt/app.py:536 msgid "Exit" msgstr "ออก" #: qt/app.py:540 msgid "Help" msgstr "ช่วยเหลือ" #: qt/app.py:544 #, fuzzy msgid "Profiles config file" msgstr "กำลังบันทึกไฟล์กำหนดค่า..." #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "เว็บไซต์" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "บันทึกการเปลี่ยนแปลง" #: qt/app.py:553 msgid "FAQ" msgstr "FAQ" #: qt/app.py:556 msgid "Ask a question" msgstr "แปลว่า \"ถามคำถาม" #: qt/app.py:559 msgid "Report a bug" msgstr "รายงานข้อบกพร่อง" #: qt/app.py:562 msgid "Translation" msgstr "การแปล" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "คืน" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "เรียกคืนไฟล์หรือโฟลเดอร์ที่เลือกไปยังตำแหน่งเดิม" #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "กู้คืนไปยัง …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "เรียกคืนไฟล์หรือโฟลเดอร์ที่เลือกไปยังตำแหน่งใหม่" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "คืนค่าโฟลเดอร์ที่แสดงอยู่ในปัจจุบันพร้อมทั้งเนื้อหาทั้งหมดไปยังตำแหน่งเดิม" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "คืนค่าโฟลเดอร์ที่แสดงอยู่ในปัจจุบันพร้อมทั้งเนื้อหาทั้งหมดไปยังตำแหน่งใหม่" #: qt/app.py:595 msgid "Up" msgstr "ขึ้น" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "แสดงแฟ้มที่ซ่อน" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "สร้างสแนปช็อต…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "" #: qt/app.py:676 msgid "&Restore" msgstr "&คืนค่า" #: qt/app.py:682 msgid "&Help" msgstr "&ช่วยเหลือ" #: qt/app.py:818 #, fuzzy msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "หากคุณปิดหน้าต่างนี้ โปรแกรม Back In Time จะไม่สามารถปิดเครื่องของคุณเมื่อสแนปช็อตเสร็จสิ้นแล้วได้\n" "คุณต้องการปิดหรือไม่?" #: qt/app.py:821 #, fuzzy msgid "Do you really want to close it?" msgstr "คุณต้องการคืนค่าไฟล์เหล่านี้ใช่หรือไม่" #: qt/app.py:987 msgid "Working:" msgstr "กำลังทำงาน中:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "ทำเสร็จ, ไม่จำเป็นต้องสำรอง" #: qt/app.py:1044 msgid "Working" msgstr "กำลังทำงาน" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "ข้อผิดพลาด" #: qt/app.py:1076 msgid "Sent" msgstr "ได้ส่งแล้ว" #: qt/app.py:1077 msgid "Speed" msgstr "ความเร็ว" #: qt/app.py:1078 msgid "ETA" msgstr "ETA" #: qt/app.py:1140 msgid "Global" msgstr "ทั่วโลก" #: qt/app.py:1141 msgid "Root" msgstr "รูท" #: qt/app.py:1142 msgid "Home" msgstr "โฮม" #: qt/app.py:1170 msgid "Backup folders" msgstr "โฟลเดอร์สำรองข้อมูล" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "ชื่อสแนปช็อต" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "คุณแน่ใจว่าต้องการลบสแนปช็อตหรือไม่" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "สำรองข้อมูลไฟล์ท้องถิ่นก่อนเขียนทับหรือลบ\n" "ลบพร้อมคำติดท้าย {suffix}." #: qt/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "เวอร์ชันใหม่ของไฟล์จะถูกเปลี่ยนชื่อด้วยคำติดท้าย {suffix} ก่อนที่จะถูกคืนค่า" " หากคุณไม่ต้องการใช้งานอีกต่อไป คุณสามารถลบไฟล์เหล่านี้ด้วย {cmd}" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "คืนค่าเฉพาะไฟล์ที่ไม่มีอยู่หรือ\n" "เป็นไฟล์เวอร์ชันใหม่กว่าที่อยู่ในตำแหน่งปลายทาง\n" "ใช้ตัวเลือก \"rsync --update\"" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "ลบไฟล์ที่เป็นเวอร์ชันใหม่กว่าในโฟลเดอร์เดิม" #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "คืนค่าไฟล์หรือโฟลเดอร์ที่เลือกไปยังตำแหน่งเดิมและ\n" "ลบไฟล์/โฟลเดอร์ที่ไม่อยู่ในสแนปช็อต\n" "การดำเนินการนี้จะลบไฟล์/โฟลเดอร์ที่ถูกยกเว้นในขณะที่สแนปช็อตถูกสร้างขึ้น!\n" "โปรดระมัดระวังอย่างยิ่ง!!!" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "คุณต้องการคืนค่าไฟล์เหล่านี้ใช่หรือไม่\n" "ไปยังโฟลเดอร์ใหม่ {path}" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "คุณต้องการคืนค่าไฟล์เหล่านี้ใช่หรือไม่" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "คุณแน่ใจว่าต้องการลบไฟล์เวอร์ชันใหม่ทั้งหมดใน {path} หรือไม่?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "คุณแน่ใจว่าต้องการลบไฟล์เวอร์ชันใหม่ทั้งหมดในโฟลเดอร์เดิมของคุณหรือไม่?" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "คำเตือน: การลบไฟล์ในระบบไฟล์รูทอาจทำให้ระบบของคุณเสียหายได้ทั้งหมด!!!" #: qt/app.py:1750 msgid "Snapshot" msgstr "สแนปช็อต" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "คืนค่า {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "คืนค่า {path} ไปยัง…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "" #: qt/languagedialog.py:92 msgid "System default" msgstr "เพิ่มค่าเริ่มต้น" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:188 #, 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} มาแล้ว 2-3 ครั้ง\n" "การแปลภาษาของ Black In Time ในเวอร์ชั่นที่คุณติดตั้งเป็นภาษา {language} ได้ {perc} สำเร็จ ไม่ว่าคุณจะมีความสามารถในทางเทคนิคหรือไม่ คุณสามารถมีส่วนร่วมในการแปลภาษา และมีส่วนร่วมกับโปรแกรม Back In Time ได้\n" "ไปที่ {translation_platform_url} ถ้าคุณอยากจะมีส่วนร่วม สำหรับคำถามและการช่วยเหลือเพิ่มเติม ไปที่ {back_in_time_project_website}\n" "ทางเราต้องขออภัยสำหรับการรบกวน และข้อความนี้จะไม่แสดงบนหน้าจอของคุณอีก ข้อความนี้จะอยู่ที่เมนูความช่วยเหลือ\n" "ทีมงาน Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "การแปล" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "การแปล" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "มุมมองบันทึกล่าสุด" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "มุมมองบันทึกสแนปช็อต" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "โปรไฟล์:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "สแนปช็อต:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "ตัวกรอง:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "ทั้งหมด" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "การเปลี่ยนแปลง" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "ข้อผิดพลาด" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "ข้อมูล" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] ข้อผิดพลาด, [I] ข้อมูล, [C] การเปลี่ยนแปลง" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "ถอดรหัสเส้นทาง" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "คำถาม" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "โปรไฟล์: \"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "ดูบันทึกล่าสุด" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "กำลังทำงาน…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "ได้ส่งแล้ว:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "ความเร็ว:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "สแนปช็อต" #: qt/qttools.py:427 msgid "Today" msgstr "วันนี้" #: qt/qttools.py:434 msgid "Yesterday" msgstr "เมื่อวาน" #: qt/qttools.py:443 msgid "This week" msgstr "สัดาห์นี้" #: qt/qttools.py:450 msgid "Last week" msgstr "สัปดาห์ที่แล้ว" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "นี่ไม่ใช่สแนปช็อต แต่เป็นมุมมองสดของไฟล์ท้องถิ่นของคุณ" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "ตรวจสอบล่าสุดเมื่อ {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "แสดงบันทึกทั้งหมด" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "โฮสต์:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "พอร์ต:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "ผู้ใช้งาน:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "โปรไฟล์หลัก" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "แก้ไข" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "เพิ่ม" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "ลบ" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&ทั่วไป" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "โหมด:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "สถานที่ที่จะบันทึกสแนปช็อต" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "โฟลเดอร์" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "การตั้งค่า SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "เส้นทาง:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "วิธีการเข้ารหัส:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "กุญแจส่วนตัว:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "เลือกไฟล์กุญแจส่วนตัวที่มีอยู่ (ซึ่งมักมีชื่อว่า \"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "สร้างคีย์ SSH ใหม่โดยไม่มีรหัสผ่าน " "(ไม่อนุญาตหากไฟล์กุญแจส่วนตัวถูกเลือกแล้ว)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "รหัสผ่าน" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "บันทึกรหัสผ่านใน Keyring" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "เก็บรหัสผ่านในแคชสำหรับ Cron (ปัญหาด้านความปลอดภัย: root " "สามารถอ่านรหัสผ่านได้)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "ขั้นสูง" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "เส้นทางสแนปช็อตทั้งหมด:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "กำหนดเวลาตามกำหนดการ" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "ถูกปิดใช้งานแล้ว" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "ทุกครั้งที่เปิดเครื่อง/เริ่มรีบูต" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "ทุก {n} นาที" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "ทุกชั่วโมง" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "ทุก {n} ชั่วโมง" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "ชั่วโมงที่กำหนดเอง" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "ทุกวัน" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "เป็นรูปแบบที่ทำซ้ำตามเวลาที่กำหนด (ระบบ anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "เมื่อไดรฟ์เชื่อมต่อกับระบบ (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "ทุกสัปดาห์" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "ทุกเดือน" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "ทุกปี" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "วันในสัปดาห์:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "ชั่วโมง:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "ชั่วโมง:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "ให้ Back In Time ทำงานซ้ำอย่างต่อเนื่อง " "ซึ่งเป็นประโยชน์หากเครื่องคอมพิวเตอร์ไม่ทำงานอย่างเป็นประจำ" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "ทุกๆ:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "ชั่วโมง" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "วัน" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "สัปดาห์" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "เดือน" #: qt/settingsdialog.py:555 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 "" "ให้ Back In Time เรียกใช้งานเมื่อไดรฟ์ถูกเชื่อมต่อ (เพียงครั้งเดียวในช่วง X วัน) \n" "คุณจะได้รับคำถามให้กรอกรหัสผ่านของ sudo ของคุณ" #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&รวมเข้ากับ" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "รวมไฟล์และโฟลเดอร์" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "เพิ่มไฟล์" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "เพิ่มโฟลเดอร์" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&ยกเว้น" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "ระบุรูปแบบที่ต้องการยกเว้น, ไฟล์ หรือ โฟลเดอร์ที่ต้องการยกเว้น" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "เพิ่มค่าเริ่มต้น" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "ยกเว้นไฟล์ที่มีขนาดใหญ่กว่า:" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "ยกเว้นไฟล์ที่มีขนาดใหญ่กว่า: " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "ยกเว้นไฟล์ที่มีขนาดใหญ่กว่าค่าที่ระบุใน %(prefix)s\n" "เมื่อ 'โหมดการสำรองข้อมูลแบบ rsync เต็มรูปแบบ' ถูกปิดใช้งาน สิ่งนี้จะมีผลกับไฟล์ใหม่เท่านั้น\n" "เนื่องจากสำหรับ rsync นี้เป็นตัวเลือกการถ่ายโอน ไม่ใช่ตัวเลือกในการยกเว้น\n" "ดังนั้นไฟล์ขนาดใหญ่ที่ได้รับการสำรองข้อมูลมาก่อนหน้านี้จะยังคงอยู่ในสแนปช็อต\n" "แม้ว่าไฟล์เหล่านั้นจะเปลี่ยนแปลงลง" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&ลบโดยอัตโนมัติ" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "เก่ากว่า:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "ปี" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "หากพื้นที่ว่างน้อยกว่า:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "หากพื้นที่ว่างของไอโนด์น้อยกว่า:" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "การลบอย่างฉลาด:" #: qt/settingsdialog.py:793 #, fuzzy msgid "Run in background on remote host." msgstr "เรียกใช้ในพื้นหลังบนโฮสต์ระยะไกล" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "ทดลอง" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "เก็บรักษาสแนปช็อตทั้งหมดสำหรับช่วงเวลาล่าสุด" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 #, fuzzy msgid "day(s)." msgstr "วัน" #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "เก็บรักษาสแนปช็อตหนึ่งต่อวันสำหรับช่วงเวลาล่าสุด" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "เก็บรักษาสแนปช็อตหนึ่งต่อสัปดาห์สำหรับช่วงเวลาล่าสุด" #: qt/settingsdialog.py:818 #, fuzzy msgid "week(s)." msgstr "สัปดาห์" #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "เก็บรักษาสแนปช็อตหนึ่งต่อเดือนสำหรับช่วงเวลาล่าสุด" #: qt/settingsdialog.py:825 #, fuzzy msgid "month(s)." msgstr "เดือน" #: qt/settingsdialog.py:828 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "เก็บรักษาสแนปช็อตหนึ่งต่อปีสำหรับทุกปี" #: qt/settingsdialog.py:837 #, fuzzy msgid "Don't remove named snapshots." msgstr "ไม่ลบสแนปช็อตที่มีชื่อ" #: qt/settingsdialog.py:849 msgid "&Options" msgstr "& ตัวเลือก" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "เปิดใช้งานการแจ้งเตือน" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "ปิดใช้งานการสร้างสแนปช็อตเมื่อใช้งานบนแบตเตอรี่" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "ไม่สามารถรับสถานะพลังงานจากระบบได้" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "เรียกใช้สแนปช็อตเพียงหนึ่งตัวในเวลาเดียวกันเท่านั้น" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "สแนปช็อตอื่น ๆ จะถูกระบบปิดกั้นจนกว่าสแนปช็อตปัจจุบันจะเสร็จสมบูรณ์\n" "นี่เป็นตัวเลือกที่มีผลทั่วโลก ดังนั้นจะมีผลต่อโปรไฟล์ทั้งหมดสำหรับผู้ใช้รายนี้\n" "แต่คุณจำเป็นต้องเปิดใช้งานตัวเลือกนี้สำหรับผู้ใช้อื่น ๆ ด้วย" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "สำรองข้อมูลไฟล์ที่ถูกแทนที่เมื่อกู้คืน" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "เวอร์ชันใหม่ของไฟล์จะถูกเปลี่ยนชื่อด้วยคำติดท้าย {suffix} ก่อนที่จะถูกคืนค่า" " หากคุณไม่ต้องการใช้งานอีกต่อไป คุณสามารถลบไฟล์เหล่านี้ด้วย {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "ดำเนินการต่อเมื่อเกิดข้อผิดพลาด (เก็บสแนปช็อตที่ไม่สมบูรณ์)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "ใช้ checksum เพื่อตรวจจับการเปลี่ยนแปลง" #: qt/settingsdialog.py:899 #, fuzzy msgid "Take a new snapshot whether there were changes or not." msgstr "สร้างสแนปช็อตใหม่โดยไม่ว่าจะมีการเปลี่ยนแปลงหรือไม่ก็ตาม" #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "ระดับบันทึก:" #: qt/settingsdialog.py:911 msgid "None" msgstr "ไม่มี" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "&ตัวเลือกขั้นสูง" #: qt/settingsdialog.py:936 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "เปลี่ยนตัวเลือกเหล่านี้เท่านั้นหากคุณรู้ว่าต้องการทำอะไรจริงๆ" #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "รัน 'rsync' ด้วย '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "เป็นงาน cron" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "บนโฮสต์ระยะไกล" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "เมื่อสร้างสแนปช็อตด้วยตนเอง" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(โปรดติดตั้ง 'nocache' เพื่อเปิดใช้งานตัวเลือกนี้)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "บนเครื่องใช้งานท้องถิ่น" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "เปลี่ยนเส้นทาง stdout เป็น /dev/null ใน cronjobs" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "เปลี่ยนเส้นทาง stderr เป็น /dev/null ใน cronjobs" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "จำกัดการใช้แบนด์วิดธ์ของ rsync:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/sec" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "สงวน ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "สงวนแอตทริบิวต์ขยาย(xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "คัดลอกลิงก์ที่ไม่ปลอดภัย (ทำงานเฉพาะลิงก์ที่เป็นแบบสมบูรณ์)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, fuzzy, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "ตัวเลือกจะต้องอยู่ในเครื่องหมายคำพูด เช่น {example}" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "วางตัวเลือกเพิ่มเติมให้กับ rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "เพิ่มคำนำหน้าให้กับคำสั่ง SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "คำนำหน้าที่จะเรียกใช้ก่อนทุกคำสั่งบนเครื่องโฮสต์ระยะไกล\n" "ตัวแปรต้องถูกหนีเอสด้วย $FOO\n" "การตั้งค่านี้ไม่มีผลต่อ rsync ดังนั้นในการเพิ่มคำนำหน้าสำหรับ rsync ให้ใช้ \"%(cbRsyncOptions)s\" พร้อมกับ\n" " %(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "คุณแน่ใจว่าต้องการลบไฟล์เวอร์ชันใหม่ทั้งหมดใน {name} หรือไม่?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "แนะนำอย่างมาก" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "" #: qt/settingsdialog.py:2169 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "คุณแน่ใจว่าต้องการลบสแนปช็อตหรือไม่" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "ไม่สามารถเขียน crontab ใหม่ได้" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "ค่าเริ่มต้น" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "คำสั่ง:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "พารามิเตอร์:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "ใช้ %1 และ %2 สำหรับพารามิเตอร์เส้นทาง" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "เฉพาะสแนปช็อตที่แตกต่างเท่านั้น" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "รายการเฉพาะสแนปช็อตที่เท่ากับ: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "ตรวจสอบลึก (มากขึ้นในความแม่นยำ แต่ช้ากว่า)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "ลบ" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "เลือกทั้งหมด" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "ไปยัง" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "& ตัวเลือก" #: qt/snapshotsdialog.py:355 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "คุณไม่สามารถเปรียบเทียบสแนปช็อตกับตัวเองได้" #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "คุณต้องการลบ \"{file}\" ในสแนปช็อต \"{snapshot_id}\" จริงหรือไม่?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "คุณต้องการลบ \"{file}\" ในสแนปช็อต {count} รายการ จริงหรือไม่?" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "ไม่สามารถเพิกถอนสิ่งนี้ได้!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "ยกเว้น \"{path}\" จากสแนปช็อตในอนาคตหรือไม่?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "ไม่พบ crontab คุณแน่ใจว่าติดตั้ง\n" #~ " cron แล้วหรือไม่? \n" #~ "หากไม่ได้ติดตั้ง คุณควรปิดใช้งานการสำรองข้อมูลอัตโนมัติทั้งหมด" #~ msgid "Full snapshot path" #~ msgstr "เส้นทางสแนปช็อตทั้งหมด" #~ msgid "Mode" #~ msgstr "โหมด" #~ msgid "Profile" #~ msgstr "โปรไฟล์" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "โปรไฟล์ '{profile}': ป้อนรหัสผ่านสำหรับ {mode}: " #~ msgid "WARNING" #~ msgstr "คำเตือน" #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} ใช้ EncFS เพื่อการเข้ารหัสข้อมูล " #~ "การตรวจสอบความปลอดภัยล่าสุดพบว่ามีเวกเตอร์การโจมตีหลายรูปแบบสำหรับ EncFS " #~ "โปรดดู \"A NOTE ON SECURITY\" ใน \"man backintime\"" backintime-1.5.2/common/po/tr.po000066400000000000000000001574151465446530500165510ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-22 07:07+0000\n" "Last-Translator: buhtz \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Uyarı" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Ana profil" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Yerel (EncFS şifreli)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (EncFS şifreli)" #: common/config.py:309 msgid "Local" msgstr "Yerel" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH gizli anahtarı" #: common/config.py:314 msgid "Local encrypted" msgstr "Yerel olarak şifrelenmiş" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Şifreleme" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH şifrelenmiş" #: common/config.py:327 msgid "Default" msgstr "Varsayılan" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Anlık görüntü dizini geçersiz!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Yedekleme için en az bir klasör seçmelisiniz!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Yedekleme klasörü dahil edilemez." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Yedekleme alt klasörü dahil edilemez." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Hatalı seçenek. {path} bir klasör değil." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Sunucu/Kullanıcı/Profil Kimliği boş olamaz." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Yazılamıyor: {path}\n" "Yazma izniniz olduğuna emin misiniz?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "{path} için girilen hedef dosya sistemi hard-link desteklemeyen FAT ile " "biçimlendirilmiş. Lütfen özgün Linux dosya sistemi kullanın." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. 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/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Linkleri kopyala (sembolik linklerin hedef dosyalarını kopyalar)" #: common/config.py:497 msgid "Expert Options" msgstr "Uzman Seçenekleri" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "{path} için hedef dosya sistemi bir sshfs ile bağlanmış paylaşımdır. Sshfs " "sabit bağlantıları desteklemez. Lütfen 'SSH' modunu kullanın." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Yeni crontab yazımı başarısız oldu." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Crontab komutu mevcut olmasına rağmen Cron çalışmıyor. Planlanan yedekleme " "işleri çalışmayacak. Cron yüklü olabilir ancak etkinleştirilmemiş olabilir. " "\"systemctl enable cron\" komutunu deneyin veya GNU Linux dağıtımınızın " "destek kanallarına başvurun." #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Udev zamanlama kipi {mode} ile çalışmıyor" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "{path} için UUID bulunamadı" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Yapılandırma kaydedilemedi" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Yapılandırma yüklenemedi" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\" profili zaten var." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Son profil kaldırılamaz." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "'{command}' bağlanamıyor" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Şifrelenmiş klasör için yapılandırma bulunamadı." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Yeni şifreli klasör oluşturulsun mu?" #: common/encfstools.py:151 msgid "Cancel" msgstr "İptal" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Lütfen parolayı onaylayın." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Parola eşleşmiyor." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Anlık görüntü al" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "{mountprocess} , {mountpoint} den ayrılamıyor." #: common/mount.py:696 #, fuzzy, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} bulunamadı. Lütfen {installcommand} komutunu kullanarak yükleyin." #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Bağlantı noktası {mntpoint} boş değil." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Şifrenizi {mode}profili için giriniz \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "BAŞARISIZ" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "İzinleri geri yükle" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Tamamlandı" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Pille çalışırken yedeklemeyi erteleme" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Anlık görüntü klasörü bulunamadı.\n" "Çıkarılabilir bir sürücüyse lütfen takınız." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "%s saniye bekliyor." msgstr[1] "%s saniye bekliyor." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "{snapshot_id} anlık görüntüsü alınamadı." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Sonlandırılıyor" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Klasör oluşturulamadı" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Yapılandırma dosyası kaydediliyor…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "İzinler kaydediliyor…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Devam edilebilecek {snapshot_id} artık anlık görüntüsü bulundu." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Son çalıştırmadan kalan {snapshot_id} klasörü kaldırılıyor" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Klasör kaldırılamıyor" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Anlık görüntü al" #: common/snapshots.py:1417 msgid "Success" msgstr "Başarılı" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Hata sebebiyle kısmen aktarıldı" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Kaybolan kaynak dosyalar nedeniyle kısmi aktarım (bkz. 'man rsync')" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' sonlandı, çıkış kodu: {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Daha fazla ayrıntı için bakınız; 'man rsync'" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Negatif rsync çıkış kodları sinyal numaralarıdır, bkz. 'kill -l' ve 'man " "kill'" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Hiçbir değişiklik yok, yeni anlık görüntü almak gereksiz" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{new_path} yolu {path} olarak yeniden adlandırılamadı" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Akıllı kaldırma" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Eski anlık görüntüleri kaldırma" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "En az boş alan korunmaya çalışılıyor" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "En az {perc} boş düğümü korumaya çalışıyor" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Şimdi" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs} bağlanamadı" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent bulunamadı. Lütfen kurulu olduğundan emin olun." #: common/sshtools.py:444 msgid "" "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/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "{host} için {cipher} şifresi başarısız oldu." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Uzak yol mevcut ancak bir dizin değil." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Uzak yol yazılabilir değil." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Uzak yol çalıştırılabilir değil." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Uzak klasör oluşturulamadı." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Uzak bilgisayar {host} desteklemediği komut: {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Daha fazla yönerge için 'man backintime'a bakın" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "{host} ana bilgisayarındaki kontrol komutları bilinmeyen bir hata döndürdü" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Uzak sunucu {host} sabit bağlantıları desteklemiyor" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "" "Genel SSH anahtarını \"{pubkey}\" uzak ana bilgisayar \"{host}\" üzerine " "kopyala." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "\"{user}\" kullanıcısı için parola giriniz." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Hakkında" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Yazanlar" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Çeviriler" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Lisans" #: qt/app.py:169 msgid "Shortcuts" msgstr "Kısayollar" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Bu klasör, \n" "seçili anlık görüntüde yok." #: qt/app.py:256 msgid "Add to Include" msgstr "Dahil Et'e Ekle" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Dışla'ya Ekle" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "{app_name} herhangi bir yapılandırma bulunamadığı için ilk defa " "çalıştırılıyor gibi görünüyor." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Var olan bir yapılandırmayı içe aktarın (yedekeleme hedef dosyasından veya " "farklı bir bilgisayardan)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Klasör oluşturulamadı." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Eğer bu bir çıkarılabilir sürücüde ise lütfen takın ve ardından Tamam'a " "basın." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Anlık görüntü al" #: qt/app.py:483 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." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Anlık görüntü kipi (sağlama toplamı kipi)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Dosya değişikliklerini algılamak için sağlama toplamlarını kullan." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Anlık görüntü sürecini duraklat" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Anlık görüntü sürecini sürdür" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Anlık görüntü sürecini durdur" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Anlık görüntü listesini yenile" #: qt/app.py:508 msgid "Name snapshot" msgstr "Anlık görüntüyü adlandır" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Anlık görüntüyü sil" #: qt/app.py:516 msgid "View snapshot log" msgstr "Anlık görüntü günlüğünü görüntüle" #: qt/app.py:520 msgid "View last log" msgstr "Son günlüğü görüntüle" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Profilleri yönet…" #: qt/app.py:528 msgid "Shutdown" msgstr "Kapat" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Anlık görüntü tamamlandığında sistemi kapat." #: qt/app.py:532 msgid "Setup language…" msgstr "Kurulum dili…" #: qt/app.py:536 msgid "Exit" msgstr "Çıkış" #: qt/app.py:540 msgid "Help" msgstr "Yardım" #: qt/app.py:544 msgid "Profiles config file" msgstr "Profiller yapılandırma dosyası" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Web Sitesi" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Değişiklik Günlüğü" #: qt/app.py:553 msgid "FAQ" msgstr "SSS" #: qt/app.py:556 msgid "Ask a question" msgstr "Soru Sor" #: qt/app.py:559 msgid "Report a bug" msgstr "Hata Bildir" #: qt/app.py:562 msgid "Translation" msgstr "Çeviri" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Çeviriye katılım mesajını tekrar gösterir." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Şifreleme Geçişi (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "EncFS kaldırma mesajını tekrar gösterir." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Geri Yükle" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Seçilen dosyaları veya klasörleri özgün hedefe geri yükle." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Şuraya geri yükle …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Seçilen dosyaları veya klasörleri yeni hedefe geri yükle." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "Gösterilen klasörü ve tüm içeriğini özgün hedefe geri yükle." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Şu anda gösterilen klasörü ve tüm içeriğini yeni hedefe geri yükle." #: qt/app.py:595 msgid "Up" msgstr "Yukarı" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Gizli dosyaları göster" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Anlık görüntüleri karşılaştır…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Zaman Tüneli" #: qt/app.py:665 msgid "&Backup" msgstr "&Yedekle" #: qt/app.py:676 msgid "&Restore" msgstr "&Geri Yükle" #: qt/app.py:682 msgid "&Help" msgstr "Y&ardım" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Eğer bu pencereyi kapatırsanız, Zaman Tüneli anlık görüntü işlemi " "tamamlandığında sisteminizi kapatamayacak." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Gerçekten kapatmak istiyor musunuz?" #: qt/app.py:987 msgid "Working:" msgstr "Çalışıyor:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Tamamlandı, yedekleme gerekmiyor" #: qt/app.py:1044 msgid "Working" msgstr "Çalışıyor" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Hata" #: qt/app.py:1076 msgid "Sent" msgstr "Gönder" #: qt/app.py:1077 msgid "Speed" msgstr "Hız" #: qt/app.py:1078 msgid "ETA" msgstr "ETA" #: qt/app.py:1140 msgid "Global" msgstr "Küresel" #: qt/app.py:1141 msgid "Root" msgstr "Kök Dizini" #: qt/app.py:1142 msgid "Home" msgstr "Ev" #: qt/app.py:1170 msgid "Backup folders" msgstr "Yedekleme klasörleri" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Anlık Görüntü Adı" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Bu anlık görüntüyü silmek istediğinizden emin misiniz?" msgstr[1] "Bu anlık görüntüleri silmek istediğinizden emin misiniz?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Üzerine yazmadan veya kaldırmadan önce\n" "{suffix} ile yerel dosyaları yedekleyin." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them 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:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Yalnızca var olmayan ya da hedefteki ögelerden\n" "daha yeni olanları geri yükle.\n" "\"rsync --update\" seçeneğini kullanarak." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Özgün klasördeki daha yeni ögeleri kaldır." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Seçilen dosyaları veya klasörleri orijinal hedefe geri yükleyin ve anlık " "görüntüde bulunmayan dosya veya klasörleri silin. Çok dikkatli olun çünkü bu" " işlem, anlık görüntü alınırken hariç tutulan dosya ve klasörleri " "silecektir." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Bu ögeyi gerçekten {path} yeni klasörüne\n" "geri yüklemek istiyor musunuz?" msgstr[1] "" "Bu ögeleri gerçekten {path} yeni klasörüne\n" "geri yüklemek istiyor musunuz?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Bu ögeyi gerçekten geri yüklemek istiyor musunuz?" msgstr[1] "Bu ögeleri gerçekten geri yüklemek istiyor musunuz?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "{path} içindeki tüm yeni dosyaları kaldırmak istediğinizden emin misiniz?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Özgün klasördeki tüm yeni dosyaları kaldırmak istediğinizden emin misiniz?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}UYARI{BOLDEND}: Dosyaları dosya sistemi kökünden silmek sisteminizin " "tamamını bozabilir." #: qt/app.py:1750 msgid "Snapshot" msgstr "Anlık görüntü" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "{path} geri yükle" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "{path} geri yükle, hedef …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Dil ayarları yalnızca Zamanda Geri Dönüş yeniden başlatıldıktan sonra etkili" " olur." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "EncFS desteği yakın gelecekte sonlandırılacak. Profil için artık o modu " "kullanmanız önerilmiyor." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Şifreli yedeklemelerin sürekli desteklenmesi için bir değiştirme kararı hala" " bekleniyor, proje kaynaklarına ve katkı sağlayıcıların uygunluğuna bağlı " "olarak. Daha fazla detay bu {whitepaper}'da bulunabilir." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "Beyaz Kitap" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Şifreli anlık görüntü profilleri için destek önemli değişiklikler geçiriyor " "ve EncFS yakın gelecekte kaldırılacaktır." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Aşağıdaki profil(ler) EncFS ile şifreleme kullanıyor:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Şifreli yedeklemelerin sürekli desteklenmesi için bir değiştirme kararı hala" " bekleniyor, proje kaynaklarına ve katkı sağlayıcıların uygunluğuna bağlı " "olarak. Kullanıcılar bu tartışmaya katılmaya davet ediliyor. İleriki adımlar" " hakkında güncel bilgiler bu {whitepaper}'da bulunabilir." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Bu mesaj bir daha gösterilmeyecek. Bu iletişim kutusu her zaman yardım " "menüsünden erişilebilir." #: qt/encfsmsgbox.py:87 #, fuzzy msgid "Your Back In Time Team" msgstr "Zaman Tüneli" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Kurulum dili" #: qt/languagedialog.py:92 msgid "System default" msgstr "Sistem öntanımlısı" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "İşletim sistemi dilini kullanın." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Çevrilen: {percent}" #: qt/languagedialog.py:188 #, 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 "" "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/languagedialog.py:217 msgid "translation platform" msgstr "çeviri platformu" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Sizin çeviriniz" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Son Günlük Görünüm" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Anlık Görüntü Günlük Görünümü" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Profil:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Anlık görüntüler:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Filtrele:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Tümü" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Değişiklikler" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Hatalar" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Bilgi" msgstr[1] "Bilgiler" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync aktarımı başarısızlıkları (deneysel)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Hata, [I] Bilgi, [C] Değiştir" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "kod çözümleme yolları" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Soru" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Profil: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Son Günlüğü Görüntüle" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "{appname}ʼı Başlat" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Çalışıyor…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Gönderilen:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Hız:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "Tahmini Varış Süresi:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Anlık görüntüler" #: qt/qttools.py:427 msgid "Today" msgstr "Bugün" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Dün" #: qt/qttools.py:443 msgid "This week" msgstr "Bu hafta" #: qt/qttools.py:450 msgid "Last week" msgstr "Geçen hafta" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Bu bir anlık görüntü DEĞİL, yerel dosyalarınızın canlı bir görüntüsüdür" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Son denetim {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Tüm Günlüğü Göster" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH Vekil" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Ana Makine:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Port:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Kullanıcı:" #: qt/settingsdialog.py:120 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 "" "Hedef ana bilgisayara bu vekil sunucu (aynı zamanda atlama sunucusu olarak " "da bilinir) üzerinden bağlanın. Ayrıntılar için \"ssh\" komut belgesindeki " "\"-J\" veya \"ssh_config\" man sayfasındaki \"ProxyJump\" seçeneğine bakın." #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "Profilleri yönet" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Düzenle" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Ekle" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Kaldır" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Genel" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Kip:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Anlık görüntülerin nereye kaydedileceği" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Klasör" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH Ayarları" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Yol:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Şifre:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Özel Anahtar:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Var olan bir özel anahtar dosyası seçin (normalde \"id_rsa\" olarak " "adlandırılır)" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Parola olmadan yeni SSH anahtarı oluştur (özel anahtar dosyası zaten " "seçiliyse buna izin verilmez)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Parola" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Parolayı Anahtarlığa Kaydet" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Cron için Parolayı Önbellekle (Güvenlik sorunu: root parolayı okuyabilir)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Gelişmiş" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Tam anlık görüntü yolu:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Zamanlama" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Devre dışı" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Her Başlatıldığında/Yeniden Başlatıldığında" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "{n} dakikada bir" msgstr[1] "{n} dakikada bir" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Her Saat" msgstr[1] "Her {n} saatte" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "{n} saatte bir" msgstr[1] "{n} saatte bir" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Özel saatler" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Her gün" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Tekrar Tekrar (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Sürücü bağlandığında (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Her hafta" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Her ay" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Her yıl" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "Gün:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "Haftanın günü:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Saat:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Saatler:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Back In Time'ı tekrar tekrar çalıştır. Bu, bilgisayar düzenli olarak " "çalışmıyorsa kullanışlıdır." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Her:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Saat" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Gün" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Hafta" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Ay" #: qt/settingsdialog.py:555 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 "" "Sürücü bağlanır bağlanmaz Back In Time'ı çalıştır (yalnızca X günde bir).\n" "Sizden sudo parolası istenecektir." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Hata ayıklama mesajlarının kaydetmeyi etkinleştir" #: qt/settingsdialog.py:568 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:570 msgid "" "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:583 msgid "&Include" msgstr "&Dahil Et" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Dosyaları ve klasörleri dahil et" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Dosya Ekle" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Klasör Ekle" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "Dışl&a" #: qt/settingsdialog.py:625 #, 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}Bilgi{ENDBOLD}: 'SSH şifreli' modda, yalnızca tek veya çift yıldızlar " "işlevseldir (örneğin {example2}). Diğer türdeki joker karakterler ve " "desenler göz ardı edilir (örneğin {example1}). Dosya adları, EncFS " "tarafından şifrelendiği için bu modda öngörülemez." #: qt/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Dışlama deseni, dosyalar veya klasörler" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Öntanımlı Ekle" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Dışarıda bırakılacak dosyaların boyutundan büyük olanlar:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "" "{size_unit} biriminde belirtilen değerden büyük dosyaları dışarıda bırakın." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "'Tam rsync modu' devre dışı bırakıldığında, bu yalnızca rsync için bir " "aktarım seçeneği olduğu için yeni dosyaları etkiler. Bu nedenle, daha önce " "yedeklenmiş büyük dosyalar, değiştirilmiş olsalar bile anlık görüntülerde " "korunacaktır." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Kendiliğinden Kaldır" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Daha eski:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Yıl" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Eğer boş alan şundan azsa:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Eğer boş inode sayısı şundan azsa:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Akıllı kaldırma:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Uzak makinede arka planda çalıştır." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "DENEYSEL" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Tüm anlık görüntüleri şu kadar süre için sakla" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "gün" #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Son X gün için her gün bir anlık görüntü sakla" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Son X hafta için her hafta bir anlık görüntü sakla" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "hafta" #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Son X ay için her ay bir anlık görüntü sakla" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "ay" #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Tüm yıllar için her yıl bir anlık görüntü sakla." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Adlandırılmış anlık görüntüleri kaldırma." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Seçenekler" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Bildirimleri etkinleştir" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Pille çalışırken anlık görüntüleri devre dışı bırak" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Güç durumu sistemden alınamıyor" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Her seferinde yalnızca bir anlık görüntü çalıştır" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Diğer anlık görüntüler şu anlık yapılan anlık görüntüleme işlemi bitene " "kadar engellenecek. Bu global bir seçenektir, bu nedenle bu kullanıcı için " "tüm profilleri etkileyecektir. Ancak diğer kullanıcılar için de bunu " "etkinleştirmeniz gerekecek." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Geri yüklemede değiştirilen dosyaları yedekle" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Dosyaların daha yeni sürümleri geri yüklenmeden önce sonuna {suffix} " "eklenerek yeniden adlandırılacak. Onlara artık ihtiyacınız yoksa {cmd} ile " "kaldırabilirsiniz" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Hatalarda devam et (eksik anlık görüntüleri tut)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Değişiklikleri algılamak için sağlama toplamı kullan" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Değişiklik olsun ya da olmasın yeni anlık görüntü al." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Günlük Düzeyi:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Yok" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "&Uzman Seçenekleri" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Dikkat: Bu seçenekleri yalnızca ne yaptığınızı gerçekten biliyorsanız " "değiştirin." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "'rsync' komutunu '{cmd}' ile çalıştır:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "zamanlanmış görev olarak" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "uzak makinede" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "elle anlık görüntü alınırken" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Bu seçeneği etkinleştirmek için lütfen 'nocache' kurun)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "yerel makinede" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Zamanlanmış görevlerde stdout'u /dev/null'a yönlendir." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Cron, bir MTA yüklü ise, cron işlerinin çıktısını ekli bir e-posta ile " "otomatik olarak gönderecektir." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Zamanlanmış görevlerde stderr'ı /dev/null'a yönlendir." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Eğer bir MTA yüklü ise, Cron otomatik olarak cron işlerinin hatalarını " "içeren bir e-posta gönderecektir." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Rsync bant genişliği kullanımını sınırla:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/sn" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "ACL koru" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Genişletilmiş öznitelikleri koru (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Güvenli olmayan bağlantıları kopyala (yalnızca mutlak bağlantılarla çalışır)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Yalnızca bir dosya sistemiyle sınırla" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Seçenekler çift tırnak içine alınmalıdır, ör: {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Ek seçenekleri rsyncʼa yapıştır" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "SSH komutlarına ön ek ekle" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Uzak ana bilgisayar üzerinde her komuttan önce çalıştırılacak önek." #: qt/settingsdialog.py:1188 #, 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 "" "Değişkenler $FOO ile kaçınılmalıdır. Bu rsync'i etkilemez. Rsync için bir ön" " ek eklemek için \"{example_value}\" ve {rsync_options_value} kullanın." #: qt/settingsdialog.py:1196 msgid "default" msgstr "öntanımlı" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Uzak makinenin çevrim içi olduğunu denetle" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Uyarı: Eğer devre dışı bırakılırsa ve uzak ana bilgisayar erişilemezse, bu " "bazı garip hatalara neden olabilir." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "" "Uzak ana bilgisayarın gerekli tüm komutları destekleyip desteklemediğini " "kontrol edin." #: qt/settingsdialog.py:1221 msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Uyarı: Eğer devre dışı bırakılırsa ve uzak ana bilgisayar gerekli tüm " "komutları desteklemiyorsa, bu bazı garip hatalara neden olabilir." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Yapılandırma Geri Yükle" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Kullanıcı Geri Çağrısını Düzenle" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "EncFS desteği öngörülebilir gelecekte durdurulacaktır. Sürdürülebilir " "şifreli yedekleme desteği ile ilgili karar, proje kaynakları ve katkıda " "bulunanların müsaitliğine bağlı olarak hala beklemede. Daha fazla ayrıntı " "{whitepaper}'da bulunabilir." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Yeni profil" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Profili yeniden adlandır" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "\"{name}\" profilini silmek istediğinizden emin misiniz?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "{BOLD}Önemle Önerilir{ENDBOLD}: (Tüm öneriler zaten dahil edildi.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Önemle Tavsiye Edlilir{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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 "" "Ö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:1681 msgid "You did not choose a private key file for SSH." msgstr "SSH için bir gizli anahtar dosyası seçmediniz." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "Yeni bir şifresiz genel/özel anahtar çifti oluşturmak ister misiniz?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Özel anahtar dosyası \"{file}\" yok." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Şifresiz girişi etkinleştirmek için genel SSH anahtarınızı uzak ana " "bilgisayara kopyalamak ister misiniz?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "{host} ana bilgisayarının kimliği doğrulanamıyor." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} anahtar parmak iziniz:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Lütfen bu parmak izini doğrulayın. 'known_hosts' dosyanıza eklemek ister " "misiniz?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Dışlama deseni" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Dosyayı dışla" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Klasörü dışla" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Dosyayı dahil et" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" bir sembolik bağlantı. Bağlantılı hedef, siz onu da dahil edene kadar yedeklenmeyecek.\n" "Bunun yerine sembolik bağlantının hedefini dahil etmek ister misiniz?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Klasörü dahil et" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Anlık görüntüler klasörünü değiştirmek istediğinizden emin misiniz?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "{path} yolunda yeni SSH anahtarı oluşturulamadı" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" "'SSH şifrelenmiş' modunda bu desen işlevli olmadığı için devre dışı " "bırakıldı." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(varsayılan: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "devre dışı" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "etkin" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Ayarları İçe Aktar" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Yapılandırma bulunamadı" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "İçe Aktar" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Ayarlama dosyasının içe aktarılacağı anlık görüntü klasörünü seçin. Yol şu " "şekilde olabilir: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Eğer klasör harici veya uzak bir sürücüde bulunuyorsa, önceden manuel olarak" " bağlanmalıdır." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Anlık görüntüleri karşılaştırmayla ilgili seçenekler" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Komut:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Parametreler:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Yol parametreleri olarak %1 ve %2 kullan" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Lütfen farklı fark komutu belirleyin veya İptal'e basın." #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" "Bu komut \"{cmd}\" bu sistemde bulunmamaktadır. Lütfen farklı bir şey " "deneyin veya İptal'e basın." #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Fark komutu için parametre belirlenmedi. Varsayılan değerler kullanılıyor " "\"{params}\"." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Yalnızca fark anlık görüntüleri" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Yalnızca şuna eşit anlık görüntüleri listele:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Derin denetim (daha doğru, ancak yavaş)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Sil" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Tümünü Seç" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Karşılaştır" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Git" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Seçenekler" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Bir anlık görüntüyü kendisiyle karşılaştıramazsınız." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "{snapshot_id} anlık görüntüsündeki {file} dosyasını gerçekten silmek istiyor" " musun?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" "{count} anlık görüntü içindeki {file} dosyasını gerçekten silmek istiyor " "musun?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "UYARI: Bu işlemin geri alınması mümkün değil." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "{path} yolu gelecek anlık görüntülerden dışlansın mı?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Crontab dosyası bulunamadı. Cron yüklü olduğundan emin misiniz? Değilse, tüm" #~ " otomatik yedeklemeleri devre dışı bırakmalısınız." #~ msgid "Full snapshot path" #~ msgstr "Tam anlık görüntü yolu" #~ msgid "Mode" #~ msgstr "Kip" #~ msgid "Profile" #~ msgstr "Profil" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Profil '{profile}': {mode} kipi için parola giriniz: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Kullanıcı geri çağrı komut dosyasındaki Shebang yürütülebilir değil." #~ msgid "WARNING" #~ msgstr "UYARI" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "1.7.2 ve önceki encfs sürümlerinin --reverse seçeneğinde bir hata var. " #~ "Lütfen encfs'yi güncelleyin." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "kullanıcı geri çağrı betiğinde shebang (#!/bin/sh) satırı yok." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app}, şifreleme için EncFS kullanıyor. Yakın tarihli bir güvenlik " #~ "araştırması, bunun için birkaç olası saldırı ihtimalini ortaya çıkardı. " #~ "Lütfen \"man backintime\" içindeki \"GÜVENLİK ÜZERİNE BİR NOT\"a bakın." backintime-1.5.2/common/po/uk.po000066400000000000000000002033241465446530500165320ustar00rootroot00000000000000# Ukrainian 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: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-06 06:28+0000\n" "Last-Translator: buhtz \n" "Language-Team: Ukrainian \n" "Language: uk\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.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Попередження" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Основний профіль" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Локальний (зашифрований EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (зашифрований EncFS)" #: common/config.py:309 msgid "Local" msgstr "Локальний" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Приватний ключ SSH" #: common/config.py:314 msgid "Local encrypted" msgstr "Локальний зашифрований" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Шифрування" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH зашифрований" #: common/config.py:327 msgid "Default" msgstr "За замовчуванням" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Профіль: «{name}»" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Неправильна тека!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Потрібно обрати хоча б одну теку для резервування!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Не можна включати теку резервної копії." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Не можна включати підтеку резервної копії." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Неправильний параметр. {path} не є текою." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Поля «Сервер», «Користувач» і «Профіль» не можуть бути порожніми." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Неможливо записати в: {path}\n" "Ви впевнені, що маєте право на запис?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "" "Вказане розташування {path} має файлову систему FAT, яка не підтримує " "жорсткі посилання. Використовуйте файлові системи для Linux." #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "Вказане розташування {path} — спільний SMB-ресурс. Переконайтеся, що SMB-" "сервер підтримує символьні посилання, або активуйте {copyLinks} в " "{expertOptions}." #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "Копіювати посилання (слідувати за символьними посиланнями)" #: common/config.py:497 msgid "Expert Options" msgstr "Розширені налаштування" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "" "Вказане розташування {path} — спільний sshfs-ресурс. Sshfs не підтримує " "жорсткі посилання. Будь ласка, використовуйте режим «SSH»." #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "Не вдалося записати новий crontab." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "Cron не запускається, незважаючи на наявність команди crontab. Заплановані " "завдання резервного копіювання не буде виконано. Можливо, Cron встановлено, " "але не ввімкнено. Спробуйте команду «systemctl enable cron» або зверніться " "до каналів підтримки Вашого дистрибутиву GNU Linux." #: common/config.py:1746 #, 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 Service " "«{dbus_interface}» недоступний" #: common/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Розклад udev не працює з режимом {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Не вдалося знайти UUID для {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Не вдалося зберегти конфігурацію" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Не вдалося завантажити конфігурацію" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Профіль «{name}» вже існує." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Неможливо видалити останній профіль." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Не вдається змонтувати «{command}»" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Конфігурацію для зашифрованої теки не знайдено." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Створити нову зашифровану теку?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Скасувати" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Підтвердіть пароль." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Пароль не збігається." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Зробити копію" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Не вдається розмонтувати {mountprocess} з {mountpoint}." #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "" "{command} не знайдено. Встановіть її (напр., за допомогою " "«{installcommand}»)" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Точка монтування {mntpoint} не порожня." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Введіть пароль для профілю {mode} «{profile}»:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "НЕВДАЧА" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Відновлення дозволів" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Виконано" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Вимкнення резервування при роботі від батареї" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Не вдається знайти теку резервних копій.\n" "Якщо вона знаходиться на знімному диску, підключіть його." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Очікування %s секунда." msgstr[1] "Очікування %s секунди." msgstr[2] "Очікування %s секунд." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Не вдалося зробити копію {snapshot_id}." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Завершення" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Не вдається створити теку" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Збереження файлу конфігурації…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Збереження дозволів…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Незавершену копію {snapshot_id} можна продовжити." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Видалення незавершеної копії {snapshot_id} від попереднього запуску" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Не вдається видалити теку" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Створення резервної копії" #: common/snapshots.py:1417 msgid "Success" msgstr "Успішно" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Переміщено частково, через помилку" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Переміщено частково, оскільки вихідні файли зникли (див. «man rsync»)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "«rsync» завершився з кодом виходу {exit_code}" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "Докладніше див. «man rsync»" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Від'ємні значення кодів виходу rsync є сигнальними номерами, див. «kill -l» " "і «man kill»" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Немає змін, резервування не потрібне" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Не вдається перейменувати {new_path} в {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "Розумне видалення" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Видалення старих копій" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "Спроба зберегти мінімум вільного місця" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Спроба зберегти мінімум {perc} вільних айнодів" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "Зараз" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Не вдається змонтувати {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent не знайдено. Переконайтеся, що його встановлено." #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Не вдалося розблокувати приватний ключ SSH. Пароль неправильний або " "недоступний для cron." #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Помилка шифру {cipher} для сервера {host}." #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "Віддалений шлях існує, але це не тека." #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "Неможливо записати у віддалений шлях." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Віддалений шлях неможливо виконати." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Не вдається створити віддалений шлях." #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Сервер {host} не підтримує {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Дивіться «man backintime» для подальших інструкцій" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Перевірка команд на сервері {host} повернула невідому помилку" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Сервер {host} не підтримує жорсткі посилання" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "Копіювати публічний ключ SSH «{pubkey}» на сервер «{host}»." #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Будь ласка, введіть пароль для «{user}»." #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Про програму" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Автори" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Переклади" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Ліцензія" #: qt/app.py:169 msgid "Shortcuts" msgstr "Скорочення" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Такої теки немає\n" "в обраній резервній копії." #: qt/app.py:256 msgid "Add to Include" msgstr "Включити" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Виключити" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "" "Схоже, що це перший запуск {app_name}, оскільки не знайдено жодної " "конфігурації." #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "" "Імпортувати існуючу конфігурацію (з теки резервної копії або з іншого " "комп'ютера)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "Не вдається знайти теку резервних копій." #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "" "Якщо вона знаходиться на знімному диску, підключіть його і натисніть OK." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Зробити копію" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "Виявляти зміни за датою зміни і розміром." #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "Зробити копію з контрольними сумами" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Виявляти зміни за контрольними сумами." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Зупинити створення копії" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Продовжити створення копії" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Скасувати створення копії" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Оновити список копій" #: qt/app.py:508 msgid "Name snapshot" msgstr "Назвати копію" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Видалити копію" #: qt/app.py:516 msgid "View snapshot log" msgstr "Переглянути журнал копій" #: qt/app.py:520 msgid "View last log" msgstr "Переглянути останній журнал" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Керувати профілями…" #: qt/app.py:528 msgid "Shutdown" msgstr "Вимкнення" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Вимкнути систему після завершення резервування." #: qt/app.py:532 msgid "Setup language…" msgstr "Змінити мову…" #: qt/app.py:536 msgid "Exit" msgstr "Вийти" #: qt/app.py:540 msgid "Help" msgstr "Допомога" #: qt/app.py:544 msgid "Profiles config file" msgstr "Файл конфігурації профілів" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Вебсайт" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Історія змін" #: qt/app.py:553 msgid "FAQ" msgstr "Питання та відповіді" #: qt/app.py:556 msgid "Ask a question" msgstr "Задати запитання" #: qt/app.py:559 msgid "Report a bug" msgstr "Повідомити про помилку" #: qt/app.py:562 msgid "Translation" msgstr "Переклад" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "Показує повідомлення про участь у перекладі." #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "Перехід на нове шифрування (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "Показує повідомлення про видалення EncFS." #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Відновити" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "Відновити обрані файли чи теки до їхнього початкового розташування." #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Відновити до …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "Відновити обрані файли чи теки до вказаного розташування." #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "Відновити поточну теку і весь її вміст до початкового розташування." #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Відновити поточну теку і весь її вміст до вказаного розташування." #: qt/app.py:595 msgid "Up" msgstr "Вгору" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Показати приховані файли" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Порівняти копії…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&Резервування" #: qt/app.py:676 msgid "&Restore" msgstr "&Відновлення" #: qt/app.py:682 msgid "&Help" msgstr "&Допомога" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "" "Якщо закрити це вікно, Back In Time не зможе вимкнути Вашу систему після " "закінчення резервування." #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "Справді закрити?" #: qt/app.py:987 msgid "Working:" msgstr "Виконання:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Виконано, резервування не потрібне" #: qt/app.py:1044 msgid "Working" msgstr "Виконання" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Помилка" #: qt/app.py:1076 msgid "Sent" msgstr "Надіслано" #: qt/app.py:1077 msgid "Speed" msgstr "Швидкість" #: qt/app.py:1078 msgid "ETA" msgstr "Залишилося" #: qt/app.py:1140 msgid "Global" msgstr "Загальні" #: qt/app.py:1141 msgid "Root" msgstr "Root" #: qt/app.py:1142 msgid "Home" msgstr "Home" #: qt/app.py:1170 msgid "Backup folders" msgstr "Теки резервування" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Назва копії" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Бажаєте видалити цю резервну копію?" msgstr[1] "Бажаєте видалити ці резервні копії?" msgstr[2] "Бажаєте видалити ці резервні копії?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Робити копію елементів перед їх перезаписом\n" "або видаленням, додаючи до назви {suffix}." #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "" "Перед відновленням новіші версії файлів буде перейменовано з додаванням " "{suffix}. Якщо вони Вам не потрібні, можна видалити їх за допомогою команди:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Відновити лише елементи, які відсутні або\n" "новіші, ніж ті, що у вказаному розташуванні.\n" "Використовується «rsync --update»." #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Видалити новіші елементи у початковому розташуванні." #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "" "Відновити обрані файли чи теки до початкового розташування і видалити " "файли/теки, яких немає у резервній копії. Будьте дуже обережні, оскільки це " "призведе до видалення файлів і тек, які було виключено під час резервування." #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "Ви справді хочете відновити цей елемент до нової теки\n" "{path}?" msgstr[1] "" "Ви справді хочете відновити ці елементи до нової теки\n" "{path}?" msgstr[2] "" "Ви справді хочете відновити ці елементи до нової теки\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Ви справді хочете відновити цей елемент?" msgstr[1] "Ви справді хочете відновити ці елементи?" msgstr[2] "Ви справді хочете відновити ці елементи?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Бажаєте видалити всі новіші файли в {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "Бажаєте видалити всі новіші файли в початковому розташуванні?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "" "{BOLD}УВАГА{BOLDEND}: видалення файлів у root може зламати всю Вашу систему." #: qt/app.py:1750 msgid "Snapshot" msgstr "Копія" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Відновити {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Відновити {path} до …" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Налаштування мови набувають чинності лише після перезапуску Back In Time." #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Підтримку EncFS буде припинено найближчим часом. Не рекомендується надалі " "використовувати цей режим для профілю." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Рішення про заміну для подальшої підтримки зашифрованих резервних копій ще " "не прийнято і залежить від ресурсів проєкту та наявності розробників. Більше" " інформації можна знайти в цьому {whitepaper}." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "документі" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Підтримка зашифрованих профілів перебуває на стадії суттєвих змін, " "найближчим часом EncFS буде вилучено." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Профілі, які використовують шифрування EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "Рішення про заміну для подальшої підтримки зашифрованих резервних копій ще " "не прийнято і залежить від ресурсів проєкту та наявності розробників. " "Запрошуємо користувачів долучитися до обговорення. Оновлену інформацію про " "наступні кроки можна знайти в цьому {whitepaper}." #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" "Це повідомлення більше не виводитиметься. Це діалогове вікно доступне в " "будь-який час через меню «Допомога»." #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Ваша команда Back In Time" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Змінити мову" #: qt/languagedialog.py:92 msgid "System default" msgstr "Як у системі" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Використовувати мову операційної системи." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Перекладено: {percent}" #: qt/languagedialog.py:188 #, python-brace-format, ignore-placeholder-compare 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 українською мовою.\n" "Переклад встановленої версії Back In Time на українську завершено на {perc}. Незалежно від Вашого рівня технічних знань, Ви може зробити свій внесок у переклад і, таким чином, у розвиток Back In Time.\n" "Якщо бажаєте зробити внесок, відвідайте {translation_platform_url}. Додаткову допомогу та відповіді на запитання можна отримати на сайті проєкту {back_in_time_project_website}.\n" "Перепрошуємо за переривання, це повідомлення більше не виводитиметься. Це діалогове вікно доступне в будь-який час через меню «Допомога».\n" "Ваша команда Back In Time" #: qt/languagedialog.py:217 msgid "translation platform" msgstr "платформу для перекладу" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Ваш переклад" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Останній журнал" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Журнал копій" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Профіль:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "Резервні копії:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "Фільтр:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Усе" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Зміни" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Помилки" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "Інформація" msgstr[1] "Інформація" msgstr[2] "Інформація" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "Помилки переміщення rsync (експериментально)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Помилка, [I] Інформація, [C] Зміна" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "декодувати шляхи" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Питання" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Профіль: {profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Переглянути останній журнал" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Запуск {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Виконання…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Надіслано:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Швидкість:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "Залишилося:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Резервні копії" #: qt/qttools.py:427 msgid "Today" msgstr "Сьогодні" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Вчора" #: qt/qttools.py:443 msgid "This week" msgstr "Цього тижня" #: qt/qttools.py:450 msgid "Last week" msgstr "Попереднього тижня" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Це НЕ резервна копія, а Ваші файли на комп'ютері" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Остання перевірка {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Показати повний журнал" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "Проксі-сервер SSH" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Сервер:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Порт:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Користувач:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Керувати профілями" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Редагувати" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Додати" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Видалити" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Загальне" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Режим:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Де зберігати резервні копії" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Тека" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Налаштування SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Шлях:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Шифр:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Приватний ключ:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Оберіть файл приватного ключа (зазвичай має назву «id_rsa»)" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Створити новий ключ SSH без пароля (недоступно, якщо Ви вже обрали файл " "приватного ключа)." #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Пароль" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Зберегти пароль у зв'язці ключів" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "Кешувати пароль для Cron (Увага: root може читати пароль)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Додатково" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Повний шлях до копії:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Розклад" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Вимкнено" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "При кожному запуску/перезапуску" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Кожну {n} хвилину" msgstr[1] "Кожні {n} хвилини" msgstr[2] "Кожні {n} хвилин" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Кожну годину" msgstr[1] "Кожні {n} години" msgstr[2] "Кожні {n} годин" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Кожну {n} годину" msgstr[1] "Кожні {n} години" msgstr[2] "Кожні {n} годин" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Вказати години" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Щодня" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Повторно (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Коли диск приєднано (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Щотижня" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Щомісяця" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Щороку" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "День:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "День тижня:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "Година:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "Години:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Запускати Back In Time повторно. Корисно, якщо комп'ютер не працює постійно." #: qt/settingsdialog.py:528 msgid "Every:" msgstr "Кожні:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "годин" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "днів" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "тижнів" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "місяців" #: qt/settingsdialog.py:555 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 "" "Запускати Back In Time, коли Ви підключаєте диск (один раз на X днів).\n" "Вам потрібно буде ввести пароль sudo." #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "Увімкнути запис повідомлень про налагодження" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" "Записує повідомлення рівня налагодження до системного журналу за допомогою " "«--debug»." #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" "Обережно: використовуйте лише тимчасово для діагностики, оскільки " "генерується велика кількість вихідних даних." #: qt/settingsdialog.py:583 msgid "&Include" msgstr "В&ключити" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Включити файли і теки" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Додати файл" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Додати теку" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "Вик&лючити" #: qt/settingsdialog.py:625 #, 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/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "Виключити шаблони, файли або теки" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Додати за замовчуванням" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Виключити файли більші ніж:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Виключити файли більші ніж значення в {size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Якщо «Full rsync mode» вимкнено, це вплине лише на нові файли, оскільки для " "rsync це переміщення, а не виключення. Тому великі файли, зарезервовані " "раніше, залишаться в копіях, навіть якщо вони змінилися." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&Автоматичне видалення" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "Старіше ніж:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "років" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "Якщо вільного місця менше ніж:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "Якщо вільних айнодів менше ніж:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "Розумне видалення:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Виконувати у фоні на сервері." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "ЕКСПЕРИМЕНТАЛЬНО" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Зберігати всі копії за останні" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "днів." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Зберігати одну копію за день за останні" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Зберігати одну копію за тиждень за останні" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "тижнів." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Зберігати одну копію за місяць за останні" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "місяців." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Зберігати одну копію за рік за всі роки." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Не видаляти названі резервні копії." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "&Налаштування" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Увімкнути сповіщення" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Вимкнути резервування при роботі від батареї" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Статус живлення недоступний" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Створювати одну резервну копію за раз" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Інші копії буде заблоковано, доки триває поточне резервування. Це глобальне " "налаштування, отже вплине на всі профілі цього користувача. Але для інших " "користувачів його потрібно вмикати окремо." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Під час відновлення зберігати копії переміщених файлів" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "" "Перед відновленням новіші версії файлів буде перейменовано з додаванням " "{suffix}. Якщо вони Вам не потрібні, можна видалити їх, використавши {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Ігнорувати помилки (зберігати незавершені копії)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Виявляти зміни за контрольними сумами" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Резервувати незалежно від того, чи були зміни." #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "Рівень ведення журналу:" #: qt/settingsdialog.py:911 msgid "None" msgstr "Нічого" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Роз&ширені налаштування" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Обережно: змінюйте ці параметри, тільки якщо Ви дійсно знаєте, що робите." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Запустити «rsync» з «{cmd}»:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "як cron job" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "на віддаленому сервері" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "під час резервування вручну" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Встановіть «nocache», щоб увімкнути)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "на локальному комп'ютері" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Перенаправити stdout на /dev/null в cronjobs." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" "Якщо встановлено MTA, Cron автоматично надішле електронного листа з " "прикріпленим виводом cronjobs." #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Перенаправити stderr на /dev/null в cronjobs." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" "Якщо встановлено MTA, Cron автоматично надішле електронного листа з " "прикріпленими помилками cronjobs." #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "Обмежити пропускну здатність:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "Кб/с" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Зберігати ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Зберігати додаткові атрибути (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "Копіювати ненадійні посилання (працює лише з абсолютними посиланнями)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "Обмежити однією файловою системою" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Параметри необхідно брати в лапки, напр. {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Додаткові параметри для rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Додати префікс до команд SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "Префікс для виконання перед кожною командою на віддаленому сервері." #: qt/settingsdialog.py:1188 #, 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:1196 msgid "default" msgstr "за замовчуванням" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Перевіряти, чи сервер онлайн" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Увага: якщо вимкнено, а сервер недоступний, це може призвести до " "непередбачуваних помилок." #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "Перевіряти, чи підтримує сервер усі необхідні команди." #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "Відновити конфігурацію" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Редаг. user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "Підтримку EncFS буде припинено найближчим часом. Рішення про заміну для " "подальшої підтримки зашифрованих резервних копій ще не прийнято і залежить " "від ресурсів проєкту та наявності розробників. Більше інформації можна " "знайти в цьому {whitepaper}." #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Новий профіль" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Перейменувати профіль" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Ви впевнені, що хочете видалити профіль «{name}»?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" "{BOLD}Настійно рекомендується{ENDBOLD}: (Усі рекомендації вже включено.)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}Настійно рекомендується{ENDBOLD}: {files}" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "Ви не вибрали файл приватного ключа для SSH." #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "Бажаєте згенерувати нову пару публічний/приватний ключ без пароля?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Приватний ключ «{file}» не існує." #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Бажаєте скопіювати свій публічний ключ SSH на сервер, щоб увімкнути " "безпарольний доступ?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "Автентичність сервера {host} не вдалося встановити." #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "Відбиток ключа {keytype}:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Перевірте цей відбиток. Бажаєте додати його до свого файлу «known_hosts»?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Виключити за шаблоном" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Виключити файл" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Виключити теку" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Включити файл" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "«{path}» — символьне посилання. Об'єкт, на який воно вказує, не буде зарезервовано, якщо його не включити.\n" "Включити цей об'єкт у резервну копію?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Включити теку" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Ви впевнені, що хочете змінити теку для резервних копій?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Не вдалося створити новий ключ SSH у {path}." #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "Вимкнено, оскільки цей шаблон не працює у режимі «SSH зашифрований»." #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(за замовчуванням: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "вимкнено" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "увімкнено" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "Імпортувати конфігурацію" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Не знайдено конфігурації" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "Імпорт" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" "Виберіть теку для резервних копій, з якої слід імпортувати файл " "конфігурації. Шлях може мати такий вигляд: {samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" "Якщо це тека на зовнішньому або віддаленому диску, її потрібно попередньо " "змонтувати вручну." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Параметри порівняння резервних копій" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "Команда:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "Параметри:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Використовуйте %1 та %2 як параметри шляху" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "Задайте команду diff або натисніть «Скасувати»." #: qt/snapshotsdialog.py:90 #, 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:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" "Для команди diff не задано жодних параметрів. Використовується значення за " "замовчуванням «{params}»." #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Тільки копії, що відрізняються" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "Показати лише копії, однакові з:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Глибока перевірка (більш точна, але повільніша)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Видалити" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Вибрати все" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "Порівняти" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Перейти до" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Налаштування" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Не можна порівнювати резервну копію саму з собою." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Ви справді хочете видалити {file} з резервної копії {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Ви справді хочете видалити {file} з {count} резервних копій?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "УВАГА: це не можна буде скасувати." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Виключити {path} з майбутніх резервних копій?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Не вдається знайти crontab. Ви впевнені, що cron встановлено? Якщо ні, Вам " #~ "варто вимкнути все автоматичне резервування." #~ msgid "Full snapshot path" #~ msgstr "Повний шлях до копії" #~ msgid "Info" #~ msgstr "Інформація" #~ msgid "Mode" #~ msgstr "Режим" #~ msgid "Profile" #~ msgstr "Профіль" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Профіль «{profile}»: Введіть пароль для {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang у скрипті user-callback неможливо виконати." #~ msgid "WARNING" #~ msgstr "ПОПЕРЕДЖЕННЯ" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs версії 1.7.2 і раніших містить помилку в параметрі --reverse. Будь " #~ "ласка, оновіть encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "Скрипт user-callback не містить рядка shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} використовує для шифрування EncFS. Нещодавні перевірки безпеки виявили" #~ " його вразливість до деяких видів атак. Будь ласка, перегляньте «A NOTE ON " #~ "SECURITY» в «man backintime»." backintime-1.5.2/common/po/vi.po000066400000000000000000001570371465446530500165420ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the Back In Time package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: Back In Time 1.3.4-dev\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-05 20:13+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.6.2\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "Cảnh báo" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "Hồ sơ chính" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "Cục bộ (được mã hóa bằng EncFS)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH (được mã hóa EncFS)" #: common/config.py:309 msgid "Local" msgstr "Cục bộ" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "Khóa SSH riêng" #: common/config.py:314 msgid "Local encrypted" msgstr "Mã hóa cục bộ" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "Mã hóa" #: common/config.py:320 msgid "SSH encrypted" msgstr "Đã mã hóa SSH" #: common/config.py:327 msgid "Default" msgstr "Mặc định" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Hồ sơ: “{name}”" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "Thư mục snapshot không hợp lệ!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "Bạn phải chọn ít nhất một thư mục để sao lưu!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "Thư mục sao lưu không thể được thêm vào." #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "Thư mục con sao lưu không thể được thêm vào." #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Tùy chọn không hợp lệ. {path} không phải là một thư mục." #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID không được để trống." #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Không thể ghi vào “{path}”\n" "Có thể bạn không có đủ quyền hạn để ghi?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native 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/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. 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/config.py:496 qt/settingsdialog.py:1127 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/config.py:497 msgid "Expert Options" msgstr "Tùy chọn dành cho chuyên gia" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. 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/config.py:1658 msgid "Failed to write new crontab." msgstr "Ghi crontab mới thất bại." #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." 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/config.py:1746 #, 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/config.py:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Lịch trình theo udev không hoạt động trong chế độ {mode}" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Không tìm thấy UUID cho {path}" #: common/configfile.py:107 msgid "Failed to save config" msgstr "Lưu cấu hình thất bại" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Nạp cấu hình thất bại" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Hồ sơ \"{name}\" đã tồn tại." #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "Hồ sơ cuối cùng không thể bị xóa bỏ." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Không thể mount '{command}'" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "Không tìm thấy cấu hình cho thư mục đã mã hóa." #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "Tạo một thư mục được mã hóa mới?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Hủy" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "Vui lòng xác nhận mật khẩu." #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Mật khẩu không trùng khớp." #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "Chụp snapshot" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Không thể unmount {mountprocess} từ {mountpoint}." #: common/mount.py:696 #, 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:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "Vị trí mount {mntpoint} không trống." #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "Nhập mật khẩu cho hồ sơ {mode} \"{profile}\":" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "THẤT BẠI" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "Khôi phục quyền hạn" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "Xong" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "Trì hoãn sao lưu khi đang sử dụng pin" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Không thể tìm thấy thư mục snapshot.\n" "Nếu nó nằm trong ổ cứng có thể tháo rời thì vui lòng cắm vào máy." #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Đang chờ %s giây." #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Chụp snapshot {snapshot_id} thất bại." #: common/snapshots.py:937 msgid "Finalizing" msgstr "Đang hoàn thiện" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "Không tạo được thư mục" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "Đang lưu tập tin cấu hình…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "Đang lưu quyền hạn…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Tìm thấy {snapshot_id} đang dang dở và có thể tiếp tục." #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Đang xóa thư mục {snapshot_id} dang dở từ lần chạy trước" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "Không thể xóa thư mục" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "Đang chụp snapshot" #: common/snapshots.py:1417 msgid "Success" msgstr "Thành công" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "Truyền một phần do lỗi" #: common/snapshots.py:1421 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:1425 #, 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:1438 msgid "See 'man rsync' for more details" msgstr "Xem 'man rsync' để biết nhiều chi tiết hơn" #: common/snapshots.py:1445 #, 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:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "Không có gì thay đổi, không cần snapshot mới" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Không thể đổi tên {new_path} thành {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 #, fuzzy msgid "Smart removal" msgstr "Xóa thông minh" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "Đang xóa các snapshot cũ" #: common/snapshots.py:1890 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:1929 #, 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:2989 qt/app.py:1743 msgid "Now" msgstr "Vừa mới đây" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Không thể mount {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Không tìm thấy ssh-agent. Đảm bảo rằng nó đã được cài." #: common/sshtools.py:444 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:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Thuật toán mã hóa {cipher} thất bại cho {host}." #: common/sshtools.py:682 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:687 msgid "Remote path is not writable." msgstr "Đường dẫn từ xa không thể được viết vào." #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "Đường dẫn từ xa không thể được thực thi." #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "Không tạo được đường dẫn từ xa." #: common/sshtools.py:981 #, 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:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "Xem 'man backintime' cho các chỉ dẫn sâu hơn" #: common/sshtools.py:989 #, python-brace-format msgid "Check 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:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Máy ở xa {host} không hỗ trợ harklink" #: common/sshtools.py:1164 #, 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:1166 #, fuzzy, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "Vui lòng nhập mật khẩu cho “{user}”" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "Giới thiệu" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "Tác giả" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "Phiên dịch" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "Giấy phép" #: qt/app.py:169 msgid "Shortcuts" msgstr "Lối tắt" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Thư mục này không tồn tại\n" "trong bản snapshot đang được chọn." #: qt/app.py:256 msgid "Add to Include" msgstr "Thêm vào Bao gồm" #: qt/app.py:258 msgid "Add to Exclude" msgstr "Thêm vào Loại trừ" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as 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:348 msgid "" "Import an existing configuration (from a backup target folder 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:375 #, fuzzy msgid "Can't find snapshots folder." msgstr "Không tạo được thư mục" #: qt/app.py:376 #, fuzzy msgid "If it is on a removable drive please plug it in and then press OK." 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." #: qt/app.py:481 msgid "Take a snapshot" msgstr "Chụp snapshot" #: qt/app.py:483 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:486 msgid "Take a snapshot (checksum mode)" msgstr "Chụp snapshot với checksum" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "Dùng checksum để phát hiện sự thay đổi của file." #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "Tạm dừng quá trình chụp snapshot" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "Tiếp tục quá trình chụp snapshot" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "Dừng hẳn quá trình chụp snapshot" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "Tải lại danh sách snapshot" #: qt/app.py:508 msgid "Name snapshot" msgstr "Đặt tên snapshot" #: qt/app.py:512 msgid "Remove snapshot" msgstr "Xóa snapshot" #: qt/app.py:516 msgid "View snapshot log" msgstr "Xem nhật ký Snapshot" #: qt/app.py:520 msgid "View last log" msgstr "Xem nhật ký cuối cùng" #: qt/app.py:524 msgid "Manage profiles…" msgstr "Quản lý hồ sơ…" #: qt/app.py:528 msgid "Shutdown" msgstr "Tắt máy" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "Tắt máy sau khi chụp snapshot hoàn tất." #: qt/app.py:532 msgid "Setup language…" msgstr "Cài đặt ngôn ngữ…" #: qt/app.py:536 msgid "Exit" msgstr "Thoát" #: qt/app.py:540 msgid "Help" msgstr "Trợ giúp" #: qt/app.py:544 msgid "Profiles config file" msgstr "Tập tin cấu hình hồ sơ" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "Trang web" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "Nhật ký thay đổi" #: qt/app.py:553 msgid "FAQ" msgstr "Câu hỏi thường gặp" #: qt/app.py:556 msgid "Ask a question" msgstr "Đặt câu hỏi" #: qt/app.py:559 msgid "Report a bug" msgstr "Báo lỗi" #: qt/app.py:562 msgid "Translation" msgstr "Phiên dịch" #: qt/app.py:564 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:568 msgid "Encryption Transition (EncFS)" msgstr "Chuyển đổi mã hóa (EncFS)" #: qt/app.py:570 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:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "Khôi phục" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." 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:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "Khôi phục đến …" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." 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:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." 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:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." 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:595 msgid "Up" msgstr "Lên" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "Hiện tập tin ẩn" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "Chụp snapshot…" #: qt/app.py:660 msgid "Back In &Time" msgstr "" #: qt/app.py:665 msgid "&Backup" msgstr "&Sao lưu" #: qt/app.py:676 msgid "&Restore" msgstr "&Khôi phục" #: qt/app.py:682 msgid "&Help" msgstr "T&rợ giúp" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot 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:821 msgid "Do you really want to close it?" msgstr "Bạn có thật sự muốn đóng nó ?" #: qt/app.py:987 msgid "Working:" msgstr "Đang chạy:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "Xong, không cần sao lưu" #: qt/app.py:1044 msgid "Working" msgstr "Đang chạy" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "Lỗi" #: qt/app.py:1076 msgid "Sent" msgstr "Đã gửi" #: qt/app.py:1077 msgid "Speed" msgstr "Tốc độ" #: qt/app.py:1078 msgid "ETA" msgstr "Còn lại" #: qt/app.py:1140 msgid "Global" msgstr "Toàn cục" #: qt/app.py:1141 msgid "Root" msgstr "Thư mục Root" #: qt/app.py:1142 msgid "Home" msgstr "Thư mục Home" #: qt/app.py:1170 msgid "Backup folders" msgstr "Các thư mục sao lưu" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "Đổi tên snapshot" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Bạn có chắc chắn muốn xóa snapshot không?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "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/app.py:1416 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them 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/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" 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/app.py:1467 msgid "Remove newer elements in original folder." msgstr "Xóa các tập tin có phiên bản mới hơn trong thư mục gốc." #: qt/app.py:1470 #, fuzzy msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." 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/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" 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/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to 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/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "Bạn có thật sự muốn xóa hết các tập tin có phiên bản mới hơn trong {path}?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Bạn có chắc chắn muốn xóa hết các tập tin có phiên bản mới hơn trong thư mục" " gốc?" #: qt/app.py:1514 #, fuzzy, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your 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:1750 msgid "Snapshot" msgstr "Snapshot" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "Khôi phục {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "Khôi phục {path} đến …" #: qt/app.py:1948 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/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "" "Hỗ trợ cho EncFS sẽ ngừng trong tương lai gần. Không khuyến khích sử dụng " "chế độ đó cho hồ sơ nữa." #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "" "Quyết định về việc thay thế để tiếp tục hỗ trợ sao lưu mã hóa vẫn đang chờ " "xử lý, phụ thuộc vào tài nguyên dự án và sự sẵn có của người đóng góp. Thêm " "thông tin chi tiết có sẵn ở {whitepaper} ." #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "sách trắng" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "" "Hỗ trợ cho các hồ sơ snapshot được mã hóa đang trải qua các thay đổi đáng " "kể, và EncFS sẽ được gỡ bỏ trong tương lai gần." #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "Các hồ sơ cá nhân sau đây sử dụng mã hóa với EncFS:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "Thiết đặt ngôn ngữ" #: qt/languagedialog.py:92 msgid "System default" msgstr "Mặc định hệ thống" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "Dùng ngôn ngữ của hệ điều hành." #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "Đã dịch: {percent}" #: qt/languagedialog.py:188 #, 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/languagedialog.py:217 msgid "translation platform" msgstr "nền tảng phiên dịch" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "Bản dịch của bạn" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "Xem nhật ký được lưu lần cuối cùng" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "Xem nhật ký snapshot" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "Hồ sơ:" #: qt/logviewdialog.py:84 #, fuzzy msgid "Snapshots:" msgstr "Snapshot" #: qt/logviewdialog.py:99 #, fuzzy msgid "Filter:" msgstr "Bộ lọc" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "Tất cả" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "Có thay đổi" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "Có lỗi" #: qt/logviewdialog.py:115 qt/messagebox.py:71 #, fuzzy msgid "Information" msgid_plural "Information" msgstr[0] "Thông tin" #: qt/logviewdialog.py:117 #, fuzzy msgid "rsync transfer failures (experimental)" msgstr "Truyền rsync thất bại (thử nghiệm)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Lỗi, [I] Thông tin, [C] Thay đổi" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "giải mã đường dẫn" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "Thắc mắc" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "Hồ sơ: “{profile_name}”" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "Xem nhật ký cuối cùng được lưu" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "Bắt đầu {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "Đang chạy…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "Đã gửi:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "Tốc độ:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "Snapshot" #: qt/qttools.py:427 msgid "Today" msgstr "Hôm nay" #: qt/qttools.py:434 msgid "Yesterday" msgstr "Hôm qua" #: qt/qttools.py:443 msgid "This week" msgstr "Tuần này" #: qt/qttools.py:450 msgid "Last week" msgstr "Tuần trước" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" "Đây không phải là một snapshot mà là hình ảnh trực tiếp của các tập tin cục " "bộ của bạn" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "Kiểm tra lần cuối: {time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "Hiện toàn bộ Nhật ký" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "Máy chủ:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "Cổng:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "Người dùng:" #: qt/settingsdialog.py:120 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:170 msgid "Manage profiles" msgstr "Quản lý hồ sơ" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "Sửa" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "Thêm" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "Gỡ bỏ" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&Tổng quan" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "Chế độ:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "Nơi lưu các snapshot" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "Thư mục" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "Cài đặt SSH" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "Đường dẫn:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "Thuật toán mã hóa:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "Khóa riêng:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Chọn một tập tin khóa riêng đã tồn tại (thường được đặt tên \"id_rsa\")" #: qt/settingsdialog.py:323 #, fuzzy msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "" "Tạo một khóa SSH mới mà không cần mật khẩu (không được phép nếu một tập tin " "khóa riêng đã được chọn)" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "Mật khẩu" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "Lưu mật khẩu vào Keyring" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Lưu mật khẩu cho Cron vào cache (Vấn đề bảo mật: tài khoản root có thể đọc " "mật khẩu)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "Nâng cao" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "Đường dẫn đầy đủ của snapshot:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "Lịch trình" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "Đang tắt" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "Mỗi lần khởi động máy" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Mỗi {n} phút" #: qt/settingsdialog.py:448 #, fuzzy, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "Mỗi giờ" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Mỗi {n} giờ" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "Theo giờ tùy chỉnh" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "Hàng ngày" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "Theo định kỳ (anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "Khi ổ cứng kết nối thành công (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "Hàng tuần" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "Hàng tháng" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "Hàng năm" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "ngày thường:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "giờ:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "giờ:" #: qt/settingsdialog.py:521 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/settingsdialog.py:528 msgid "Every:" msgstr "Mỗi:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "Giờ" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "Ngày" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "Tuần" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "Tháng" #: qt/settingsdialog.py:555 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 "" "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/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&Bao gồm" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "Bao gồm các tập tin và thư mục" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "Thêm tập tin" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "Thêm thư mục" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&Ngoại trừ" #: qt/settingsdialog.py:625 #, 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:648 msgid "Exclude patterns, files or folders" msgstr "Loại trừ các pattern, tập tin hoặc thư mục" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "Thêm mặc định" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "Loại trừ các tập tin có kích thước lớn hơn :" #: qt/settingsdialog.py:698 #, fuzzy, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "Loại trừ các tập tin có kích thước lớn hơn : " #: qt/settingsdialog.py:700 #, fuzzy msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "Loại trừ các tập tin có kích thước lớn hơn giá trị trong %(prefix)s.\n" "Khi 'chế độ rsync đầy đủ' đã tắt, cài đặt này chỉ có hiệu lực với các tập tin mới\n" "bởi vì đối với rsync đây là cài đặt chuyển đổi, không phải cài đặt loại trừ.\n" "Nên những tập tin lớn đã được sao lưu trước đó sẽ được giữ nguyên trong snapshot\n" "ngay cả khi chúng có thay đổi." #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "Tự động &xóa" #: qt/settingsdialog.py:726 #, fuzzy msgid "Older than:" msgstr "Cũ hơn" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "Năm" #: qt/settingsdialog.py:748 #, fuzzy msgid "If free space is less than:" msgstr "Nếu dung lượng trống còn ít hơn" #: qt/settingsdialog.py:768 #, fuzzy msgid "If free inodes is less than:" msgstr "Nếu số inode trống còn ít hơn" #: qt/settingsdialog.py:782 #, fuzzy msgid "Smart removal:" msgstr "Xóa thông minh:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "Chạy ngầm trên máy từ xa." #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "THỬ NGHIỆM" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "Giữ tất cả snapshot trong" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "ngày vừa qua." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "Giữ một bản snapshot từ mỗi ngày trong" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "Giữ một bản snapshot từ mỗi tuần trong" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "tuần vừa qua." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "Giữ một bản snapshot từ mỗi tháng trong" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "tháng vừa qua." #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "Giữ một bản snapshot từ mỗi năm trong tất cả các năm." #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "Đừng xóa các bản snapshot đã được đổi tên." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "Tùy &chọn" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "Bật thông báo" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "Tắt chụp snapshot khi đang dùng pin" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "Trạng thái nguồn điện không khả dụng từ hệ thống" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "Chỉ chạy từng snapshot một" #: qt/settingsdialog.py:868 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "" "Các bản snapshot khác sẽ bị chặn cho đến khi bản snapshot hiện tại chạy xong.\n" "Đây là cài đặt toàn cục. Nên nó sẽ có hiệu lực với tất cả hồ sơ của người dùng này.\n" "Nhưng bạn sẽ cần kích hoạt tùy chọn này cho tất cả người dùng khác nữa." #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "Sao lưu những tập tin bị thay thế trong quá trình khôi phục" #: qt/settingsdialog.py:879 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" 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" "Nếu bạn không cần chúng nữa, bạn có thể xóa chúng với {cmd}" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Tiếp tục khi gặp lỗi (giữ các bản snapshot không hoàn thiện)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "Dùng checksum để phát hiện có thay đổi" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "Chụp snapshot mới dù có thay đổi hay không." #: qt/settingsdialog.py:906 #, fuzzy msgid "Log Level:" msgstr "Ghi lại mức độ" #: qt/settingsdialog.py:911 msgid "None" msgstr "Không có gì" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "Tùy chọn chuyên &gia" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Cảnh báo: Chỉ thay đổi những tùy chọn này nếu bạn thật sự biết mình đang làm" " gì." #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Chạy 'rsync' với '{cmd}':" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "bằng cron job" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "trên máy chủ từ xa" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "khi đang chụp snapshot thủ công" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(Vui lòng cài 'nocache' để bật tùy chọn này)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "trên máy cục bộ" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Chuyển hướng stdout vào /dev/null trong cronjob." #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Chuyển hướng stderr vào /dev/null trong cronjob." #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "" #: qt/settingsdialog.py:1024 #, fuzzy msgid "Limit rsync bandwidth usage:" msgstr "Giới hạn sử dụng băng thông rsync" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/giây" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "Giữ nguyên ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "Giữ nguyên thuộc tính mở rộng (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Sao chép liên kết không an toàn (chỉ có tác dụng với liên kết tuyệt đối)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Tùy chọn phải nằm trong ngoặc kép. Ví dụ: {example}." #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "Dán các tùy chọn phụ vào rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "Thêm tiền tố vào các lệnh SSH" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "" #: qt/settingsdialog.py:1188 #, fuzzy, 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 "" "Tiền tố để chạy trước mỗi lệnh trên máy chủ từ xa.\n" "Các biến cần phải được kết thúc bằng \\$FOO.\n" "Tùy chọn này không đụng đến rsync. Nên để thêm một tiền tố\n" "cho rsync hãy sử dụng \"%(cbRsyncOptions)s\" với\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1196 msgid "default" msgstr "mặc định" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "Kiểm tra liệu máy chủ từ xa có online" #: qt/settingsdialog.py:1214 #, fuzzy msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "" "Cảnh báo: Nếu tắt tùy chọn này và máy chủ từ xa\n" "hiện không khả dụng, tình trạng này có thể\n" "dẫn đến một vài lỗi kỳ lạ." #: qt/settingsdialog.py:1218 #, fuzzy msgid "Check if remote host supports all necessary commands." msgstr "Kiểm tra liệu máy chủ từ xa có hỗ trợ tất cả các lệnh cần thiết" #: qt/settingsdialog.py:1221 #, fuzzy msgid "" "Warning: If disabled and the remote host does not support all necessary " "commands, this could lead to some weird errors." msgstr "" "Cảnh báo: nếu tắt tùy chọn này và máy chủ từ xa\n" "không hỗ trợ tất cả các lệnh cần thiết,\n" "tình trạng này có thể dẫn đến một vài lỗi kỳ lạ." #: qt/settingsdialog.py:1237 msgid "Restore Config" msgstr "Khôi phục cấu hình" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "Sửa user-callback" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "Hồ sơ mới" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "Đổi tên hồ sơ" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Bạn có chắc chắn muốn xóa hồ sơ \"{name}\" ?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "" #: qt/settingsdialog.py:1427 #, fuzzy, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "Rất khuyến khích" #: qt/settingsdialog.py:1634 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/settingsdialog.py:1681 msgid "You did not choose a private key file for SSH." msgstr "" #: qt/settingsdialog.py:1682 #, fuzzy msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Bạn chưa chọn một tập tin khóa riêng cho SSH.\n" "Bạn có muốn tạo một cặp khóa riêng và khóa công khai không có mật khẩu?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Tập tin khóa riêng \"{file}\" không tồn tại." #: qt/settingsdialog.py:1847 #, fuzzy msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "" "Bạn có muốn sao chép khóa SSH công khai của bạn đến\n" "máy chủ từ xa để bật đăng nhập không cần mật khẩu?" #: qt/settingsdialog.py:1878 #, fuzzy, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "" "Không thể xác thực máy chủ {host}.\n" "\n" "Vân tay khóa {keytype} là:" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1889 #, fuzzy msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "" "Vui lòng xác nhận vân tay này! Bạn có muốn thêm nó vào tập tin 'known_hosts'" " của bạn?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "Loại trừ pattern" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "Loại trừ tập tin" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "Loại trừ thư mục" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "Bao gồm tập tin" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "\"{path}\" là một liên kết tượng trưng. Tập tin gốc sẽ không được sao lưu cho đến khi bạn bao gồm nó vào nữa.\n" "Bạn có muốn bao gồm tập tin gốc thay vào đó?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "Bao gồm thư mục" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "Bạn có chắc chắn muốn thay đổi thư mục snapshot không?" #: qt/settingsdialog.py:2194 #, fuzzy, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "Tạo khoá SSH trong đường dẫn {path} thất bại" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "" #: qt/settingsdialog.py:2369 #, fuzzy msgid "(default: {})" msgstr "mặc định" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "đang tắt" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "đang bật" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "Không tìm thấy cấu hình" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Thiết đặt so sánh các snapshot" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command:" msgstr "Lệnh" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters:" msgstr "Tham số" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "Hãy dùng “%1” và “%2” cho tham số đường dẫn" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "" #: qt/snapshotsdialog.py:90 #, python-brace-format msgid "" "The command \"{cmd}\" cannot be found on this system. Please try something " "else or press Cancel." msgstr "" #: qt/snapshotsdialog.py:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "Chỉ những snapshot khác nhau" #: qt/snapshotsdialog.py:142 #, fuzzy msgid "List only snapshots that are equal to:" msgstr "Chỉ những snapshot giống với: " #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "Kiểm tra toàn diện (chính xác hơn nhưng chậm hơn)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "Xóa" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "Chọn tất cả" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "So sánh" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "Đi tới" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "Tùy chọn" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "Không thể so sánh một snapshot với chính nó." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Bạn có thật sự muốn xóa {file} trong snapshot {snapshot_id}?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Bạn có thật sự muốn xóa {file} trong snapshot {count}?" #: qt/snapshotsdialog.py:408 #, fuzzy msgid "WARNING: This cannot be revoked." msgstr "Lệnh này không thể thu hồi!" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Loại trừ {path} ra khỏi các snapshot trong tương lai?" #, fuzzy #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "" #~ "Không thể tìm thấy crontab.\n" #~ "Bạn có chắc là cron đã được cài đặt chưa?\n" #~ "Nếu chưa cài đặt bạn nên tắt tất cả sao lưu tự động." #~ msgid "Full snapshot path" #~ msgstr "Đường dẫn snapshot đầy đủ" #~ msgid "Mode" #~ msgstr "Chế độ" #~ msgid "Profile" #~ msgstr "Hồ sơ" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "Hồ sơ '{profile}': Nhập mật khẩu cho {mode}: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "Shebang trong đoạn script user-callback không được thực thi." #~ msgid "WARNING" #~ msgstr "CẢNH BÁO" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "" #~ "encfs phiên bản 1.7.2 và trước đó có lỗi với tùy chọn --reverse. Xin hãy cập" #~ " nhật encfs." #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "Đoạn script user-callback không có dòng shebang (#!/bin/sh)." #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} sử dụng EncFS để mã hóa. Một cuộc kiểm toán bảo mật gần đây đã tiết lộ" #~ " một số vectơ tấn công có thể xảy ra đối với hệ thống tập tin này. Vui lòng " #~ "xem qua \"GHI CHÚ VỀ BẢO MẬT\" trong \"man backintime\"." backintime-1.5.2/common/po/zh_CN.po000066400000000000000000001455331465446530500171230ustar00rootroot00000000000000# Simplified Chinese 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-07-11 12:12+0000\n" "Last-Translator: ktzhao \n" "Language-Team: Chinese (Simplified) \n" "Language: zh_CN\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.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "警告" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "主要配置" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "本地(EncFS加密)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH(EncFS加密)" #: common/config.py:309 msgid "Local" msgstr "本地" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH 私钥" #: common/config.py:314 msgid "Local encrypted" msgstr "本地加密的" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "加密" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH 加密" #: common/config.py:327 msgid "Default" msgstr "默认" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "配置:\"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "快照文件夹无效!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "您必须选择至少一个文件夹进行备份!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "不能包含备份文件夹。" #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "不能包含备份子文件夹。" #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "无效的选项。{path} 不是一个文件夹。" #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID 不能为空。" #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "无法写入:{path}\n" "您确定有写入权限吗?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "{path} 的目标文件系统格式为 FAT,不支持硬连接。请使用原生 Linux 文件系统。" #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "{path} 的目标文件系统是 SMB 格式挂载的共享。请确保远程 SMB 服务器支持符号链接或激活 {expertOptions} 中的 " "{copyLinks}。" #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "复制链接(取消引用符号链接)" #: common/config.py:497 msgid "Expert Options" msgstr "专家选项" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "{path} 的目标文件系统是 sshfs 格式挂载的共享。 sshfs 不支持硬链接。 请改用“SSH”模式。" #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "新增定时任务失败。" #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "crontab 指令可用但 cron 未在运行,因此无法执行定时备份。cron 可能已安装但未启用,请尝试指令\"systemctl enable " "cron\"或向您 GNU Linux 发行版的支持频道求助。" #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "计划 的udev 不适用于 {mode}模式" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "无法找到 \"{path}\" 的 UUID" #: common/configfile.py:107 msgid "Failed to save config" msgstr "保存配置失败" #: common/configfile.py:143 msgid "Failed to load config" msgstr "加载配置失败" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\"配置已存在。" #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "最后一个配置无法被移除。" #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "无法加载 {command}" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "未找到加密文件夹的配置。" #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "是否创建一个新的加密文件夹?" #: common/encfstools.py:151 msgid "Cancel" msgstr "取消" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "请确认密码。" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "密码不一致。" #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "创建快照" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "无法从 {mountpoint} 卸载 {mountprocess}。" #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "请安装未找到的 {command} ,安装指令如 {installcommand}。" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "挂载点{mntpoint} 不为空。" #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "为{mode}配置{profile}输入密码:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "失败" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "恢复权限" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "已完成" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "依靠电池供电时,延后备份" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "未能找到快照文件夹。\n" "如果快照位于外置存储器,请先将存储器接入。" #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "等待 %s 秒。" #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "创建快照 {snapshot_id} 失败。" #: common/snapshots.py:937 msgid "Finalizing" msgstr "正在完成" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "无法创建文件夹" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "保存配置文件 …" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "保存权限 …" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "找到剩余的{snapshot_id},可以继续。" #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "正在删除上次运行中剩余的 {snapshot_id} 文件夹" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "无法删除文件夹" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "创建快照" #: common/snapshots.py:1417 msgid "Success" msgstr "成功" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "由于错误仅部分转移" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "由于源文件消失导致仅部分传输(请参阅“man rsync”)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "“rsync”以退出代码 {exit_code} 结束" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "请查看“man rsync”以了解更多细节" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "负的 rsync 退出代码是信号编号,请参阅“kill -l”和“man Kill”" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "没有改动内容,无需创建新快照" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "无法将{new_path}重命名为{path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "智能移除" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "移除旧的快照" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "尝试保留最小可用空间" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "尝试保留至少 {perc} 个可用的 inodes" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "现在" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "无法挂载 {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent 未找到。请确保已安装该工具。" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "无法解锁 SSH 私钥,可能密码不正确或该密码不适用于 cron。" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "{host} 的密码 {cipher} 失败。" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "远程路径存在但不是一个目录。" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "远程路径不可写。" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "远程路径不可被执行。" #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "无法创建远程路径。" #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "远程主机 {host} 不支持 {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "请参阅“man backintime”以获取更多说明" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "检查主机 {host} 上返回未知错误的命令" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "远程主机 {host} 不支持硬链接" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "复制 ssh 公钥\"{pubkey}\"到远程主机 \"{host}\"。" #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "请输入“{user}”的密码。" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "关于" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "作者" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "翻译" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "许可" #: qt/app.py:169 msgid "Shortcuts" msgstr "快捷方式" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "当前选定的快照中\n" "不存在此文件夹。" #: qt/app.py:256 msgid "Add to Include" msgstr "添加到包含" #: qt/app.py:258 msgid "Add to Exclude" msgstr "添加到排除" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "{app_name}首次运行,没有找到配置文件。" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "(从备份目标文件夹或另一台计算机)导入已有配置?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "无法创建文件夹。" #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "找不到快照文件夹。如果使用的是可移动磁盘,请连接到电脑,然后按 “确定”。" #: qt/app.py:481 msgid "Take a snapshot" msgstr "创建快照" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "使用修改时间和大小进行文件更改检测。" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "生成快照(校验和模式)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "使用校验码探测文件变更。" #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "暂停快照进程" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "恢复快照过程" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "停止快照进程" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "刷新快照列表" #: qt/app.py:508 msgid "Name snapshot" msgstr "命名快照" #: qt/app.py:512 msgid "Remove snapshot" msgstr "移除快照" #: qt/app.py:516 msgid "View snapshot log" msgstr "查看快照日志" #: qt/app.py:520 msgid "View last log" msgstr "查看最近的日志" #: qt/app.py:524 msgid "Manage profiles…" msgstr "管理配置…" #: qt/app.py:528 msgid "Shutdown" msgstr "关机" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "快照完成后关闭系统。" #: qt/app.py:532 msgid "Setup language…" msgstr "设置语言…" #: qt/app.py:536 msgid "Exit" msgstr "退出" #: qt/app.py:540 msgid "Help" msgstr "帮助" #: qt/app.py:544 msgid "Profiles config file" msgstr "配置文件" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "网站" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "变更日志" #: qt/app.py:553 msgid "FAQ" msgstr "常见问题" #: qt/app.py:556 msgid "Ask a question" msgstr "提出问题" #: qt/app.py:559 msgid "Report a bug" msgstr "报告 BUG" #: qt/app.py:562 msgid "Translation" msgstr "翻译" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "再次显示翻译参与者的相关信息。" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "加密改动(EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "再次显示关于移除EncFS加密的信息。" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "还原" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "将选定的文件或文件夹恢复到原始目标。" #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "还原至…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "将选定的文件或文件夹恢复到新的目标。" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "将当前显示的文件夹及其所有内容恢复到原始目标。" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "将当前显示的文件夹及其所有内容恢复到新目标。" #: qt/app.py:595 msgid "Up" msgstr "上移" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "显示隐藏文件" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "比较快照…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&备份" #: qt/app.py:676 msgid "&Restore" msgstr "&还原" #: qt/app.py:682 msgid "&Help" msgstr "&帮助" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "如果关闭此窗口,则快照完成后 Back In Time 无法关闭你的系统。" #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "确实要关闭它吗?" #: qt/app.py:987 msgid "Working:" msgstr "运行中:" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "已完成,无需备份" #: qt/app.py:1044 msgid "Working" msgstr "运行中" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "错误" #: qt/app.py:1076 msgid "Sent" msgstr "已发送" #: qt/app.py:1077 msgid "Speed" msgstr "速度" #: qt/app.py:1078 msgid "ETA" msgstr "预计完成时间" #: qt/app.py:1140 msgid "Global" msgstr "全局" #: qt/app.py:1141 msgid "Root" msgstr "根目录" #: qt/app.py:1142 msgid "Home" msgstr "主目录" #: qt/app.py:1170 msgid "Backup folders" msgstr "备份文件夹" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "快照名" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "您确定要移除这些快照?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "在覆盖或移除本地文件前创建带有后缀 {suffix}\n" "的备份副本。" #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "在恢复之前,较新版本的文件将使用后缀 {suffix} 进行重命名。如果您不再需要它们,以下的指令删除他们:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "只恢复不存在或\n" "比目标更新的元素。\n" "使用“rsync --update”选项。" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "删除原始文件夹中较新的元素。" #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "将选定的文件或文件夹恢复到原始目标,并删除不在快照中的文件或文件夹,要非常小心,因为这会删除创建快照时被排除的文件和文件夹。" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "是否确定将这些文件还原至新文件夹\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "是否确定还原这些文件?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "您确定要移除 {path} 的所有新文件吗?" #: qt/app.py:1508 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "您确定要删除原始文件夹中的所有较新文件吗?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "{BOLD}警告{BOLDEND}:删除文件系统根目录中的文件可能会破坏整个系统。" #: qt/app.py:1750 msgid "Snapshot" msgstr "快照" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "还原 {path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "还原 {path} 至…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "语言设置仅在重新启动 Back In Time 后生效。" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "我们将在不久的将来停止支持 EncFS,因此不建议您继续使用该模式。" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "目前仍未决定是否继续支持加密备份,这取决于项目资源和贡献者的可用性。更多详细信息请参阅本{whitepaper}。" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "白皮书" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "我们正在重构加密快照。EncFS将在未来被移除。" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "以下配置将使用 EncFS 进行加密:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "我们还在商讨如何取代当前的加密备份方案,具体实现取决于项目资源和贡献者数量,欢迎广大用户踊跃参与讨论。更新详情详见{whitepaper}。" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "该消息仅会显示这一次。您可以在帮助菜单随时回顾这个对话框。" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "Back In Time团队" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "设置语言" #: qt/languagedialog.py:92 msgid "System default" msgstr "系统默认" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "使用操作系统语言。" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "翻译:{percent}" #: qt/languagedialog.py:188 #, 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/languagedialog.py:217 msgid "translation platform" msgstr "翻译平台" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "你的翻译" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "最近日志查看" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "快照日志查看" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "配置:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "快照:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "过滤器:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "所有的" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "变更" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "错误" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "提示信息" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync 传输失败(实验性" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] 错误,[I] 通知,[C] 变更" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "解码路径" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "问题" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "配置:\"{profile_name}\"" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "查看最近的日志" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "启动 {appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "运行中…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "发送:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "速度:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "快照" #: qt/qttools.py:427 msgid "Today" msgstr "今天" #: qt/qttools.py:434 msgid "Yesterday" msgstr "昨天" #: qt/qttools.py:443 msgid "This week" msgstr "本周" #: qt/qttools.py:450 msgid "Last week" msgstr "上周" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "这不是快照,而是本地文件的实时视图" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "最近检查{time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "显示完整日志" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH 代理" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "主机:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "端口:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "用户:" #: qt/settingsdialog.py:120 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 "" "通过此代理(又称跳转主机)连接到目标主机。详细信息请参阅“ssh”命令文档中的“-J”或“ssh_config”手册页中的“ProxyJump”。" #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "管理配置" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "编辑" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "新增" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "移除" #: qt/settingsdialog.py:210 msgid "&General" msgstr "&常规" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "模式:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "何处保存快照" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "文件夹" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH 设置" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "路径:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "密码:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "私钥:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "选择一个已存在的私钥文件(通常名为 \"id_rsa\")" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "创建一个新的没有密码的 SSH 密钥(如果已经选择了私钥文件则不允许)。" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "密码" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "保存密码至密钥环" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "Cron 的缓存密码(安全问题:root 可以读取密码)" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "高级" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "快照完整路径:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "计划任务" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "已禁用" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "每次启动/重启时" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "每{n}分钟" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "每{n}小时" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "每{n}小时" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "自定义小时" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "每天" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "重复(anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "当设备连接时(udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "每周" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "每月" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "每年" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "天:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "工作日:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "小时:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "小时:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "重复运行Back In Time。 如果计算机没有正常运行,这非常有用。" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "每:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "小时" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "天" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "周" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "月" #: qt/settingsdialog.py:555 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 "" "连接驱动器后立即运行Back In Time(每 X 天一次)。\n" "系统将提示您输入 sudo 密码。" #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "启用调试消息记录" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "通过“--debug”将调试级别的消息写入系统日志。" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "警告:仅暂时将其用于诊断,因为它会生成大量输出。" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "&包含" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "包含文件和文件夹" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "添加文件" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "添加文件夹" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "&排除" #: qt/settingsdialog.py:625 #, 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/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "不包含模式,文件或文件夹" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "添加默认值" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "排除大于以下的文件:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "排除大于以下的文件{size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "排除大于 %(prefix)s 的文件。禁用“完全 rsync 模式”后,这只会影响新文件,因为对于 rsync " "这是一个传输选项,而不是排除选项。因此之前备份过的大文件将保留在快照中,即使他们已经改变。" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "&自动移除" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "早于:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "年" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "如果磁盘可用空间少于:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "如果磁盘可用节点 inodes 少于:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "智能移除:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "在远程主机后台运行。" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "实验功能" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "保留最新的所有快照" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "天。" #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "每天保留一个最新的快照" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "每周保留一个最新的快照" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "周。" #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "每月保留一个最新的快照" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "月。" #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "每年保留一个快照。" #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "不要移除已命名的快照。" #: qt/settingsdialog.py:849 msgid "&Options" msgstr "选项(_O)" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "启用通知" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "使用电池时禁用快照" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "无法从系统中获取电源状态" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "一次仅运行一个快照" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "在当前快照完成前会阻塞其他快照。这是一个全局选项,因此会影响当前用户的所有配置。而你需要为其他所有用户也开启此选项的。" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "恢复时备份替换的文件" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "在恢复之前,较新版本的文件将使用后缀 {suffix} 进行重命名。如果您不再需要它们,可以使用 {cmd} 删除它们" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "忽略错误并继续(保留不完整的快照)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "使用校验检测变更" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "无论是否有改变,都创建新快照。" #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "日志级别:" #: qt/settingsdialog.py:911 msgid "None" msgstr "空" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "专家选项" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "警告:除非您知道自己在干什么,否则不要变更这些选项。" #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "使用“{cmd}”运行“rsync”:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "作为 cron 作业" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "在远程主机上" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "当手动创建快照时" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(请安装“nocache”以启用此选项)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "在本地机器上" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "在定时任务中将标准输出重定向到 /dev/null。" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "如果安装了 MTA,Cron 将自动发送一封附有 cronjobs 输出的电子邮件。" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "在定时任务中将标准错误重定向到 /dev/null。" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "如果安装了 MTA,Cron 将自动发送一封附有 cronjobs 错误的电子邮件。" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "限制 rsync 使用带宽:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/秒" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "保留 ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "保留扩展属性 (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "复制不安全的链接(仅绝对链接有效)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "限制为一个文件系统" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "选项必须使用引号,例如:{example}。" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "粘贴附加选项到 rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "为 SSH 命令添加前缀" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "在远程主机上每个命令之前运行的前缀。" #: qt/settingsdialog.py:1188 #, 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:1196 msgid "default" msgstr "默认" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "检查远程主机是否在线" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "警告:如果禁用且远程主机不可用,这可能会导致一些奇怪的错误。" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "检查远程主机是否支持所有必要的命令。" #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "还原配置" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "编辑用户回调" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "我们将在不久的将来停止支持 EncFS。具体的加密备份替代方案还在商讨中,结果取决于项目资源和贡献者数量。详情请参考{whitepaper}。" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "新建配置" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "重命名配置" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "是否确定删除配置“{name}”?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "{BOLD}强烈推荐{ENDBOLD}:(已包含所有建议。)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}强烈推荐{ENDBOLD}:{files}" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "您没有选择 SSH 的私钥文件。" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "你还没有为 SSH 选择私钥文件。你想要生成新的无密码公/私密钥对吗?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "私钥文件 \"{file}\" 不存在。" #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "你想要将 SSH 公钥复制到远程主机来使用无密码登录吗?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "无法确定主机 {host} 的真实性。" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} 密钥指纹为:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "请验证此指纹! 您想将其添加到您的“known_hosts”文件中吗?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "排除模式" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "排除文件" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "排除文件夹" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "包含文件" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "“{path}”是一个符号链接。 除非您也将链接的目标包含在内,否则不会对其进行备份。\n" "您想改为包含符号链接的目标吗?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "包含文件夹" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "确定要改变快照文件夹吗?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "在 {path} 创建 SSH 密钥失败。" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "已禁用,因为此模式在“SSH 加密”模式下不起作用。" #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(默认: {})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "已停用" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "已启用" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "导入配置" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "没有找到配置" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "导入" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "选择要从中导入配置文件的快照文件夹。路径可能类似于:{samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "如果该文件夹位于外部或远程驱动器上,则必须事先手动挂载。" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "有关比较快照的选项" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "命令:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "参数:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "用 %1 和 %2 作为路径参数" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "请设置diff命令或按取消。" #: qt/snapshotsdialog.py:90 #, 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:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "diff 命令未设置参数。使用默认值“{params}”。" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "仅不同快照" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "仅列出与以下内容相同的快照:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "深度检查(更精确,但是较慢)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "删除" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "选择所有" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "比较" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "转到" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "选项" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "不能将快照与其自身比较。" #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "你确定要删除快照 \"{snapshot_id}\" 的\"{file}\" 吗?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "你确定要在 {count} 份快照中都删除的 \"{file}\" 吗?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "该操作无法撤销." #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "在未来的快照中排除 \"{path}\"?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "无法找到 crontab。您确定已经安装了 cron?如果没有安装,您应当禁用所有自动备份。" #~ msgid "Full snapshot path" #~ msgstr "快照完整路径" #~ msgid "Mode" #~ msgstr "模式" #~ msgid "Profile" #~ msgstr "配置" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "配置文件“{profile}”:输入 {mode} 的密码: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "用户回调脚本中的 Shebang 不可执行。" #~ msgid "WARNING" #~ msgstr "警告" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "encfs 1.7.2 及更早的版本中 --reverse 选项存在缺陷,请更新系统中安装的 encfs 版本。" #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "用户回调脚本没有 shebang (#!/bin/sh) 行。" #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} 使用 EncFS 进行加密。 最近的安全审计揭示了几种可能的攻击方式。 请看一下“man backintime”中的“安全注意事项”。" backintime-1.5.2/common/po/zh_TW.po000066400000000000000000001457231465446530500171560ustar00rootroot00000000000000# 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. # msgid "" msgstr "" "Project-Id-Version: backintime\n" "Report-Msgid-Bugs-To: c.buhtz@posteo.jp\n" "POT-Creation-Date: 2024-07-22 21:49+0200\n" "PO-Revision-Date: 2024-08-05 20:13+0000\n" "Last-Translator: buhtz \n" "Language-Team: Chinese (Traditional) \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-Generator: Weblate 5.6.2\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:44 qt/encfsmsgbox.py:24 qt/settingsdialog.py:1262 msgid "Warning" msgstr "警告" #: common/config.py:148 common/config.py:290 msgid "Main profile" msgstr "主要設定檔" #: common/config.py:297 msgid "Local (EncFS encrypted)" msgstr "本地(EncFS 加密)" #: common/config.py:298 msgid "SSH (EncFS encrypted)" msgstr "SSH(EncFS 加密)" #: common/config.py:309 msgid "Local" msgstr "本地端" #: common/config.py:311 msgid "SSH" msgstr "SSH" #: common/config.py:311 common/config.py:321 qt/settingsdialog.py:2185 msgid "SSH private key" msgstr "SSH 私鑰" #: common/config.py:314 msgid "Local encrypted" msgstr "本地加密的" #: common/config.py:315 common/config.py:322 msgid "Encryption" msgstr "加密" #: common/config.py:320 msgid "SSH encrypted" msgstr "SSH 加密" #: common/config.py:327 msgid "Default" msgstr "預設" #: common/config.py:358 common/config.py:370 common/config.py:387 #: common/config.py:398 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "設定檔:\"{name}\"" #: common/config.py:359 msgid "Snapshots folder is not valid!" msgstr "快照資料夾不合規定!" #: common/config.py:371 msgid "You must select at least one folder to back up!" msgstr "必須選擇一個資料夾用來備份!" #: common/config.py:388 msgid "Backup folder cannot be included." msgstr "無法包含備份資料夾。" #: common/config.py:400 msgid "Backup sub-folder cannot be included." msgstr "不能包含備份子資料夾。" #: common/config.py:447 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} 並不是資料夾。" #: common/config.py:456 msgid "Host/User/Profile-ID must not be empty." msgstr "主機/使用者/設定檔 ID 不得空白。" #: common/config.py:466 common/config.py:513 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "不能寫入:{path}\n" "請確認是否有寫入權限?" #: common/config.py:483 #, python-brace-format msgid "" "Destination filesystem for {path} is formatted with FAT which doesn't " "support hard-links. Please use a native Linux filesystem." msgstr "{path} 的目標檔案系統採用 FAT 格式,不支援硬連結。 請使用本機 Linux 檔案系統。" #: common/config.py:492 #, python-brace-format msgid "" "Destination filesystem for {path} is an SMB-mounted share. Please make sure " "the remote SMB server supports symlinks or activate {copyLinks} in " "{expertOptions}." msgstr "" "{path} 的目標檔案系統是 SMB 安裝的共用。 請確保遠端 SMB 伺服器支援符號連結或啟動 {expertOptions} 中的 " "{copyLinks}。" #: common/config.py:496 qt/settingsdialog.py:1127 msgid "Copy links (dereference symbolic links)" msgstr "複製鏈結(取消引用符式鏈結)" #: common/config.py:497 msgid "Expert Options" msgstr "進階選項" #: common/config.py:501 #, python-brace-format msgid "" "Destination filesystem for {path} is an sshfs-mounted share. Sshfs doesn't " "support hard-links. Please use mode 'SSH' instead." msgstr "{path} 的目標檔案系統是 sshfs 掛載並共用。 sshfs 不支援硬式鏈結。 請改用“SSH”模式。" #: common/config.py:1658 msgid "Failed to write new crontab." msgstr "無法寫入新的 crontab。" #: common/config.py:1666 msgid "" "Cron is not running despite the crontab command being available. Scheduled " "backup jobs will not run. Cron might be installed but not enabled. Try the " "command \"systemctl enable cron\" or consult the support channels of your " "GNU Linux distribution." msgstr "" "儘管 crontab 命令可用,但 Cron 並未運行。已排程的備份作業將不會運作。 Cron 可能已安裝但未啟用。嘗試命令「systemctl " "enable cron」或諮詢您的 GNU Linux 發行版的支援管道。" #: common/config.py:1746 #, 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:1761 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "排程 udev 不適用於 {mode} 模式" #: common/config.py:1772 #, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "找不到 {path} 的 UUID" #: common/configfile.py:107 msgid "Failed to save config" msgstr "儲存配置失敗" #: common/configfile.py:143 msgid "Failed to load config" msgstr "載入配置失敗" #: common/configfile.py:690 common/configfile.py:789 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\" 設定檔已存在。" #: common/configfile.py:735 msgid "The last profile cannot be removed." msgstr "最後一個設定檔無法刪除。" #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "無法掛載 “{command}”" #: common/encfstools.py:139 msgid "Config for encrypted folder not found." msgstr "找不到加密資料夾的配置。" #: common/encfstools.py:147 msgid "Create a new encrypted folder?" msgstr "建立一個新的加密資料夾?" #: common/encfstools.py:151 msgid "Cancel" msgstr "取消" #: common/encfstools.py:156 msgid "Please confirm the password." msgstr "請確認密碼。" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "密碼不符。" #: common/encfstools.py:521 common/snapshots.py:1034 msgid "Take snapshot" msgstr "進行快照" #: common/mount.py:609 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "無法從 {mountpoint} 卸載 {mountprocess}。" #: common/mount.py:696 #, python-brace-format msgid "{command} not found. Please install it (e.g. via \"{installcommand}\")" msgstr "找不到指令{command}。請安裝它(例如透過“{installcommand}”)" #: common/mount.py:720 #, python-brace-format msgid "Mountpoint {mntpoint} not empty." msgstr "掛載點 {mntpoint} 不為空。" #: common/password.py:269 #, python-brace-format msgid "Enter password for {mode} profile \"{profile}\":" msgstr "輸入 {mode} 設定檔「{profile}」的密碼:" #: common/snapshots.py:363 common/snapshots.py:614 msgid "FAILED" msgstr "失敗" #: common/snapshots.py:560 common/snapshots.py:622 msgid "Restore permissions" msgstr "回復權限" #: common/snapshots.py:617 qt/app.py:291 qt/app.py:997 qt/app.py:1032 #: qt/qtsystrayicon.py:81 msgid "Done" msgstr "完成" #: common/snapshots.py:737 msgid "Deferring backup while on battery" msgstr "使用電池時延遲備援" #: common/snapshots.py:835 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "找不到快照資料夾。\n" "如放在移動式磁碟,請先插上。" #: common/snapshots.py:839 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "等待%s秒。" #: common/snapshots.py:907 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "快照 {snapshot_id} 執行失敗。" #: common/snapshots.py:937 msgid "Finalizing" msgstr "關閉光碟中" #: common/snapshots.py:1069 msgid "Can't create folder" msgstr "無法建立資料夾" #: common/snapshots.py:1086 msgid "Saving config file…" msgstr "儲存設定檔…" #: common/snapshots.py:1167 msgid "Saving permissions…" msgstr "儲存權限…" #: common/snapshots.py:1279 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "找到剩餘的{snapshot_id},可以繼續。" #: common/snapshots.py:1302 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "正在刪除上次執行中剩餘的 {snapshot_id} 資料夾" #: common/snapshots.py:1312 msgid "Can't remove folder" msgstr "無法刪除資料夾" #: common/snapshots.py:1366 msgid "Taking snapshot" msgstr "進行快照" #: common/snapshots.py:1417 msgid "Success" msgstr "成功" #: common/snapshots.py:1420 msgid "Partial transfer due to error" msgstr "由於錯誤只轉移部分" #: common/snapshots.py:1421 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "由於來源檔消失而導致只傳輸部分(請見“man rsync”)" #: common/snapshots.py:1425 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "“rsync”以退出代碼 {exit_code} 結束" #: common/snapshots.py:1438 msgid "See 'man rsync' for more details" msgstr "有關更多詳細信息,請參閱“man rsync”" #: common/snapshots.py:1445 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "負 rsync 退出代碼是信號編號,請參閱“kill -l”和“man Kill”" #: common/snapshots.py:1466 msgid "Nothing changed, no new snapshot necessary" msgstr "沒有任何改變,不需要新的快照" #: common/snapshots.py:1510 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "無法將 {new_path} 重新命名為 {path}" #: common/snapshots.py:1828 common/snapshots.py:1880 msgid "Smart removal" msgstr "智慧移除" #: common/snapshots.py:1855 msgid "Removing old snapshots" msgstr "刪除快照" #: common/snapshots.py:1890 msgid "Trying to keep min free space" msgstr "嘗試保留最小剩餘空間" #: common/snapshots.py:1929 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "嘗試保留最小 {perc} 可用 inode" #: common/snapshots.py:2989 qt/app.py:1743 msgid "Now" msgstr "現在" #: common/sshtools.py:239 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "無法掛載 {sshfs}" #: common/sshtools.py:301 msgid "ssh-agent not found. Please make sure it is installed." msgstr "未找到 ssh 代理程式。 請確保它已安裝。" #: common/sshtools.py:444 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "無法解鎖 ssh 私鑰。 密碼錯誤或密碼不可用於 cron。" #: common/sshtools.py:535 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "{host} 的密碼 {cipher} 失敗。" #: common/sshtools.py:682 msgid "Remote path exists but is not a directory." msgstr "遠端路徑存在但不是目錄。" #: common/sshtools.py:687 msgid "Remote path is not writable." msgstr "遠端路徑不可寫入。" #: common/sshtools.py:692 msgid "Remote path is not executable." msgstr "遠端路徑不可執行。" #: common/sshtools.py:697 msgid "Couldn't create remote path." msgstr "無法建立遠端資料夾。" #: common/sshtools.py:981 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "遠端主機 {host} 不支援 {command}" #: common/sshtools.py:985 common/sshtools.py:994 msgid "Look at 'man backintime' for further instructions" msgstr "請參閱“man backintime”以獲取更多說明" #: common/sshtools.py:989 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "檢查主機 {host} 上的指令回傳未知錯誤" #: common/sshtools.py:1010 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "遠端主機 {host} 不支援硬連結" #: common/sshtools.py:1164 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"." msgstr "將ssh公鑰“{pubkey}”複製到遠端主機“{host}”。" #: common/sshtools.py:1166 #, python-brace-format msgid "Please enter a password for \"{user}\"." msgstr "請輸入「{user}」的密碼。" #: qt/aboutdlg.py:44 qt/app.py:572 msgid "About" msgstr "關於" #: qt/aboutdlg.py:68 qt/aboutdlg.py:106 msgid "Authors" msgstr "作者" #: qt/aboutdlg.py:69 qt/aboutdlg.py:112 msgid "Translations" msgstr "翻譯" #: qt/aboutdlg.py:70 qt/aboutdlg.py:118 msgid "License" msgstr "執照" #: qt/app.py:169 msgid "Shortcuts" msgstr "快速鍵" #: qt/app.py:189 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "該資料夾不存在\n" "在目前選定的快照中。" #: qt/app.py:256 msgid "Add to Include" msgstr "添加到包含" #: qt/app.py:258 msgid "Add to Exclude" msgstr "添加到排除" #: qt/app.py:343 #, python-brace-format msgid "" "{app_name} appears to be running for the first time as no configuration is " "found." msgstr "由於未找到配置,{app_name} 似乎是第一次運行。" #: qt/app.py:348 msgid "" "Import an existing configuration (from a backup target folder or another " "computer)?" msgstr "匯入現有配置(從備份目標資料夾或另一台電腦)?" #: qt/app.py:375 msgid "Can't find snapshots folder." msgstr "無法建立資料夾。" #: qt/app.py:376 msgid "If it is on a removable drive please plug it in and then press OK." msgstr "快照資料夾不存在,若存在於移動儲存設備,請插入裝置並確認。" #: qt/app.py:481 msgid "Take a snapshot" msgstr "進行快照" #: qt/app.py:483 msgid "Use modification time & size for file change detection." msgstr "使用修改時間和大小進行檔案變更檢測。" #: qt/app.py:486 msgid "Take a snapshot (checksum mode)" msgstr "建立快照(校驗和模式)" #: qt/app.py:488 msgid "Use checksums for file change detection." msgstr "使用校驗和進行文件更改檢測。" #: qt/app.py:491 qt/qtsystrayicon.py:86 msgid "Pause snapshot process" msgstr "暫停快照進程" #: qt/app.py:496 qt/qtsystrayicon.py:90 msgid "Resume snapshot process" msgstr "恢復快照過程" #: qt/app.py:500 qt/qtsystrayicon.py:95 msgid "Stop snapshot process" msgstr "停止快照進程" #: qt/app.py:504 msgid "Refresh snapshot list" msgstr "刷新快照列表" #: qt/app.py:508 msgid "Name snapshot" msgstr "快照名稱" #: qt/app.py:512 msgid "Remove snapshot" msgstr "刪除快照" #: qt/app.py:516 msgid "View snapshot log" msgstr "查看快照日誌" #: qt/app.py:520 msgid "View last log" msgstr "查看最後的日誌" #: qt/app.py:524 msgid "Manage profiles…" msgstr "管理設定檔…" #: qt/app.py:528 msgid "Shutdown" msgstr "關閉" #: qt/app.py:530 msgid "Shut down system after snapshot has finished." msgstr "快照完成後關閉系統。" #: qt/app.py:532 msgid "Setup language…" msgstr "設定語言…" #: qt/app.py:536 msgid "Exit" msgstr "離開" #: qt/app.py:540 msgid "Help" msgstr "求助" #: qt/app.py:544 msgid "Profiles config file" msgstr "設定檔" #: qt/app.py:547 qt/languagedialog.py:222 msgid "Website" msgstr "網站" #: qt/app.py:550 qt/app.py:1375 msgid "Changelog" msgstr "變更日誌" #: qt/app.py:553 msgid "FAQ" msgstr "常問問題" #: qt/app.py:556 msgid "Ask a question" msgstr "問題" #: qt/app.py:559 msgid "Report a bug" msgstr "報告錯誤" #: qt/app.py:562 msgid "Translation" msgstr "翻譯" #: qt/app.py:564 msgid "Shows the message about participation in translation again." msgstr "再次顯示參與翻譯的訊息。" #: qt/app.py:568 msgid "Encryption Transition (EncFS)" msgstr "加密轉換 (EncFS)" #: qt/app.py:570 msgid "Shows the message about EncFS removal again." msgstr "再次顯示有關 EncFS 刪除的訊息。" #: qt/app.py:575 qt/restoredialog.py:46 qt/snapshotsdialog.py:164 #: qt/snapshotsdialog.py:169 msgid "Restore" msgstr "還原" #: qt/app.py:577 msgid "Restore the selected files or folders to the original destination." msgstr "將選定的檔案或資料夾恢復到原始目的地。" #: qt/app.py:580 qt/app.py:1546 qt/app.py:1578 qt/snapshotsdialog.py:166 msgid "Restore to …" msgstr "還原…" #: qt/app.py:582 msgid "Restore the selected files or folders to a new destination." msgstr "將選定的檔案或資料夾還原到新的目標。" #: qt/app.py:587 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "將目前顯示的資料夾及其所有內容還原到原始目的地。" #: qt/app.py:592 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "將目前顯示的資料夾及其所有內容還原為新目標。" #: qt/app.py:595 msgid "Up" msgstr "向上" #: qt/app.py:598 qt/settingsdialog.py:2448 msgid "Show hidden files" msgstr "顯示隱藏檔案" #: qt/app.py:601 msgid "Compare snapshots…" msgstr "比較快照…" #: qt/app.py:660 msgid "Back In &Time" msgstr "Back In &Time" #: qt/app.py:665 msgid "&Backup" msgstr "&備份" #: qt/app.py:676 msgid "&Restore" msgstr "還原" #: qt/app.py:682 msgid "&Help" msgstr "求助" #: qt/app.py:818 msgid "" "If you close this window, Back In Time will not be able to shut down your " "system when the snapshot is finished." msgstr "如果您關閉此窗口,Back In Time 將無法在快照完成後關閉您的系統。" #: qt/app.py:821 msgid "Do you really want to close it?" msgstr "確認要刪除此快照?" #: qt/app.py:987 msgid "Working:" msgstr "運作中 :" #: qt/app.py:1035 msgid "Done, no backup needed" msgstr "已完成,不需要進行備份" #: qt/app.py:1044 msgid "Working" msgstr "運作中" #: qt/app.py:1053 qt/messagebox.py:78 msgid "Error" msgstr "錯誤" #: qt/app.py:1076 msgid "Sent" msgstr "傳送" #: qt/app.py:1077 msgid "Speed" msgstr "速度" #: qt/app.py:1078 msgid "ETA" msgstr "預計到達時間" #: qt/app.py:1140 msgid "Global" msgstr "全域設定" #: qt/app.py:1141 msgid "Root" msgstr "主目錄" #: qt/app.py:1142 msgid "Home" msgstr "家目錄" #: qt/app.py:1170 msgid "Backup folders" msgstr "備份資料夾" #: qt/app.py:1266 msgid "Snapshot Name" msgstr "快照名稱" #: qt/app.py:1313 msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "確認要刪除快照?" #: qt/app.py:1408 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "建立帶有尾隨 {suffix} 的備份副本\n" "在覆蓋或刪除本地元素之前。" #: qt/app.py:1416 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with the " "following command:" msgstr "在復原之前,較新版本的檔案將使用尾隨 {suffix} 進行重新命名。如果您不再需要它們,可以使用以下命令刪除它們:" #: qt/app.py:1432 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "只恢復不存在或\n" "比目的地新的元素。\n" "使用“rsync --update”選項。" #: qt/app.py:1467 msgid "Remove newer elements in original folder." msgstr "刪除原始資料夾中較新的元素。" #: qt/app.py:1470 msgid "" "Restore selected files or folders to the original destination and delete " "files or folders which are not in the snapshot. Be extremely careful because" " this will delete files and folders which were excluded during taking the " "snapshot." msgstr "將選定的檔案或資料夾恢復到原始目的地並刪除不在快照中的檔案或資料夾。要非常小心,因為這會刪除快照期間排除的檔案和資料夾。" #: qt/app.py:1481 #, python-brace-format msgid "" "Do you really want to restore this element into the new folder\n" "{path}?" msgid_plural "" "Do you really want to restore these elements into the new folder\n" "{path}?" msgstr[0] "" "您確實要將這些元素還原到新資料夾中嗎\n" "{path}?" #: qt/app.py:1490 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "你真的想恢復這些元素嗎?" #: qt/app.py:1505 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "您確定要刪除 {path} 中的所有較新檔案嗎?" #: qt/app.py:1508 #, fuzzy msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "您確定要刪除 {path} 中的所有較新檔案嗎?" #: qt/app.py:1514 #, python-brace-format msgid "" "{BOLD}Warning{BOLDEND}: Deleting files in the filesystem root could break " "your entire system." msgstr "{BOLD}警告{BOLDEND}:刪除檔案系統根目錄中的檔案可能會破壞整個系統。" #: qt/app.py:1750 msgid "Snapshot" msgstr "快照" #: qt/app.py:1787 #, python-brace-format msgid "Restore {path}" msgstr "還原{path}" #: qt/app.py:1789 #, python-brace-format msgid "Restore {path} to …" msgstr "還原{path}到…" #: qt/app.py:1948 msgid "The language settings take effect only after restarting Back In Time." msgstr "語言設定僅在重新啟動 Back In Time 後生效。" #: qt/encfsmsgbox.py:42 msgid "" "Support for EncFS will be discontinued in the foreseeable future. It is not " "recommended to use that mode for a profile furthermore." msgstr "在可預見的將來,將停止對 EncFS 的支援。不建議進一步對設定檔使用該模式。" #: qt/encfsmsgbox.py:46 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "More details are available in this {whitepaper}." msgstr "關於替換加密備份的持續支援的決定仍在等待中,具體取決於專案資源和貢獻者的可用性。此{whitepaper}中提供了更多詳細資訊。" #: qt/encfsmsgbox.py:52 qt/encfsmsgbox.py:84 qt/settingsdialog.py:1270 msgid "whitepaper" msgstr "白皮書" #: qt/encfsmsgbox.py:66 msgid "" "The support for encrypted snapshot profiles is undergoing significant " "changes, and EncFS will be removed in the foreseeable future." msgstr "對加密快照設定檔的支援正在發生重大變化,並且 EncFS 將在可預見的將來被刪除。" #: qt/encfsmsgbox.py:75 msgid "The following profile(s) use encryption with EncFS:" msgstr "以下設定檔使用 EncFS 進行加密:" #: qt/encfsmsgbox.py:77 #, python-brace-format msgid "" "A decision on a replacement for continued support of encrypted backups is " "still pending, depending on project resources and contributor availability. " "Users are invited to join this discussion. Updated details on the next steps" " are available in this {whitepaper}." msgstr "" "關於替換加密備份的持續支援的決定仍在等待中,具體取決於專案資源和貢獻者的可用性。邀請用戶加入此討論。本{whitepaper}中提供了有關後續步驟的更新詳細資訊。" #: qt/encfsmsgbox.py:85 msgid "" "This message will not be shown again. This dialog is available at any time " "via the help menu." msgstr "此訊息將不會再次顯示。您可以隨時透過幫助選單存取此對話框。" #: qt/encfsmsgbox.py:87 msgid "Your Back In Time Team" msgstr "您的Back In Time團隊" #: qt/languagedialog.py:30 msgid "Setup language" msgstr "設定語言" #: qt/languagedialog.py:92 msgid "System default" msgstr "系統預設" #: qt/languagedialog.py:99 msgid "Use operating systems language." msgstr "使用作業系統語言。" #: qt/languagedialog.py:150 #, python-brace-format msgid "Translated: {percent}" msgstr "翻譯: {percent}" #: qt/languagedialog.py:188 #, 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/languagedialog.py:217 msgid "translation platform" msgstr "翻譯平台" #: qt/languagedialog.py:242 msgid "Your translation" msgstr "您的翻譯" #: qt/logviewdialog.py:66 msgid "Last Log View" msgstr "最後一次日誌查看" #: qt/logviewdialog.py:68 msgid "Snapshot Log View" msgstr "快照日誌視圖" #: qt/logviewdialog.py:76 qt/settingsdialog.py:178 qt/settingsdialog.py:414 #: qt/settingsdialog.py:2627 msgid "Profile:" msgstr "設定:" #: qt/logviewdialog.py:84 msgid "Snapshots:" msgstr "快照:" #: qt/logviewdialog.py:99 msgid "Filter:" msgstr "篩選:" #: qt/logviewdialog.py:105 qt/settingsdialog.py:921 msgid "All" msgstr "全部" #: qt/logviewdialog.py:111 qt/logviewdialog.py:114 qt/settingsdialog.py:920 msgid "Changes" msgstr "變化" #: qt/logviewdialog.py:111 qt/logviewdialog.py:113 qt/settingsdialog.py:917 #: qt/settingsdialog.py:920 msgid "Errors" msgstr "錯誤" #: qt/logviewdialog.py:115 qt/messagebox.py:71 msgid "Information" msgid_plural "Information" msgstr[0] "資訊" #: qt/logviewdialog.py:117 msgid "rsync transfer failures (experimental)" msgstr "rsync 傳輸失敗(實驗性)" #: qt/logviewdialog.py:128 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] 錯誤,[I] 訊息,[C] 更改" #: qt/logviewdialog.py:131 qt/qtsystrayicon.py:99 msgid "decode paths" msgstr "解碼路徑" #: qt/messagebox.py:87 qt/messagebox.py:97 msgid "Question" msgstr "問題" #: qt/qtsystrayicon.py:77 #, python-brace-format msgid "Profile: {profile_name}" msgstr "設定檔:{profile_name}" #: qt/qtsystrayicon.py:104 msgid "View Last Log" msgstr "查看最後的日誌" #: qt/qtsystrayicon.py:108 #, python-brace-format msgid "Start {appname}" msgstr "啟動{appname}" #: qt/qtsystrayicon.py:174 msgid "Working…" msgstr "運作中…" #: qt/qtsystrayicon.py:205 msgid "Sent:" msgstr "傳送:" #: qt/qtsystrayicon.py:206 msgid "Speed:" msgstr "速度:" #: qt/qtsystrayicon.py:207 msgid "ETA:" msgstr "ETA:" #: qt/qttools.py:404 qt/snapshotsdialog.py:123 msgid "Snapshots" msgstr "快照" #: qt/qttools.py:427 msgid "Today" msgstr "今天" #: qt/qttools.py:434 msgid "Yesterday" msgstr "昨天" #: qt/qttools.py:443 msgid "This week" msgstr "本週" #: qt/qttools.py:450 msgid "Last week" msgstr "上週" #: qt/qttools.py:596 msgid "This is NOT a snapshot but a live view of your local files" msgstr "這不是快照,而是本地文件的即時視圖" #: qt/qttools.py:601 #, python-brace-format msgid "Last check {time}" msgstr "最後一次檢查{time}" #: qt/restoredialog.py:59 msgid "Show full Log" msgstr "顯示完整日誌" #: qt/settingsdialog.py:96 msgid "SSH Proxy" msgstr "SSH 代理" #: qt/settingsdialog.py:103 qt/settingsdialog.py:275 qt/settingsdialog.py:402 msgid "Host:" msgstr "主機:" #: qt/settingsdialog.py:107 qt/settingsdialog.py:280 msgid "Port:" msgstr "端口:" #: qt/settingsdialog.py:111 qt/settingsdialog.py:285 qt/settingsdialog.py:408 msgid "User:" msgstr "用戶:" #: qt/settingsdialog.py:120 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 "" "透過此代理程式(也稱為跳轉主機)連接到目標主機。有關詳細信息,請參閱“ssh”命令文件中的“-J”或“ssh_config”手冊頁中的“ProxyJump”。" #: qt/settingsdialog.py:170 msgid "Manage profiles" msgstr "管理設定檔" #: qt/settingsdialog.py:187 msgid "Edit" msgstr "編輯" #: qt/settingsdialog.py:191 qt/settingsdialog.py:668 msgid "Add" msgstr "添加" #: qt/settingsdialog.py:195 qt/settingsdialog.py:615 qt/settingsdialog.py:686 msgid "Remove" msgstr "消除" #: qt/settingsdialog.py:210 msgid "&General" msgstr "一般" #: qt/settingsdialog.py:220 qt/settingsdialog.py:2629 msgid "Mode:" msgstr "模式:" #: qt/settingsdialog.py:239 qt/settingsdialog.py:2161 msgid "Where to save snapshots" msgstr "快照存放位置" #: qt/settingsdialog.py:255 msgid "Folder" msgstr "資料夾" #: qt/settingsdialog.py:263 msgid "SSH Settings" msgstr "SSH 設定" #: qt/settingsdialog.py:290 msgid "Path:" msgstr "路徑:" #: qt/settingsdialog.py:296 msgid "Cipher:" msgstr "密碼:" #: qt/settingsdialog.py:302 msgid "Private Key:" msgstr "私鑰:" #: qt/settingsdialog.py:312 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "選擇現有的私鑰檔案(通常命名為“id_rsa”)" #: qt/settingsdialog.py:323 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)." msgstr "建立一個新的無密碼 SSH 金鑰(如果已選擇私鑰檔案則不允許)。" #: qt/settingsdialog.py:353 qt/settingsdialog.py:362 qt/settingsdialog.py:368 msgid "Password" msgstr "密碼" #: qt/settingsdialog.py:374 msgid "Save Password to Keyring" msgstr "將密碼儲存到密鑰環" #: qt/settingsdialog.py:378 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "Cron 的快取密碼(安全性問題:root 可以讀取密碼" #: qt/settingsdialog.py:390 msgid "Advanced" msgstr "進階" #: qt/settingsdialog.py:420 qt/settingsdialog.py:2283 msgid "Full snapshot path:" msgstr "完整快照路徑:" #: qt/settingsdialog.py:427 msgid "Schedule" msgstr "排程" #: qt/settingsdialog.py:439 msgid "Disabled" msgstr "關閉" #: qt/settingsdialog.py:440 msgid "At every boot/reboot" msgstr "每次啟動/重新啟動時" #: qt/settingsdialog.py:442 qt/settingsdialog.py:444 qt/settingsdialog.py:446 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "每{n}分鐘" #: qt/settingsdialog.py:448 #, python-brace-format msgid "Every hour" msgid_plural "Every {n} hours" msgstr[0] "每隔{n}小時" #: qt/settingsdialog.py:450 qt/settingsdialog.py:452 qt/settingsdialog.py:454 #: qt/settingsdialog.py:456 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "每{n}月" #: qt/settingsdialog.py:457 msgid "Custom hours" msgstr "客製化時間" #: qt/settingsdialog.py:458 msgid "Every day" msgstr "每天" #: qt/settingsdialog.py:459 msgid "Repeatedly (anacron)" msgstr "重複(anacron)" #: qt/settingsdialog.py:460 msgid "When drive gets connected (udev)" msgstr "當驅動器連接 (udev)" #: qt/settingsdialog.py:461 msgid "Every week" msgstr "每週" #: qt/settingsdialog.py:462 msgid "Every month" msgstr "每月" #: qt/settingsdialog.py:463 msgid "Every year" msgstr "每年" #: qt/settingsdialog.py:468 msgid "Day:" msgstr "天:" #: qt/settingsdialog.py:479 msgid "Weekday:" msgstr "工作日:" #: qt/settingsdialog.py:495 msgid "Hour:" msgstr "時:" #: qt/settingsdialog.py:510 msgid "Hours:" msgstr "時:" #: qt/settingsdialog.py:521 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "反覆運行Back In Time。 如果計算機沒有正常運行,這非常有用。" #: qt/settingsdialog.py:528 msgid "Every:" msgstr "每天:" #: qt/settingsdialog.py:542 msgid "Hour(s)" msgstr "小時" #: qt/settingsdialog.py:543 qt/settingsdialog.py:738 msgid "Day(s)" msgstr "天" #: qt/settingsdialog.py:544 qt/settingsdialog.py:739 msgid "Week(s)" msgstr "週" #: qt/settingsdialog.py:545 msgid "Month(s)" msgstr "月" #: qt/settingsdialog.py:555 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 "" "連接驅動器後立即運行Back In Time(每 X 天一次)。\n" "系統將提示您輸入 sudo 密碼。" #: qt/settingsdialog.py:564 msgid "Enable logging of debug messages" msgstr "啟用偵錯訊息的日誌記錄" #: qt/settingsdialog.py:568 msgid "Writes debug-level messages into the system log via \"--debug\"." msgstr "透過“--debug”將偵錯等級訊息寫入系統日誌。" #: qt/settingsdialog.py:570 msgid "" "Caution: Only use this temporarily for diagnostics, as it generates a large " "amount of output." msgstr "注意:僅臨時使用它進行診斷,因為它會產生大量輸出。" #: qt/settingsdialog.py:583 msgid "&Include" msgstr "包含" #: qt/settingsdialog.py:590 msgid "Include files and folders" msgstr "包括文件和資料夾" #: qt/settingsdialog.py:607 qt/settingsdialog.py:672 msgid "Add file" msgstr "新增檔案" #: qt/settingsdialog.py:611 qt/settingsdialog.py:676 msgid "Add folder" msgstr "新增資料夾" #: qt/settingsdialog.py:621 msgid "&Exclude" msgstr "不包含" #: qt/settingsdialog.py:625 #, 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/settingsdialog.py:648 msgid "Exclude patterns, files or folders" msgstr "排除模式、檔案或資料夾" #: qt/settingsdialog.py:681 msgid "Add default" msgstr "新增預設值" #: qt/settingsdialog.py:694 msgid "Exclude files bigger than:" msgstr "排除大於以下文件:" #: qt/settingsdialog.py:698 #, python-brace-format msgid "Exclude files bigger than value in {size_unit}." msgstr "排除大於以下文件{size_unit}." #: qt/settingsdialog.py:700 msgid "" "With 'Full rsync mode' disabled, this will only impact new files since for " "rsync, this is a transfer option, not an exclusion option. Therefore, large " "files that have been backed up previously will persist in snapshots even if " "they have been modified." msgstr "" "排除大於 %(prefix)s 中的值的檔案。停用「完全 rsync 模式」後,這只會影響新文件因為對於 rsync " "這是一個傳輸選項,而不是排除選項。因此之前備份過的大檔案將保留在快照中,即使他們已經改變。" #: qt/settingsdialog.py:720 msgid "&Auto-remove" msgstr "自動移除" #: qt/settingsdialog.py:726 msgid "Older than:" msgstr "早于:" #: qt/settingsdialog.py:740 msgid "Year(s)" msgstr "年" #: qt/settingsdialog.py:748 msgid "If free space is less than:" msgstr "剩餘空間少於:" #: qt/settingsdialog.py:768 msgid "If free inodes is less than:" msgstr "剩餘空間少於:" #: qt/settingsdialog.py:782 msgid "Smart removal:" msgstr "智慧移除:" #: qt/settingsdialog.py:793 msgid "Run in background on remote host." msgstr "在遠端主機的後台運行。" #: qt/settingsdialog.py:794 msgid "EXPERIMENTAL" msgstr "實驗性的" #: qt/settingsdialog.py:800 msgid "Keep all snapshots for the last" msgstr "將所有快照保留到最後" #: qt/settingsdialog.py:804 qt/settingsdialog.py:811 msgid "day(s)." msgstr "天." #: qt/settingsdialog.py:807 msgid "Keep one snapshot per day for the last" msgstr "每天保留一張快照作為最後一次" #: qt/settingsdialog.py:814 msgid "Keep one snapshot per week for the last" msgstr "每周保留一張快照作為最後一次" #: qt/settingsdialog.py:818 msgid "week(s)." msgstr "週." #: qt/settingsdialog.py:821 msgid "Keep one snapshot per month for the last" msgstr "每月保留一張快照作為最後一次" #: qt/settingsdialog.py:825 msgid "month(s)." msgstr "月。" #: qt/settingsdialog.py:828 msgid "Keep one snapshot per year for all years." msgstr "所有年份每年保留一個快照。" #: qt/settingsdialog.py:837 msgid "Don't remove named snapshots." msgstr "不要移除命名的快照." #: qt/settingsdialog.py:849 msgid "&Options" msgstr "選項" #: qt/settingsdialog.py:854 msgid "Enable notifications" msgstr "開啟通知" #: qt/settingsdialog.py:858 msgid "Disable snapshots when on battery" msgstr "使用電池時停用快照功能" #: qt/settingsdialog.py:862 msgid "Power status not available from system" msgstr "無法獲得系統電源狀態" #: qt/settingsdialog.py:865 msgid "Run only one snapshot at a time" msgstr "一次僅運行一個快照" #: qt/settingsdialog.py:868 msgid "" "Other snapshots will be blocked until the current snapshot is done. This is " "a global option. So it will affect all profiles for this user. But you need " "to activate this for all other users, too." msgstr "其他快照將被阻止,直到目前快照完成。這是一個全域選項。 因此它將影響該用戶的所有設定檔。但您也需要為所有其他用戶啟動此功能。" #: qt/settingsdialog.py:876 msgid "Backup replaced files on restore" msgstr "恢復時備份替換的文件" #: qt/settingsdialog.py:879 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring. If you don't need them anymore you can remove them with {cmd}" msgstr "在復原之前,較新版本的檔案將使用 {suffix} 進行重新命名。如果您不再需要它們,可以使用 {cmd} 刪除它們" #: qt/settingsdialog.py:891 msgid "Continue on errors (keep incomplete snapshots)" msgstr "出錯時繼續(保留不完整的快照)" #: qt/settingsdialog.py:895 msgid "Use checksum to detect changes" msgstr "使用校驗和來檢測更改" #: qt/settingsdialog.py:899 msgid "Take a new snapshot whether there were changes or not." msgstr "無論是否發生更改,都拍攝新快照。" #: qt/settingsdialog.py:906 msgid "Log Level:" msgstr "日誌等級:" #: qt/settingsdialog.py:911 msgid "None" msgstr "沒有任何" #: qt/settingsdialog.py:931 msgid "E&xpert Options" msgstr "專家選項" #: qt/settingsdialog.py:936 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "注意:只有當您確實知道自己在做什麼時才更改這些選項。" #: qt/settingsdialog.py:941 qt/settingsdialog.py:957 qt/settingsdialog.py:979 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "使用“{cmd}”運行“rsync”:" #: qt/settingsdialog.py:948 qt/settingsdialog.py:964 msgid "as cron job" msgstr "作為 cron 作業" #: qt/settingsdialog.py:953 qt/settingsdialog.py:974 qt/settingsdialog.py:995 msgid "on remote host" msgstr "在遠端主機上" #: qt/settingsdialog.py:969 msgid "when taking a manual snapshot" msgstr "進行手動快照時" #: qt/settingsdialog.py:982 msgid "(Please install 'nocache' to enable this option)" msgstr "(請安裝“nocache”以啟用此選項)" #: qt/settingsdialog.py:989 msgid "on local machine" msgstr "本機" #: qt/settingsdialog.py:1000 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "在 cronjobs 中將 stdout 重新導向到 /dev/null。" #: qt/settingsdialog.py:1005 msgid "" "Cron will automatically send an email with attached output of cronjobs if an" " MTA is installed." msgstr "如果安裝了 MTA,Cron 將自動發送一封電子郵件,其中附有 cronjobs 的輸出。" #: qt/settingsdialog.py:1011 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "在 cronjobs 中將 stdout 重新導向到 /dev/null。" #: qt/settingsdialog.py:1016 msgid "" "Cron will automatically send an email with attached errors of cronjobs if an" " MTA is installed." msgstr "如果安裝了 MTA,Cron 將自動發送一封電子郵件,其中附有 cronjobs 錯誤。" #: qt/settingsdialog.py:1024 msgid "Limit rsync bandwidth usage:" msgstr "限制 rsync 頻寬:" #: qt/settingsdialog.py:1027 msgid "KB/sec" msgstr "KB/秒" #: qt/settingsdialog.py:1071 msgid "Preserve ACL" msgstr "保留 ACL" #: qt/settingsdialog.py:1089 msgid "Preserve extended attributes (xattr)" msgstr "保留擴充屬性 (xattr)" #: qt/settingsdialog.py:1112 msgid "Copy unsafe links (works only with absolute links)" msgstr "複製不安全連結(僅適用於絕對連結)" #: qt/settingsdialog.py:1148 msgid "Restrict to one file system" msgstr "限制為一個檔案系統" #: qt/settingsdialog.py:1168 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "選項必須加引號,例如{example}。" #: qt/settingsdialog.py:1171 msgid "Paste additional options to rsync" msgstr "將附加選項貼到 rsync" #: qt/settingsdialog.py:1185 msgid "Add prefix to SSH commands" msgstr "為 SSH 指令加上前綴" #: qt/settingsdialog.py:1187 msgid "Prefix to run before every command on remote host." msgstr "在遠端主機上的每個命令之前運行的前綴。" #: qt/settingsdialog.py:1188 #, 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。 " "所以要加上前綴{example_value}{rsync_options_value}。" #: qt/settingsdialog.py:1196 msgid "default" msgstr "預設" #: qt/settingsdialog.py:1211 msgid "Check if remote host is online" msgstr "檢查遠端主機是否在線" #: qt/settingsdialog.py:1214 msgid "" "Warning: If disabled and the remote host is not available, this could lead " "to some weird errors." msgstr "警告:如果已停用且遠端主機不可用,這可能會導致一些奇怪的錯誤。" #: qt/settingsdialog.py:1218 msgid "Check if remote host supports all necessary commands." msgstr "檢查遠端主機是否支援所有必要的命令." #: qt/settingsdialog.py:1221 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:1237 msgid "Restore Config" msgstr "恢復配置" #: qt/settingsdialog.py:1239 msgid "Edit user-callback" msgstr "編輯用戶回調" #: qt/settingsdialog.py:1263 #, python-brace-format msgid "" "Support for EncFS will be discontinued in the foreseeable future. A decision" " on a replacement for continued support of encrypted backups is still " "pending, depending on project resources and contributor availability. More " "details are available in this {whitepaper}." msgstr "" "在可預見的將來,將停止對 EncFS " "的支援。關於替換加密備份的持續支援的決定仍在等待中,具體取決於專案資源和貢獻者的可用性。此{whitepaper}中提供了更多詳細資訊。" #: qt/settingsdialog.py:1285 msgid "New profile" msgstr "新增設定檔" #: qt/settingsdialog.py:1302 msgid "Rename profile" msgstr "重新命名設定檔" #: qt/settingsdialog.py:1318 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "確認刪除設定檔\"{name}\"?" #: qt/settingsdialog.py:1422 #, python-brace-format msgid "" "{BOLD}Highly recommended{ENDBOLD}: (All recommendations already included.)" msgstr "{BOLD}強烈推薦{ENDBOLD}:(所有建議均已包含在內。)" #: qt/settingsdialog.py:1427 #, python-brace-format msgid "{BOLD}Highly recommended{ENDBOLD}: {files}" msgstr "{BOLD}強烈推薦{ENDBOLD}:{files}" #: qt/settingsdialog.py:1634 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:1681 msgid "You did not choose a private key file for SSH." msgstr "您沒有為 SSH 選擇私鑰檔案。" #: qt/settingsdialog.py:1682 msgid "" "Would you like to generate a new password-less public/private key pair?" msgstr "您沒有為 SSH 選擇私鑰檔案。您想產生一個新的無密碼公鑰/私鑰對嗎?" #: qt/settingsdialog.py:1692 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "私鑰檔案“{file}”不存在。" #: qt/settingsdialog.py:1847 msgid "" "Would you like to copy your public SSH key to the remote host to enable " "password-less login?" msgstr "您想要將您的SSH 公 鑰複製到遠端主機啟用無密碼登入?" #: qt/settingsdialog.py:1878 #, python-brace-format msgid "The authenticity of host {host} can't be established." msgstr "無法確定主機 {host} 的真實性。" #: qt/settingsdialog.py:1881 #, python-brace-format msgid "{keytype} key fingerprint is:" msgstr "{keytype} 指紋是:" #: qt/settingsdialog.py:1889 msgid "" "Please verify this fingerprint. Would you like to add it to your " "'known_hosts' file?" msgstr "請驗證此指紋. 您想將其新增至您的「known_hosts」檔案嗎?" #: qt/settingsdialog.py:2061 msgid "Exclude pattern" msgstr "排除樣式" #: qt/settingsdialog.py:2074 msgid "Exclude file" msgstr "排除檔案" #: qt/settingsdialog.py:2078 msgid "Exclude folder" msgstr "排除資料夾" #: qt/settingsdialog.py:2102 msgid "Include file" msgstr "包含檔案" #: qt/settingsdialog.py:2111 qt/settingsdialog.py:2141 #, python-brace-format msgid "" "\"{path}\" is a symlink. The linked target will not be backed up until you include it, too.\n" "Would you like to include the symlink target instead?" msgstr "" "{path}是一個符號連結。 除非您也將連結的目標包含在內,否則不會對其進行備份。\n" "您想改為包含符號連結目標嗎?" #: qt/settingsdialog.py:2132 msgid "Include folder" msgstr "包含資料夾" #: qt/settingsdialog.py:2169 msgid "Are you sure you want to change snapshots folder?" msgstr "確認修改快照資料夾?" #: qt/settingsdialog.py:2194 #, python-brace-format msgid "Failed to create new SSH key in {path}." msgstr "無法在 {path} 中建立新的 SSH 金鑰。" #: qt/settingsdialog.py:2310 msgid "" "Disabled because this pattern is not functional in mode 'SSH encrypted'." msgstr "已停用,因為此模式在「SSH 加密」模式下不起作用。" #: qt/settingsdialog.py:2369 msgid "(default: {})" msgstr "(預設:{})" #: qt/settingsdialog.py:2370 msgid "disabled" msgstr "被禁用" #: qt/settingsdialog.py:2370 msgid "enabled" msgstr "已啟用" #: qt/settingsdialog.py:2413 msgid "Import configuration" msgstr "導入配置" #: qt/settingsdialog.py:2469 qt/settingsdialog.py:2565 msgid "No config found" msgstr "沒有找到配置" #: qt/settingsdialog.py:2494 msgid "Import" msgstr "導入" #: qt/settingsdialog.py:2522 #, python-brace-format msgid "" "Select the snapshot folder from which the configuration file should be " "imported. The path may look like: {samplePath}" msgstr "選擇應從中匯入設定檔的快照資料夾。該路徑可能類似於:{samplePath}" #: qt/settingsdialog.py:2527 msgid "" "If the folder is located on an external or remote drive, it must be manually" " mounted beforehand." msgstr "如果該資料夾位於外部或遠端磁碟機上,則必須事先手動安裝。" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "有關比較快照的選項" #: qt/snapshotsdialog.py:58 msgid "Command:" msgstr "命令:" #: qt/snapshotsdialog.py:62 msgid "Parameters:" msgstr "參數:" #: qt/snapshotsdialog.py:67 msgid "Use %1 and %2 for path parameters" msgstr "使用%1和%2作為路徑參數" #: qt/snapshotsdialog.py:84 msgid "Please set a diff command or press Cancel." msgstr "請設定 diff 指令或按「取消」。" #: qt/snapshotsdialog.py:90 #, 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:98 #, python-brace-format msgid "No parameters set for the diff command. Using default value \"{params}\"." msgstr "沒有為 diff 指令設定參數。使用預設值“{params}”。" #: qt/snapshotsdialog.py:134 msgid "Differing snapshots only" msgstr "僅不同快照" #: qt/snapshotsdialog.py:142 msgid "List only snapshots that are equal to:" msgstr "僅列出與以下內容相同的快照:" #: qt/snapshotsdialog.py:153 msgid "Deep check (more accurate, but slow)" msgstr "深度檢查(更準確,但速度慢)" #: qt/snapshotsdialog.py:174 msgid "Delete" msgstr "刪除" #: qt/snapshotsdialog.py:178 msgid "Select All" msgstr "全選" #: qt/snapshotsdialog.py:191 msgid "Compare" msgstr "比較" #: qt/snapshotsdialog.py:203 msgid "Go To" msgstr "前往" #: qt/snapshotsdialog.py:204 msgid "Options" msgstr "選項" #: qt/snapshotsdialog.py:355 msgid "You can't compare a snapshot to itself." msgstr "無法進行快照自我比對." #: qt/snapshotsdialog.py:398 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "您確實要刪除快照 {snapshot_id} 中的 {file} 嗎?" #: qt/snapshotsdialog.py:404 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "您確實要刪除 {count} 個快照中的 {file} 嗎?" #: qt/snapshotsdialog.py:408 msgid "WARNING: This cannot be revoked." msgstr "警告:此操作無法撤銷。" #: qt/snapshotsdialog.py:426 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "從未來的快照中排除 {path} 嗎?" #~ msgid "" #~ "Can't find crontab. Are you sure cron is installed? If not you should " #~ "disable all automatic backups." #~ msgstr "找不到 crontab 檔案。請確認是否已安裝 cron。如還沒,你應該停用所有自動備份。" #~ msgid "Full snapshot path" #~ msgstr "完整快照路徑" #~ msgid "Mode" #~ msgstr "模式" #, fuzzy #~ msgid "Profile" #~ msgstr "設定組合" #, python-brace-format #~ msgid "Profile '{profile}': Enter password for {mode}: " #~ msgstr "設定檔「{profile}」:輸入 {mode} 的密碼: " #~ msgid "Shebang in user-callback script is not executable." #~ msgstr "使用者回調腳本中的 Shebang 不可執行。" #~ msgid "WARNING" #~ msgstr "警告" #~ msgid "" #~ "encfs version 1.7.2 and before has a bug with option --reverse. Please " #~ "update encfs." #~ msgstr "encfs 版本 1.7.2 及之前版本存在選項 --reverse 的錯誤。 請更新 encfs。" #, fuzzy #~ msgid "user-callback script has no shebang (#!/bin/sh) line." #~ msgstr "使用者回呼腳本沒有 shebang (#!/bin/sh) 行。" #, python-brace-format #~ msgid "" #~ "{app} 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 "" #~ "{app} 使用 EncFS 進行加密。 最近的安全審計揭示了幾種可能的攻擊媒介。 請看一下「man backintime」中的「安全注意事項」。" backintime-1.5.2/common/progress.py000066400000000000000000000025401465446530500173500ustar00rootroot00000000000000# Copyright (C) 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. # # 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 class ProgressFile(configfile.ConfigFile): RSYNC = 50 def __init__(self, cfg, filename = None): super(ProgressFile, self).__init__() self.config = cfg self.filename = filename if self.filename is None: self.filename = self.config.takeSnapshotProgressFile() def save(self): return super(ProgressFile, self).save(self.filename) def load(self): return super(ProgressFile, self).load(self.filename) def fileReadable(self): return os.access(self.filename, os.R_OK) backintime-1.5.2/common/qt_probing.py000066400000000000000000000142431465446530500176530ustar00rootroot00000000000000import os import sys import resource import logger logger.openlog() # from tools import isRoot # This mini python script is used to determine if a Qt GUI application # can be created without an error. # # It is used e.g. for diagnostics output of backintime # or to check if a system tray icon could be shown... # # It is called by "tools.is_Qt_working()" normally # but you can also execute it manually via # python3 qt_probing.py # It works by trying to create a QApplication instance # Any error indicates that Qt is not available or not correctly configured. # WORK AROUND: # # The C++ code of Qt ends abruptly with a SIGABRT signal (qFatal macro) # if a QApplication cannot be instantiated. # This causes a coredump creation by the python default signal handler # and the signal handler cannot be disabled since it reacts to a # non-python low-level signal sent via C/C++. # # Even though the coredump message cannot be prevented there is # workaround to prevent the coredump **file** creation which # would take too much time just to probe Qt's availability: # # Use resource.setrlimit() to set resource.RLIMIT_CORE’s soft limit to 0 # # Note: This does NOT prevent the console output "Aborted (core dumped)" # even though no coredump file will be created! # You can check that no coredump file was created with the command # sudo coredumpctl list -r # # More details: # # To suppress the creation of coredump file on Linux # use resource.setrlimit() to set resource.RLIMIT_CORE’s soft limit to 0 # to prevent coredump file creation. # https://docs.python.org/3.10/library/resource.html#resource.RLIMIT_CORE # https://docs.python.org/3.10/library/resource.html#resource.setrlimit # See also the source code of the test.support.SuppressCrashReport() context manager: # if self.resource is not None: # try: # self.old_value = self.resource.getrlimit(self.resource.RLIMIT_CORE) # self.resource.setrlimit(self.resource.RLIMIT_CORE, # (0, self.old_value[1])) # except (ValueError, OSError): # pass # https://github.com/python/cpython/blob/32718f908cc92c474fd968912368b8a4500bd055/Lib/test/support/__init__.py#L1712-L1718 # and cpython "faulthandler_suppress_crash_report()" # https://github.com/python/cpython/blob/32718f908cc92c474fd968912368b8a4500bd055/Modules/faulthandler.c#L954 # See "man 2 getrlimit" for more details: # > RLIMIT_CORE # > This is the maximum size of a core file (see core(5)) in bytes # > that the process may dump. When 0 no core dump files are created # > When nonzero, larger dumps are truncated to this size. # > ... # > The soft limit is the value that the kernel enforces for the corresponding resource. # > The hard limit acts as a ceiling for the soft limit: # > an unprivileged process may set only its soft limit to a value # > in the range from 0 up to the hard limit, and (irreversibly) lower its # > hard limit. A privileged process (under Linux: one with the # > CAP_SYS_RESOURCE capability in the initial user namespace) may make # > arbitrary changes to either limit value. # # Note: The context manager test.support.SuppressCrashReport() is NOT used # here since the "test.support" module not public and its API is subject # to change without backwards compatibility concerns between releases. # Work-around to prevent the time-consuming creation of a core dump old_limits = resource.getrlimit(resource.RLIMIT_CORE) resource.setrlimit(resource.RLIMIT_CORE, (0, old_limits[1])) exit_code = 0 try: if "--debug" in sys.argv: # HACK: Minimal arg parsing to enable debug-level logging logger.DEBUG = True logger.debug(f"{__file__} started... Call args: {str(sys.argv)}") logger.debug(f"Display system: {os.environ.get('XDG_SESSION_TYPE', '($XDG_SESSION_TYPE is not set)')}") logger.debug(f"XDG_RUNTIME_DIR={os.environ.get('XDG_RUNTIME_DIR', '($XDG_RUNTIME_DIR is not set)')}") logger.debug(f"XAUTHORITY={os.environ.get('XAUTHORITY', '($XAUTHORITY is not set)')}") logger.debug(f"QT_QPA_PLATFORM={os.environ.get('QT_QPA_PLATFORM', '($QT_QPA_PLATFORM is not set)')}") logger.debug(f"Current euid: {os.geteuid()}") # Jan 25, 2024 Not enabled but just documented here since this "fix" is a hack (assumes hard-coded UID 1000 to be always correct). But it works in 99 % of installations # if isRoot(): # logger.debug("Changing euid from root to user as work-around for #1592 (qt_probing hangs in root cron job)") # # Fix inspired by # # https://stackoverflow.com/questions/71425861/connecting-to-user-dbus-as-root # os.seteuid(1000) # logger.debug(f"New euid: {os.geteuid()}") from PyQt6 import QtCore from PyQt6.QtWidgets import QApplication app = QApplication(['']) exit_code = 1 # 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 from PyQt6.QtWidgets import QSystemTrayIcon is_sys_tray_available = QSystemTrayIcon.isSystemTrayAvailable() if is_sys_tray_available: exit_code = 2 logger.debug(f"isSystemTrayAvailable for Qt: {is_sys_tray_available}") except Exception as e: logger.debug(f"Error: {repr(e)}") logger.debug(f"{__file__} is terminating normally (exit code: {exit_code})") # Exit codes: # 0 = no Qt GUI available # 1 = only Qt GUI available (no sys tray support) # 2 = Qt GUI and sys tray available # 134 (-6 as signed byte exit code type!) = SIGABRT caught by python # ("interrupted by signal 6: SIGABRT"). # This is most probably caused by a misconfigured Qt... # So the interpretation is the same as exit code 0. sys.exit(exit_code) backintime-1.5.2/common/schedule.py000066400000000000000000000122141465446530500172770ustar00rootroot00000000000000# SPDX-FileCopyrightText: © 2024 Christian BUHTZ # 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 # # This file is part of the program "Back In time" which is released under GNU # General Public License v2 (GPLv2). # See file LICENSE or go to . """Basic or low-level routines regarding scheduling. Basic functions for handling Cron, Crontab, and other scheduling-related features. """ import subprocess import logger _MARKER = '#Back In Time system entry, this will be edited by the gui:' """The string is used in crontab file to mark entries as owned by Back In Time. **WARNING**: Don't modify that string in code because it is used as match target while parsing the crontab file. See :func:`remove_bit_from_crontab()` for details. """ def read_crontab(): """Read current users crontab. On errors an empty list is returned. Returns: list: Crontab lines. Dev notes (buhtz, 2024-05): Might should raise exception on errors. """ try: proc = subprocess.run( ['crontab', '-l'], check=True, capture_output=True, text=True) except FileNotFoundError: logger.error('Command "crontab" not found.') return [] except subprocess.CalledProcessError as err: logger.error('Failed to get crontab lines. Return code ' f'of {err.cmd} was {err.returncode}.') return [] content = proc.stdout.split('\n') # Remove empty lines from the end try: while content[-1] == '': content = content[:-1] except IndexError: pass # Fixes issue #1181 (line count of empty crontab was 1 instead of 0) if content == ['']: content = [] return content def write_crontab(lines): """Write users crontab. This will overwrite the whole users crontab. So to keep the old crontab and only add new entries you need to read it first with :py:func:`tools.readCrontab`, append new entries to the list and write it back. Args: lines (list, tuple): Lines that should be written to crontab. Returns: bool: ``True`` if successful otherwise ``False``. """ content = '\n'.join(lines) # Crontab needs to end with a newline if not content.endswith('\n'): content += '\n' # Pipe the content (via echo over stdout) to crontab's stdin with subprocess.Popen(['echo', content], stdout=subprocess.PIPE) as echo: try: subprocess.run( ['crontab', '-'], stdin=echo.stdout, check=True, capture_output=True, text=True ) except FileNotFoundError as err: logger.error(f'Command "crontab" not found. Error was: {err}') return False except subprocess.CalledProcessError as err: logger.error('Failed to write crontab lines. Return code ' f'was {err.returncode}. Error was:\n{err.stderr}') return False return True def remove_bit_from_crontab(crontab): """Remove crontab entries related to backintime and having a marker line in the line before. Args: lines(list): List of crontab liens. """ # Indices of lines containing the marker marker_indexes = list(filter( lambda idx: _MARKER in crontab[idx], range(len(crontab)) )) # Check if there is a valid BIT entry after the marker lines for idx in marker_indexes[:]: try: if 'backintime' in crontab[idx+1]: continue except IndexError: pass # Remove the current index because the following line is not valid marker_indexes.remove(marker_indexes.index(idx)) modified_crontab = crontab[:] # Remove the marker comment line and the following backintime line for idx in reversed(marker_indexes): del modified_crontab[idx:idx+2] return modified_crontab def append_bit_to_crontab(crontab, bit_lines): """Add new entries to existing crontab content. Args: crontab(list): A list of strings as crontab lines. bit_lines(list): A list of strings as new crontab lines. Returns: list: The new crontab lines. """ for line in bit_lines: crontab.append(_MARKER) crontab.append(line) return crontab def is_cron_running(): """Validate if a cron instance is running. The output of ``ps`` is searched (case-insensitive) via ``grep`` for the string ``cron``. Returns: bool: The answer. """ with subprocess.Popen(['ps', '-eo', 'comm'], stdout=subprocess.PIPE) as ps: try: grep = subprocess.run( ['grep', '--ignore-case', 'cron'], stdin=ps.stdout, stdout=subprocess.PIPE, check=True ) except subprocess.CalledProcessError: return False return True backintime-1.5.2/common/snapshotlog.py000066400000000000000000000352171465446530500200540ustar00rootroot00000000000000# Back In Time # Copyright (C) 2016-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. # # 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 re import logger import snapshots import tools class LogFilter(object): """ A Filter for snapshot logs which will both decode log lines and filter them for the requested ``mode``. Args: mode (int): which filter should be used. Possible values: :py:data:`NO_FILTER`, :py:data:`ERROR`, :py:data:`CHANGES`, :py:data:`INFORMATION` or :py:data:`ERROR_AND_CHANGES` decode (encfstools.Decode): instance used for decoding lines or ``None`` """ # TODO Better use an enumeration NO_FILTER = 0 ERROR = 1 CHANGES = 2 INFORMATION = 3 ERROR_AND_CHANGES = 4 RSYNC_TRANSFER_FAILURES = 5 # Regular expressions used for filtering log file lines. # RegExp syntax see: https://docs.python.org/3.10/library/re.html#regular-expression-syntax # (?:...) = the matched substring cannot be retrieved in a group (non-capturing) REGEX = {None: None, NO_FILTER: None, ERROR: re.compile(r'^(?:\[E\]|[^\[])'), CHANGES: re.compile(r'^(?:\[C\]|[^\[])'), INFORMATION: re.compile(r'^(?:\[I\]|[^\[])'), ERROR_AND_CHANGES: re.compile(r'^(?:\[E\]|\[C\]|[^\[])'), RSYNC_TRANSFER_FAILURES: re.compile( # All links to rsync's source reference the commit 2f9b963 from Jun 27, 2023 (most-recent commit on "master" as at Jan 28, 2024) r'.*(?:' r'Invalid cross-device link' # not directly contained in rsync's source code but may be caught and passed through as-is r'|symlink has no referent' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1281 r'|readlink_stat\(.?\) failed' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1294 r'|link_stat .* failed' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1810 r'|receive_sums failed' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/sender.c#L347 r'|send_files failed to open' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/sender.c#L361 r'|fstat failed' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/sender.c#L373 r'|read errors mapping' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/sender.c#L435 r'|change_dir .* failed' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/main.c#L749 # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/main.c#L807 # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/main.c#L827 # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/main.c#L1161 r'|skipping overly long name' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1247 r'|skipping file with bogus \(zero\) st_mode' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1300 r'|skipping symlink with 0-length value' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1569 r'|cannot convert filename' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L748 # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1599 r'|cannot convert symlink data for' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1144 # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1613 r'|opendir .* failed' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1842 r'|filename overflows max-path len by' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1868 r'|cannot send file with empty name in' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1876 r'|readdir\(.*\)' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L1888 r'|cannot add local filter rules in long-named directory' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/exclude.c#L817 r'|failed to re-read xattr' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/xattrs.c#L662 r'|Skipping sender remove of destination file' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/sender.c#L152 r'|Skipping sender remove for changed file' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/sender.c#L161 r'|could not make way for' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/delete.c#L220 r'|system says the ACL I packed is invalid' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/acls.c#L435 r'|recv_acl_access: value out of range' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/acls.c#L689 r'|recv_acl_index: .* ACL index' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/acls.c#L739 r'|Create time value of .* truncated on receiver' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L858 r'|FATAL I/O ERROR: dying to avoid a \-\-delete' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/flist.c#L2005 r'|IO error encountered' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/generator.c#L295 r'|some files/attrs were not transferred' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/log.c#L97 r'|temporary filename too long' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/receiver.c#L138 r'|No batched update for' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/receiver.c#L456 r'|recv_files: .* is a directory' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/receiver.c#L805 r'|no ftruncate for over-long pre-alloc' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/util1.c#L438 r'|daemon refused to receive' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/generator.c#L1270 r'|get_xattr_data: lgetxattr' # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/xattrs.c#L199 # https://github.com/WayneD/rsync/blob/2f9b963abaa52e44891180fe6c0d1c2219f6686d/xattrs.c#L215 # r').*' # no need to match the remainder of the line r')' )} def __init__(self, mode = 0, decode = None): self.regex = self.REGEX[mode] self.decode = decode if decode: self.header = ( '### This log has been decoded with automatic search pattern\n' '### If some paths are not decoded you can manually decode ' 'them with:\n' '### \'backintime --quiet ' ) if int(decode.config.currentProfile()) > 1: self.header += '--profile "%s" ' % decode.config.profileName() self.header += '--decode \'\n\n' else: self.header = '' def filter(self, line): """ Filter and decode ``line`` with predefined ``mode`` and ``decode`` instance. Args: line (str): log line read from disk Returns: str: decoded ``line`` or ``None`` if the line was filtered """ if not line: #keep empty lines return line if self.regex and not self.regex.match(line): return if self.decode: return self.decode.log(line) else: return line class SnapshotLog(object): """ Read and write Snapshot log to "~/.local/share/backintime/takesnapshot_.log". Where is the profile ID ``profile``. Args: cfg (config.Config): current config profile (int): profile that should be used to identify the log """ NONE = 0 ERRORS = 1 CHANGES_AND_ERRORS = 2 ALL = 3 def __init__(self, cfg, profile = None): self.config = cfg if profile: self.profile = profile else: self.profile = cfg.currentProfile() self.logLevel = cfg.logLevel() self.logFileName = cfg.takeSnapshotLogFile(self.profile) self.logFile = None self.timer = tools.Alarm(self.flush, overwrite = False) def __del__(self): if self.logFile: self.logFile.close() def get(self, mode = None, decode = None, skipLines = 0): """ Read the log, filter and decode it and yield its lines. Args: mode (int): Mode used for filtering. Take a look at :py:class:`snapshotlog.LogFilter` decode (encfstools.Decode): instance used for decoding lines or ``None`` skipLines (int): skip ``n`` lines before yielding lines. This is used to append only new lines to LogView Yields: str: filtered and decoded log lines """ logFilter = LogFilter(mode, decode) count = logFilter.header.count('\n') try: with open(self.logFileName, 'rt') as f: if logFilter.header and not skipLines: yield logFilter.header for line in f.readlines(): line = logFilter.filter(line.rstrip('\n')) if not line is None: count += 1 if count <= skipLines: continue yield line except Exception as e: msg = ('Failed to get take_snapshot log from {}:'.format(self.logFile), str(e)) logger.debug(' '.join(msg), self) for line in msg: yield line def new(self, date): """ Create a new log file or - if the last new_snapshot can be continued - add a note to the current log. Args: date (datetime.datetime): current date """ if snapshots.NewSnapshot(self.config).saveToContinue: msg = "Last snapshot didn't finish but can be continued.\n\n" msg += "======== continue snapshot (profile %s): %s ========\n" else: if os.path.exists(self.logFileName): if self.logFile: self.logFile.close() self.logFile = None os.remove(self.logFileName) msg = "========== Take snapshot (profile %s): %s ==========\n" self.append(msg %(self.profile, date.strftime('%c')), 1) def append(self, msg, level): """ Append ``msg`` to the log if ``level`` is lower than configured log level. Args: msg (str): message line that should be added to the log level (int): verbosity level of current line. msg will only be added to log if level is lower than configured log level :py:func:`config.Config.logLevel`. Possible Values: :py:data:`SnapshotLog.ERRORS`, :py:data:`SnapshotLog.CHANGES_AND_ERRORS` or :py:data:`SnapshotLog.ALL` """ if level > self.logLevel: return if not self.logFile: self.logFile = open(self.logFileName, 'at') self.logFile.write(msg + '\n') self.timer.start(5) # flush the log output buffer after 5 seconds def flush(self): """ Write the in-memory buffer of the log output into the log file. """ if self.logFile: try: # TODO flush() does not necessarily write the file’s data to disk. # Use flush() followed by os.fsync() to ensure this behavior. # https://docs.python.org/2/library/stdtypes.html#file.flush self.logFile.flush() except RuntimeError as e: # Fixes #1003 (RTE reentrant call inside io.BufferedWriter) # This RTE will not be logged since this would be another reentrant call pass backintime-1.5.2/common/snapshots.py000066400000000000000000003346061465446530500175410ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 json import os from pathlib import Path import stat import datetime import gettext import bz2 import pwd import getpass import grp import subprocess import shutil import time import re from tempfile import TemporaryDirectory import config import configfile import logger import tools import encfstools import mount import progress import snapshotlog import flock from applicationinstance import ApplicationInstance from exceptions import MountException, LastSnapshotSymlink class Snapshots: """ Collection of take-snapshot and restore commands. BUHTZ 2022-10-09: In my understanding this is the representation of a snapshot in the "application layer". This seems to be the difference to the class `SID` which represents a snapshot in the "data layer". BUHTZ 2024-02-23: Not sure but it seems to be one concrete snapshot and not a collection of snapshots. In this case the class name is misleading because it is in plural form. Args: cfg (config.Config): current config """ SNAPSHOT_VERSION = 3 def __init__(self, cfg = None): self.config = cfg if self.config is None: self.config = config.Config() self.snapshotLog = snapshotlog.SnapshotLog(self.config) self.clearIdCache() self.clearNameCache() #rsync --info=progress2 output #search for: 517.38K 26% 14.46MB/s 0:02:36 #or: 497.84M 4% -449.39kB/s ??:??:?? #but filter out: 517.38K 26% 14.46MB/s 0:00:53 (xfr#53, to-chk=169/452) # because this shows current run time self.reRsyncProgress = re.compile(r'.*?' #trash at start r'(\d*[,\.]?\d+[KkMGT]?)\s+' #bytes sent r'(\d*)%\s+' #percent done r'(-?\d*[,\.]?\d*[KkMGT]?B/s)\s+' #speed r'([\d\?]+:[\d\?]{2}:[\d\?]{2})' #estimated time of arrival r'(.*$)') #trash at the end self.lastBusyCheck = datetime.datetime(1, 1, 1) self.restorePermissionFailed = False # TODO: make own class for takeSnapshotMessage def clearTakeSnapshotMessage(self): """Delete message and progress file""" Path(self.config.takeSnapshotMessageFile()).unlink(missing_ok=True) Path(self.config.takeSnapshotProgressFile()).unlink(missing_ok=True) # TODO: make own class for takeSnapshotMessage def takeSnapshotMessage(self): """Get the current message from the message file. Returns: (tuple(int, str)): The message type and its string or `None`. See `setTakeSnapshotMessage()` for further details. Dev note (buhtz): Too many try..excepts in here. """ # Dev note (buhtz): Not sure what happens here or why this is useful. wait = datetime.datetime.now() - datetime.timedelta(seconds=5) if self.lastBusyCheck < wait: self.lastBusyCheck = datetime.datetime.now() if not self.busy(): self.clearTakeSnapshotMessage() return None # Filename of the message file message_fn = self.config.takeSnapshotMessageFile() if not os.path.exists(message_fn): return None try: with open(message_fn, 'rt') as handle: items = handle.read().split('\n') # TODO (buhtz): Too broad exception except Exception as exc: logger.debug('Failed to get takeSnapshot message from ' f'{message_fn}: {str(exc)}', self) return None if len(items) < 2: return None # "Message id": Type of the message. mid = 0 try: mid = int(items[0]) # TODO (buhtz): Too broad exception except Exception as exc: logger.debug('Failed to extract message ID from ' f'{items}: {str(exc)}', self) return (mid, '\n'.join(items[1:])) # TODO: make own class for takeSnapshotMessage def setTakeSnapshotMessage(self, type_id, message, timeout=-1): """Update the status message of the active snapshot creation job Write the status message into a message file to allow async processing for the GUI, plug-ins (like user-callback) and desktop notifications. Args: type_id: Simplified severity level of the status message: 0: INFO 1: ERROR other values: defaults to INFO (may change in the future) message: status message string timeout: Requested maximum processing duration in plug-ins. Default: -1 (no limit) Called plug-ins must try to keep the timeout itself (it is not enforced by the ``PluginManager``. In fact currently all known plug-ins do ignore the timeout value! """ message_fn = self.config.takeSnapshotMessageFile() try: # Write message to file (and overwrites the previous one) with open(message_fn, 'wt') as f: f.write(str(type_id) + '\n' + message) except Exception as exc: logger.debug('Failed to set takeSnapshot message ' f'to {message_fn}: {str(exc)}', self) # Error message? if type_id == 1: self.snapshotLog.append('[E] ' + message, 1) else: self.snapshotLog.append('[I] ' + message, 3) try: profile_id = self.config.currentProfile() profile_name = self.config.profileName(profile_id) self.config.PLUGIN_MANAGER.message( profile_id, profile_name, type_id, message, timeout) except Exception as exc: logger.debug(f'Failed to send message to plugins: {str(exc)}', self) def busy(self): instance = ApplicationInstance(self.config.takeSnapshotInstanceFile(), False) return instance.busy() def pid(self): instance = ApplicationInstance(self.config.takeSnapshotInstanceFile(), False) return instance.readPidFile()[0] def clearNameCache(self): """ Reset the cache for user and group names. """ self.userCache = {} self.groupCache = {} def clearIdCache(self): """ Reset the cache for UIDs and GIDs. """ self.uidCache = {} self.gidCache = {} def uid(self, name, callback = None, backup = None): """ Get the User identifier (UID) for the user in ``name``. name->uid will be cached to speed up subsequent requests. Args: name (:py:class:`str`, :py:class:`bytes`): username to search for callback (method): callable which will handle a given message backup (int): UID which will be used if the username is unknown on this machine Returns: int: UID of the user in name or -1 if not found """ if isinstance(name, bytes): name = name.decode() if name in self.uidCache: return self.uidCache[name] else: uid = -1 try: uid = pwd.getpwnam(name).pw_uid except Exception as e: if backup: uid = backup msg = "UID for '%s' is not available on this system. Using UID %s from snapshot." %(name, backup) logger.info(msg, self) if callback is not None: callback(msg) else: self.restorePermissionFailed = True msg = 'Failed to get UID for %s: %s' %(name, str(e)) logger.error(msg, self) if callback: callback(msg) self.uidCache[name] = uid return uid def gid(self, name, callback = None, backup = None): """ Get the Group identifier (GID) for the group in ``name``. name->gid will be cached to speed up subsequent requests. Args: name (:py:class:`str`, :py:class:`bytes`): groupname to search for callback (method): callable which will handle a given message backup (int): GID which will be used if the groupname is unknown on this machine Returns: int: GID of the group in name or -1 if not found """ if isinstance(name, bytes): name = name.decode() if name in self.gidCache: return self.gidCache[name] else: gid = -1 try: gid = grp.getgrnam(name).gr_gid except Exception as e: if backup is not None: gid = backup msg = "GID for '%s' is not available on this system. Using GID %s from snapshot." %(name, backup) logger.info(msg, self) if callback: callback(msg) else: self.restorePermissionFailed = True msg = 'Failed to get GID for %s: %s' %(name, str(e)) logger.error(msg, self) if callback: callback(msg) self.gidCache[name] = gid return gid def userName(self, uid): """ Get the username for the given uid. uid->name will be cached to speed up subsequent requests. Args: uid (int): User identifier (UID) to search for Returns: str: name of the user with UID uid or '-' if not found """ if uid in self.userCache: return self.userCache[uid] else: name = '-' try: name = pwd.getpwuid(uid).pw_name except Exception as e: logger.debug('Failed to get user name for UID %s: %s' %(uid, str(e)), self) self.userCache[uid] = name return name def groupName(self, gid): """ Get the groupname for the given gid. gid->name will be cached to speed up subsequent requests. Args: gid (int): Group identifier (GID) to search for Returns: str: name of the Group with GID gid or '.' if not found """ if gid in self.groupCache: return self.groupCache[gid] else: name = '-' try: name = grp.getgrgid(gid).gr_name except Exception as e: logger.debug('Failed to get group name for GID %s: %s' %(gid, str(e)), self) self.groupCache[gid] = name return name def restoreCallback(self, callback, ok, msg): """ Format messages thrown by restore depending on whether they where successful or failed. Args: callback (method): callable instance which will handle the message ok (bool): ``True`` if the logged action was successful or ``False`` if it failed msg (str): message that should be send to callback """ if not callback is None: if not ok: msg = msg + ' : ' + _('FAILED') self.restorePermissionFailed = True callback(msg) def restorePermission(self, key_path, path, fileInfoDict, callback = None): """ Restore permissions (owner, group and mode). If permissions are already identical with the new ones just skip. Otherwise try to 'chown' to new owner and new group. If that fails (most probably because we are not running as root and normal user has no rights to change ownership of files) try to at least 'chgrp' to the new group. Finally 'chmod' the new mode. Args: key_path (bytes): original path during backup. Same as in fileInfoDict. path (bytes): current path of file that should be changed. fileInfoDict (FileInfoDict): FileInfoDict """ assert isinstance(key_path, bytes), 'key_path is not bytes type: %s' % key_path assert isinstance(path, bytes), 'path is not bytes type: %s' % path assert isinstance(fileInfoDict, FileInfoDict), 'fileInfoDict is not FileInfoDict type: %s' % fileInfoDict if key_path not in fileInfoDict or not os.path.exists(path): return info = fileInfoDict[key_path] #restore uid/gid uid = self.uid(info[1], callback) gid = self.gid(info[2], callback) #current file stats st = os.stat(path) # logger.debug('%(path)s: uid %(target_uid)s/%(cur_uid)s, gid %(target_gid)s/%(cur_gid)s, mod %(target_mod)s/%(cur_mod)s' # %{'path': path.decode(), # 'target_uid': uid, # 'cur_uid': st.st_uid, # 'target_gid': gid, # 'cur_gid': st.st_gid, # 'target_mod': info[0], # 'cur_mod': st.st_mode # }) if uid != -1 or gid != -1: ok = False if uid != st.st_uid: try: os.chown(path, uid, gid) ok = True except: pass self.restoreCallback(callback, ok, "chown %s %s : %s" % (path.decode(errors = 'ignore'), uid, gid)) st = os.stat(path) #if restore uid/gid failed try to restore at least gid if not ok and gid != st.st_gid: try: os.chown(path, -1, gid) ok = True except: pass self.restoreCallback(callback, ok, "chgrp %s %s" % (path.decode(errors = 'ignore'), gid)) st = os.stat(path) #restore perms ok = False if info[0] != st.st_mode: try: os.chmod(path, info[0]) ok = True except: pass self.restoreCallback(callback, ok, "chmod %s %04o" % (path.decode(errors = 'ignore'), info[0])) def restore(self, sid, paths, callback = None, restore_to = '', delete = False, backup = True, only_new = False): """ Restore one or more files from snapshot ``sid`` to either original or a different destination. Restore is done with rsync. If available permissions will be restored from ``fileinfo.bz2``. Args: sid (SID): snapshot from whom to restore paths (:py:class:`list`, :py:class:`tuple` or :py:class:`str`): single path (str) or multiple paths (list, tuple) that should be restored. For every path this will run a new rsync process. Permissions will be restored for all paths in one run callback (method): callable instance which will handle messages restore_to (str): full path to restore to. If empty restore to original destination delete (bool): delete newer files which are not in the snapshot backup (bool): create backup files (``*.backup.YYYYMMDD``) before changing or deleting local files. only_new (bool): Only restore files which do not exist or are newer than those in destination. Using ``rsync --update`` option. """ instance = ApplicationInstance( pidFile=self.config.restoreInstanceFile(), autoExit=False, flock=True) if instance.check(): instance.startApplication() else: logger.warning('Restore is already running', self) return if restore_to.endswith('/'): restore_to = restore_to[: -1] if not isinstance(paths, (list, tuple)): paths = (paths,) logger.info("Restore: %s to: %s" %(', '.join(paths), restore_to), self) info = sid.info cmd_prefix = tools.rsyncPrefix(self.config, no_perms=False, use_mode=['ssh']) cmd_prefix.extend(('-R', '-v')) if backup: cmd_prefix.extend(('--backup', '--suffix=%s' % self.backupSuffix())) if delete: cmd_prefix.append('--delete') cmd_prefix.append('--filter=protect %s' % self.config.snapshotsPath()) cmd_prefix.append('--filter=protect %s' % self.config._LOCAL_DATA_FOLDER) cmd_prefix.append('--filter=protect %s' % self.config._MOUNT_ROOT) if only_new: cmd_prefix.append('--update') restored_paths = [] for path in paths: tools.makeDirs(os.path.dirname(path)) src_path = path src_delta = 0 src_base = sid.pathBackup(use_mode = ['ssh']) if not src_base.endswith(os.sep): src_base += os.sep cmd = cmd_prefix[:] if restore_to: items = os.path.split(src_path) aux = items[0].lstrip(os.sep) # bugfix: restore system root ended in //. if aux: src_base = os.path.join(src_base, aux) + '/' src_path = '/' + items[1] if items[0] == '/': src_delta = 0 else: src_delta = len(items[0]) cmd.append(self.rsyncRemotePath('%s.%s' % (src_base, src_path), use_mode=['ssh'], quote='')) cmd.append('%s/' % restore_to) proc = tools.Execute(cmd, callback=callback, filters=(self.filterRsyncProgress,), parent=self) self.restoreCallback(callback, True, proc.printable_cmd) proc.run() self.restoreCallback(callback, True, ' ') restored_paths.append((path, src_delta)) try: os.remove(self.config.takeSnapshotProgressFile()) except Exception as e: logger.debug('Failed to remove snapshot progress file %s: %s' %(self.config.takeSnapshotProgressFile(), str(e)), self) #restore permissions logger.info('Restore permissions', self) self.restoreCallback(callback, True, ' ') self.restoreCallback( callback, True, '{}:'.format(_('Restore permissions'))) self.restorePermissionFailed = False fileInfoDict = sid.fileInfo #cache uids/gids for uid, name in info.listValue('user', ('int:uid', 'str:name')): self.uid(name.encode(), callback = callback, backup = uid) for gid, name in info.listValue('group', ('int:gid', 'str:name')): self.gid(name.encode(), callback = callback, backup = gid) if fileInfoDict: all_dirs = [] #restore dir permissions after all files are done for path, src_delta in restored_paths: #explore items snapshot_path_to = sid.pathBackup(path).rstrip('/') root_snapshot_path_to = sid.pathBackup().rstrip('/') #use bytes instead of string from here if isinstance(path, str): path = path.encode() if isinstance(restore_to, str): restore_to = restore_to.encode() if not restore_to: path_items = path.strip(b'/').split(b'/') curr_path = b'/' for path_item in path_items: curr_path = os.path.join(curr_path, path_item) if curr_path not in all_dirs: all_dirs.append(curr_path) else: if path not in all_dirs: all_dirs.append(path) if os.path.isdir(snapshot_path_to) and not os.path.islink(snapshot_path_to): head = len(root_snapshot_path_to.encode()) for explore_path, dirs, files in os.walk(snapshot_path_to.encode()): for item in dirs: item_path = os.path.join(explore_path, item)[head:] if item_path not in all_dirs: all_dirs.append(item_path) for item in files: item_path = os.path.join(explore_path, item)[head:] real_path = restore_to + item_path[src_delta:] self.restorePermission(item_path, real_path, fileInfoDict, callback) all_dirs.reverse() for item_path in all_dirs: real_path = restore_to + item_path[src_delta:] self.restorePermission(item_path, real_path, fileInfoDict, callback) self.restoreCallback(callback, True, '') if self.restorePermissionFailed: status = _('FAILED') else: status = _('Done') self.restoreCallback( callback, True, '{}: {}'.format(_('Restore permissions'), status) ) instance.exitApplication() def backupSuffix(self): """ Get suffix for backup files. Returns: str: backup suffix in form of '.backup.YYYYMMDD' """ return '.backup.' + datetime.date.today().strftime('%Y%m%d') def remove(self, sid): """ Remove snapshot ``sid``. BUHTZ 2022-10-11: From my understanding rsync is used here to sync the directory of a concrete snapshot (``sid``) against an empty temporary directory. In the consequence the sid directory is empty but not deleted. To delete that directory simple `rm` call (via `shutil` package) is used to delete the directory. No need to do this via SSH because the directory is temporary mounted. It is not clear for me why it is done that way. Why not simply "rm" the directory when it is mounted instead of using rsync in a previous step?! But I won't change it yet. Args: sid (SID): snapshot to remove Returns: (bool): ``True`` if succeeded otherwise ``False``. """ if isinstance(sid, RootSnapshot): return # build the rsync command and it's arguments rsync = tools.rsyncRemove(self.config) # an empty temporary directory # e.g. /tmp/tmp8g59onuz with TemporaryDirectory() as d: # the temp dir rsync.append(d + os.sep) # the real remote path of a concrete snapshot (a "sid") # e.g. user@myserver:"/MyBackup/.backintime/backintime/HOST/user/ \ # MyProfile/20221005-000003-880" rsync.append( self.rsyncRemotePath( sid.path(use_mode=['ssh', 'ssh_encfs']), # No quoting because of new argument protection of rsync. quote='' ) ) # Syncing the empty tmp directory against the sid directory # will clear the sid directory. rc = tools.Execute(rsync).run() # if rc != 0: logger.error( f'Last rsync command failed with return code "{rc}". ' 'See previous WARNING message in the logs for details.') return False # Delete the sid dir. BUT here isn't the remote path used but the # temporary mounted variant of it. # e.g. /home/user/.local/share/backintime/mnt/4_8030/backintime/ \ # HOST/user/MyProfile/20221005-000003-880 shutil.rmtree(sid.path()) return True # TODO Refactor: This functions is extremely difficult to understand: # - Nested "if"s # - Fuzzy names of classes, attributes and methods # - unclear variable names (at least for the return values) def backup(self, force=False): """Wrapper for :py:func:`takeSnapshot` which will prepare and clean up things for the main :py:func:`takeSnapshot` method. This will check that no other snapshots are running at the same time, there is nothing prohibiting a new snapshot (e.g. on battery) and the profile is configured correctly. This will also mount and unmount remote destinations. Args: force (bool): Force taking a new snapshot even if the profile is not scheduled or the machine is running on battery. Returns: bool: ``True`` if there was an error. """ ret_val, ret_error = False, True sleep = True self.config.PLUGIN_MANAGER.load(self) if not self.config.isConfigured(): logger.warning('Not configured', self) # not configured self.config.PLUGIN_MANAGER.error(1) elif (not force and self.config.noSnapshotOnBattery() and tools.onBattery()): self.setTakeSnapshotMessage( 0, _('Deferring backup while on battery')) logger.info('Deferring backup while on battery', self) logger.warning('Backup not performed', self) ret_error = False elif not force and not self.config.backupScheduled(): logger.info(f'Profile "{self.config.profileName()}" is not ' 'scheduled to run now.', self) ret_error = False else: instance = ApplicationInstance( self.config.takeSnapshotInstanceFile(), False, flock=True ) restore_instance = ApplicationInstance( self.config.restoreInstanceFile(), False ) if not instance.check(): logger.warning( 'A backup is already running. The pid of the already ' f'running backup is in file {instance.pidFile}. Maybe ' 'delete it.', self) # a backup is already running self.config.PLUGIN_MANAGER.error(2) elif not restore_instance.check(): logger.warning( 'Restore is still running. Stop backup until restore is ' 'done. The pid of the already running restore is in ' f'{restore_instance.pidFile}. Maybe delete it.', self) else: if (self.config.noSnapshotOnBattery() and not tools.powerStatusAvailable()): logger.warning('Backups disabled on battery but power ' 'status is not available', self) instance.startApplication() # Global flock to block backups from other profiles or users # (and run them serialized). The argument "disabled" is a # workaround (#1751) that should be removed/refactored after # this method ("backup()") is refactored. with flock.GlobalFlock(disable=not self.config.globalFlock()): logger.info('Lock', self) now = datetime.datetime.today() # inhibit suspend/hibernate during snapshot is running self.config.inhibitCookie = tools.inhibitSuspend( toplevel_xid=self.config.xWindowId) # mount try: hash_id = mount.Mount(cfg=self.config).mount() except MountException as ex: logger.error(str(ex), self) instance.exitApplication() logger.info('Unlock', self) time.sleep(2) return True else: self.config.setCurrentHashId(hash_id) include_folders = self.config.include() if not include_folders: logger.info('Nothing to do', self) elif not self.config.PLUGIN_MANAGER.processBegin(): logger.info('A plugin prevented the backup', self) else: # take snapshot process begin self.setTakeSnapshotMessage(0, '…') self.snapshotLog.new(now) profile_id = self.config.currentProfile() profile_name = self.config.profileName() logger.info(f"Take a new snapshot. Profile: {profile_id} " f"{profile_name}", self) if not self.config.canBackup(profile_id): if (self.config.PLUGIN_MANAGER.hasGuiPlugins and self.config.notify()): message = ( _("Can't find snapshots folder.\n" "If it is on a removable drive please " "plug it in.") + '\n' + gettext.ngettext('Waiting %s second.', 'Waiting %s seconds.', 30) % 30 ) self.setTakeSnapshotMessage( type_id=1, message=message, timeout=30) counter = 0 for counter in range(0, 30): logger.debug( 'Cannot start snapshot yet: target ' 'directory not accessible. Waiting 1s.') time.sleep(1) if self.config.canBackup(): break if counter != 0: logger.info( f'Waited {counter} seconds for target ' 'directory to be available', self) if not self.config.canBackup(profile_id): logger.warning( "Can't find snapshots folder!", self) # Can't find snapshots directory (is it on a # removable drive ?) self.config.PLUGIN_MANAGER.error(3) else: ret_error = False sid = SID(now, self.config) if sid.exists(): logger.warning(f'Snapshot path "{sid.path()}" ' 'already exists', self) # This snapshot already exists self.config.PLUGIN_MANAGER.error(4, sid) else: try: # TODO # rename ret_val to new_snapshot_created # and ret_error to has_error for clearer # code ret_val, ret_error = self.takeSnapshot( sid, now, include_folders) except: # TODO too broad exception new = NewSnapshot(self.config) if new.exists(): new.saveToContinue = False new.failed = True raise if not ret_val: self.remove(sid) if ret_error: logger.error( 'Failed to take snapshot.', self) msg = _('Failed to take snapshot ' '{snapshot_id}.').format( snapshot_id=sid.displayID) self.setTakeSnapshotMessage(1, msg) # Fixes #1491 self.config.PLUGIN_MANAGER.error(5, msg) time.sleep(2) else: logger.warning("No new snapshot", self) else: # new snapshot taken... if ret_error: logger.error('New snapshot taken but ' 'errors detected', self) # Fixes #1491 self.config.PLUGIN_MANAGER.error( 6, sid.displayID) # Why ignore errors now? ret_error = False # Probably because a new snapshot has been # created (= changes transferred) and # "continue on errors" is enabled if not ret_error: self.freeSpace(now) self.setTakeSnapshotMessage(0, _('Finalizing')) time.sleep(2) sleep = False if ret_val: # new snapshot self.config.PLUGIN_MANAGER.newSnapshot( sid, sid.path()) # Take snapshot process end self.config.PLUGIN_MANAGER.processEnd() if sleep: time.sleep(2) sleep = False if not ret_error: self.clearTakeSnapshotMessage() # unmount try: mount.Mount(cfg=self.config) \ .umount(self.config.current_hash_id) except MountException as ex: logger.error(str(ex), self) instance.exitApplication() # self.flockRelease() logger.info('Unlock', self) # --- END GlobalFlock context --- if sleep: # max 1 backup / second time.sleep(2) # release inhibit suspend if self.config.inhibitCookie: self.config.inhibitCookie = tools.unInhibitSuspend( *self.config.inhibitCookie) return ret_error def filterRsyncProgress(self, line): """ Filter rsync's stdout for progress information and store them in '~/.local/share/backintime/worker.progress' file. Args: line (str): stdout line from rsync Returns: str: ``line`` if it had no progress infos. ``None`` if ``line`` was a progress """ ret = [] for l in line.split('\n'): m = self.reRsyncProgress.match(l) if m: # if m.group(5).strip(): # return pg = progress.ProgressFile(self.config) pg.setIntValue('status', pg.RSYNC) pg.setStrValue('sent', m.group(1)) pg.setIntValue('percent', int(m.group(2))) pg.setStrValue('speed', m.group(3)) #pg.setStrValue('eta', m.group(4)) pg.save() del pg else: ret.append(l) return '\n'.join(ret) def rsyncCallback(self, line, params): """ Parse rsync's stdout, send it to takeSnapshotMessage and takeSnapshotLog. Also check if there has been changes or errors in current rsync. Args: line (str): stdout line from rsync params (list): list of two bool '[error, changes]'. Uses a side effect by changing list items here to change the original list of the caller, too (lists are passed as reference in Python). If rsync reported an error ``params[0]`` will be set to ``True``. If rsync reported a changed file ``params[1]`` will be set to ``True`` """ if not line: return # Warning (2023-11): Do not modify the source string. # See #1559 for details. self.setTakeSnapshotMessage( 0, _('Take snapshot') + " (rsync: %s)" % line) # Did rsync report an error? if line.endswith(')'): if line.startswith('rsync:'): if not line.startswith('rsync: chgrp ') and not line.startswith('rsync: chown '): # matches rsync error lines like: # rsync: [generator] link [...] failed: Invalid cross-device link (18) params[0] = True self.setTakeSnapshotMessage(1, 'Error: ' + line) if len(line) >= 13: # The prefix is created by rsync via the argument "--out-format=BACKINTIME: %i %n%L" if line.startswith('BACKINTIME: '): if line[12] != '.' and line[12:14] != 'cd': params[1] = True self.snapshotLog.append('[C] ' + line[12:], 2) def makeDirs(self, path): """ Wrapper for :py:func:`tools.makeDirs()`. Create directories ``path`` recursive and return success. If not successful send error-message to log. Args: path (str): fullpath to directories that should be created Returns: bool: ``True`` if successful """ if not tools.makeDirs(path): logger.error(f"Can't create folder: {path}", self) self.setTakeSnapshotMessage( 1, '{}: {}'.format( _("Can't create folder"), path) ) time.sleep(2) # max 1 backup / second return False return True def backupConfig(self, sid): """ Backup the config file to the snapshot and to the backup root if backup is encrypted. Args: sid (SID): snapshot in which the config should be stored """ logger.info('Save config file', self) self.setTakeSnapshotMessage(0, _('Saving config file…')) with open(self.config._LOCAL_CONFIG_PATH, 'rb') as src: with open(sid.path('config'), 'wb') as dst1: dst1.write(src.read()) if self.config.snapshotsMode() == 'local_encfs': src.seek(0) dst2_path = os.path.join( self.config.localEncfsPath(), 'config' ) with open(dst2_path, 'wb') as dst2: dst2.write(src.read()) elif self.config.snapshotsMode() == 'ssh_encfs': cmd = tools.rsyncPrefix(self.config, no_perms=False) cmd.append(self.config._LOCAL_CONFIG_PATH) remote_path = self.rsyncRemotePath( self.config.sshSnapshotsPath(), # no quoting because of rsync modern argument # protection (argument -s) quote='' ) cmd.append(remote_path) proc = tools.Execute(cmd, parent=self) rc = proc.run() # WORKAROUND # tools.Execute only create warnings if 'cmd' fails. # But we need a real ERROR here. if rc != 0: logger.error( f'Backing up the config in "{self.config.snapshotsMode()}"' f' mode failed! The return code was {rc} and the' f' command was {cmd}. Also see the previous ' 'WARNING message for a more details.', parent=self) def _backup_info_file(self, sid): """ Save infos about the snapshot into the 'info' file. The result is stored in 'sid.info' also. Args: sid (SID): Snapshot that should get the info file. """ logger.debug( f'Create info file for snapshot "{sid.displayName}".', self) machine = self.config.host() user = getpass.getuser() profile_id = self.config.currentProfile() i = configfile.ConfigFile() i.setStrValue('snapshot_date', sid.withoutTag) i.setStrValue('snapshot_machine', machine) i.setStrValue('snapshot_user', user) i.setIntValue('snapshot_profile_id', profile_id) i.setIntValue('snapshot_tag', sid.tag) i.setListValue( 'user', ('int:uid', 'str:name'), list(self.userCache.items())) i.setListValue( 'group', ('int:gid', 'str:name'), list(self.groupCache.items())) sid.info = i def backupPermissions(self, sid): """ Save permissions (owner, group, read-, write- and executable) for all files in Snapshot ``sid`` into snapshots fileInfoDict. Args: sid (SID): snapshot that should be scanned Returns: int: Return code of rsync. """ logger.info('Save permissions', self) self.setTakeSnapshotMessage(0, _('Saving permissions…')) fileInfoDict = FileInfoDict() if self.config.snapshotsMode() == 'ssh_encfs': decode = encfstools.Decode(self.config, False) else: decode = encfstools.Bounce() # backup permissions of / # bugfix for https://github.com/bit-team/backintime/issues/708 self.backupPermissionsCallback(b'/', (fileInfoDict, decode)) rsync = ['rsync', '--dry-run', '-s', '-r', '--out-format=%n'] rsync.extend(tools.rsyncSshArgs(self.config)) rsync.append( self.rsyncRemotePath( path=sid.pathBackup( use_mode=['ssh', 'ssh_encfs'] ), quote='' ) + os.sep ) with TemporaryDirectory() as d: rsync.append(d + os.sep) proc = tools.Execute(rsync, callback=self.backupPermissionsCallback, user_data=(fileInfoDict, decode), parent=self, conv_str=False, join_stderr=False) rc = proc.run() sid.fileInfo = fileInfoDict return rc def backupPermissionsCallback(self, line, user_data): """ Rsync callback for :py:func:`Snapshots.backupPermissions`. Args: line(bytes): output from rsync command user_data (tuple): two item tuple of (:py:class:`FileInfoDict`, :py:class:`encfstools.Decode`) """ fileInfoDict, decode = user_data self.collectPermission(fileInfoDict, b'/' + decode.path(line).rstrip(b'/')) def collectPermission(self, fileinfo, path): """ Collect permission infos about ``path`` and store them into ``fileinfo``. Args: fileinfo (FileInfoDict): dict of: {path: (permission, user, group)} Using sideefect on changing dict item will change original dict, too. path (bytes): full path to file or folder """ assert isinstance(path, bytes), 'path is not bytes type: %s' % path if path and os.path.exists(path): info = os.stat(path) mode = info.st_mode user = self.userName(info.st_uid).encode('utf-8', 'replace') group = self.groupName(info.st_gid).encode('utf-8', 'replace') fileinfo[path] = (mode, user, group) def takeSnapshot(self, sid, now, include_folders): """This is the main backup routine. It will take a new snapshot and store permissions of included files and folders into ``fileinfo.bz2``. Args: sid (SID): snapshot ID which the new snapshot should get now (datetime.datetime): date and time when this snapshot was started include_folders (list): folders to include. list of tuples (item, int) where ``int`` is 0 if ``item`` is a folder or 1 if ``item`` is a file Returns: list: list of two bool (``ret_val``, ``ret_error``) where ``ret_val`` is ``True`` if a new snapshot has been created and ``ret_error`` is ``True`` if there was an error during taking the snapshot """ self.setTakeSnapshotMessage(0, '...') new_snapshot = NewSnapshot(self.config) encode = self.config.ENCODE # "return" values set during async rsync execution (as user data "by ref") params = [False, False] # [error, changes] # TODO # docstring of return value for this function swaps the meaning of the # elements, this is confusing (``ret_val``, ``ret_error``) and # error-prone. Use a mutable data structure with named elements # instead, e.g. a DataClass if new_snapshot.exists() and new_snapshot.saveToContinue: logger.info(f"Found leftover '{new_snapshot.displayID}' which " "can be continued.", self) self.setTakeSnapshotMessage( 0, _('Found leftover {snapshot_id} which can be continued.') .format(snapshot_id=new_snapshot.displayID) ) # fix permissions for file in os.listdir(new_snapshot.path()): file = os.path.join(new_snapshot.path(), file) mode = os.stat(file).st_mode try: os.chmod(file, mode | stat.S_IWUSR) except PermissionError: pass # search previous log for changes and set params params[1] = new_snapshot.hasChanges elif new_snapshot.exists() and not new_snapshot.saveToContinue: logger.info(f'Remove leftover {new_snapshot.displayID} folder ' 'from last run') self.setTakeSnapshotMessage( 0, _('Removing leftover {snapshot_id} folder from last run') .format(snapshot_id=new_snapshot.displayID) ) self.remove(new_snapshot) if os.path.exists(new_snapshot.path()): logger.error( f"Can't remove folder: {new_snapshot.path()}", self) self.setTakeSnapshotMessage( 1, '{}: {}'.format(_("Can't remove folder"), new_snapshot.path()) ) time.sleep(2) # max 1 backup / second return [False, True] if not new_snapshot.saveToContinue and not new_snapshot.makeDirs(): return [False, True] prev_sid = None snapshots = listSnapshots(self.config) if snapshots: prev_sid = snapshots[0] # rsync prefix & suffix rsync_prefix = tools.rsyncPrefix(self.config, no_perms=False) if self.config.excludeBySizeEnabled(): rsync_prefix.append('--max-size=%sM' % self.config.excludeBySize()) rsync_suffix = self.rsyncSuffix(include_folders) # When there is no snapshots it takes the last snapshot from the other folders # It should delete the excluded folders then rsync_prefix.extend(('--delete', '--delete-excluded')) rsync_prefix.append('-v') # Use a fixed logging format for the rsync "changed files" list to # make it parsable e.g. in rsyncCallback() # %i = itemized list (11 characters) of what is being updated # (see "--itemize-changes" in "man rsync") # %n = the filename (short form; trailing "/" on dir) # %L = the string " -> SYMLINK", " => HARDLINK", or "" # (where SYMLINK or HARDLINK is a filename) # (see log format section in "man rsyncd.conf") rsync_prefix.extend(('-i', '--out-format=BACKINTIME: %i %n%L')) if prev_sid: link_dest = encode.path(os.path.join(prev_sid.sid, 'backup')) link_dest = os.path.join(os.pardir, os.pardir, link_dest) rsync_prefix.append('--link-dest=%s' % link_dest) # sync changed folders logger.info("Call rsync to take the snapshot", self) new_snapshot.saveToContinue = True cmd = rsync_prefix + rsync_suffix # No quoting (quote='') because of new argument protection of rsync. cmd.append(self.rsyncRemotePath( new_snapshot.pathBackup(use_mode=['ssh', 'ssh_encfs']), quote='')) self.setTakeSnapshotMessage(0, _('Taking snapshot')) # run rsync proc = tools.Execute(cmd, # TODO # interprets the user_data in params as: list of # two bool [error, changes] but params is reused # as return value of this function with [changes, # error]. Use a separate variable to avoid # confusion! callback=self.rsyncCallback, user_data=params, filters=(self.filterRsyncProgress,), parent=self) # TODO # introduce centralized log msg builder to avoid spread severity level # indicators like "[I]" here? self.snapshotLog.append('[I] ' + proc.printable_cmd, 3) # TODO # Process return value with rsync exit code to recognize errors that # cannot be recognized by parsing the rsync output currently rsync_exit_code = proc.run() # Fix for #1491 and #489 # Note that the return value (containing the exit code) of the # rsync child process is not the only way to detect errors (and # sometimes not reliably delivers <> 0 in case of an error): # Errors are also indicated via the pass-by-ref argument # user_data="params" list (updated by the callback function that # parses the rsync output for error message patterns). # cleanup try: os.remove(self.config.takeSnapshotProgressFile()) except Exception as e: logger.debug('Failed to remove snapshot progress file %s: %s' % (self.config.takeSnapshotProgressFile(), str(e)), self) # handle errors # TODO # Fix inconsistent usage: Collects return value, but errors are also # checked via params[0] has_errors = False # dict of exit codes (as keys) that are treated as INFO only by BiT # (not as ERROR). The values are message strings for the snapshot log. rsync_non_error_exit_codes = { 0: _("Success"), # ignored as fix for #1587 (until we introduce a new snapshot # result category "(with warnings)") 23: _("Partial transfer due to error"), 24: _("Partial transfer due to vanished source files " "(see 'man rsync')") } rsync_exit_code_msg = _("'rsync' ended with exit code {exit_code}") \ .format(exit_code=rsync_exit_code) if rsync_exit_code in rsync_non_error_exit_codes: self.setTakeSnapshotMessage( 0, rsync_exit_code_msg + ": " + rsync_non_error_exit_codes[rsync_exit_code]) elif rsync_exit_code > 0: # an rsync error # HACK to fix #489 (params[0] and has_errors should be merged) params[0] = True self.setTakeSnapshotMessage( 1, rsync_exit_code_msg + ": " + _("See 'man rsync' for more details")) elif rsync_exit_code < 0: # an rsync error caused by a signal # HACK to fix #489 (params[0] and has_errors should be merged) params[0] = True self.setTakeSnapshotMessage( 1, rsync_exit_code_msg + ": " + _("Negative rsync exit codes are signal numbers, see " "'kill -l' and 'man kill'")) # params[0] -> error? if params[0]: if not self.config.continueOnErrors(): self.remove(new_snapshot) return [False, True] has_errors = True new_snapshot.failed = True # params[1] -> changes? if not params[1] and not self.config.takeSnapshotRegardlessOfChanges(): self.remove(new_snapshot) logger.info("Nothing changed, no new snapshot necessary", self) self.snapshotLog.append( '[I] ' + _('Nothing changed, no new snapshot necessary'), 3) if prev_sid: prev_sid.setLastChecked() if not has_errors: tools.writeTimeStamp(self.config.anacronSpoolFile()) # Part of fix for #1491: # Returns "has_errors" instead of False now to signal rsync errors # (which may have prevented processing any changes) return [False, has_errors] self.backupConfig(new_snapshot) self.backupPermissions(new_snapshot) # copy snapshot log try: self.snapshotLog.flush() with open(self.snapshotLog.logFileName, 'rb') as logfile: new_snapshot.setLog(logfile.read()) except Exception as e: logger.debug('Failed to write takeSnapshot log %s into ' 'compressed file %s: %s' % ( self.config.takeSnapshotLogFile(), new_snapshot.path(SID.LOG), str(e)), self) # TODO How is this error handled? Currently it looks like it is # ignored (just logged)! new_snapshot.saveToContinue = False # rename snapshot os.rename(new_snapshot.path(), sid.path()) if not sid.exists(): logger.error( f"Can't rename {new_snapshot.path()} to {sid.path()}", self) self.setTakeSnapshotMessage( 1, _("Can't rename {new_path} to {path}") .format(new_path=new_snapshot.path(), path=sid.path()) ) time.sleep(2) # max 1 backup / second return [False, True] self._backup_info_file(sid) if not has_errors: tools.writeTimeStamp(self.config.anacronSpoolFile()) # create last_snapshot symlink self.createLastSnapshotSymlink(sid) return [True, has_errors] def smartRemoveKeepAll(self, snapshots, min_date, max_date): """ Return all snapshots between ``min_date`` and ``max_date``. Args: snapshots (list): full list of :py:class:`SID` objects min_date (datetime.date): minimum date for snapshots to keep max_date (datetime.date): maximum date for snapshots to keep Returns: set: set of snapshots that should be kept """ min_id = SID(min_date, self.config) max_id = SID(max_date, self.config) logger.debug("Keep all >= %s and < %s" %(min_id, max_id), self) return set([sid for sid in snapshots if sid >= min_id and sid < max_id]) def smartRemoveKeepFirst(self, snapshots, min_date, max_date, keep_healthy = False): """ Return only the first snapshot between ``min_date`` and ``max_date``. Args: snapshots (list): full list of :py:class:`SID` objects min_date (datetime.date): minimum date for snapshots to keep max_date (datetime.date): maximum date for snapshots to keep keep_healthy (bool): return the first healthy snapshot (not marked as failed) instead of the first at all. If all snapshots failed this will again return the very first snapshot Returns: set: set of snapshots that should be kept """ min_id = SID(min_date, self.config) max_id = SID(max_date, self.config) logger.debug("Keep first >= %s and < %s" %(min_id, max_id), self) for sid in snapshots: # try to keep the first healthy snapshot if keep_healthy and sid.failed: logger.debug("Do not keep failed snapshot %s" %sid, self) continue if sid >= min_id and sid < max_id: return set([sid]) # if all snapshots failed return the first snapshot # no matter if it has errors if keep_healthy: return self.smartRemoveKeepFirst(snapshots, min_date, max_date, keep_healthy = False) return set() def incMonth(self, date): """ First day of next month of ``date`` with respect on new years. So if ``date`` is December this will return 1st of January next year. Args: date (datetime.date): old date that should be increased Returns: datetime.date: 1st day of next month """ y = date.year m = date.month + 1 if m > 12: m = 1 y = y + 1 return datetime.date(y, m, 1) def decMonth(self, date): """ First day of previous month of ``date`` with respect on previous years. So if ``date`` is January this will return 1st of December previous year. Args: date (datetime.date): old date that should be decreased Returns: datetime.date: 1st day of previous month """ y = date.year m = date.month - 1 if m < 1: m = 12 y = y - 1 return datetime.date(y, m, 1) def smartRemoveList(self, now_full, keep_all, keep_one_per_day, keep_one_per_week, keep_one_per_month): """ Get a list of old snapshots that should be removed based on configurable intervals. Args: now_full (datetime.datetime): date and time when takeSnapshot was started keep_all (int): keep all snapshots for the last ``keep_all`` days keep_one_per_day (int): keep one snapshot per day for the last ``keep_one_per_day`` days keep_one_per_week (int): keep one snapshot per week for the last ``keep_one_per_week`` weeks keep_one_per_month (int): keep one snapshot per month for the last ``keep_one_per_month`` months Returns: list: snapshots that should be removed """ snapshots = listSnapshots(self.config) logger.debug(f'Considered: {snapshots}', self) if len(snapshots) <= 1: logger.debug('There is only one snapshot, so keep it', self) return [] if now_full is None: now_full = datetime.datetime.today() now = now_full.date() # keep the last snapshot keep = set([snapshots[0]]) # keep all for the last keep_all days if keep_all > 0: keep |= self.smartRemoveKeepAll( snapshots, now - datetime.timedelta(days=keep_all-1), now + datetime.timedelta(days=1)) # keep one per day for the last keep_one_per_day days if keep_one_per_day > 0: d = now for i in range(0, keep_one_per_day): keep |= self.smartRemoveKeepFirst( snapshots, d, d + datetime.timedelta(days=1), keep_healthy=True) d -= datetime.timedelta(days=1) # keep one per week for the last keep_one_per_week weeks if keep_one_per_week > 0: d = now - datetime.timedelta(days=now.weekday() + 1) for i in range(0, keep_one_per_week): keep |= self.smartRemoveKeepFirst( snapshots, d, d + datetime.timedelta(days=8), keep_healthy=True) d -= datetime.timedelta(days=7) # keep one per month for the last keep_one_per_month months if keep_one_per_month > 0: d1 = datetime.date(now.year, now.month, 1) d2 = self.incMonth(d1) for i in range(0, keep_one_per_month): keep |= self.smartRemoveKeepFirst( snapshots, d1, d2, keep_healthy=True) d2 = d1 d1 = self.decMonth(d1) # keep one per year for all years first_year = int(snapshots[-1].sid[:4]) for i in range(first_year, now.year+1): keep |= self.smartRemoveKeepFirst(snapshots, datetime.date(i, 1, 1), datetime.date(i+1, 1, 1), keep_healthy=True) logger.debug(f'Keep snapshots: {keep}', self) del_snapshots = [] for sid in snapshots: if sid in keep: continue if self.config.dontRemoveNamedSnapshots(): if sid.name: logger.debug( f'Keep snapshot: {sid}, because it has a name', self) continue del_snapshots.append(sid) return del_snapshots def smartRemove(self, del_snapshots, log = None): """ Remove multiple snapshots either with :py:func:`Snapshots.remove` or in background on the remote host if mode is `ssh` or `ssh_encfs` and smart-remove in background is activated. Args: del_snapshots (list): list of :py:class:`SID` that should be removed log (method): callable method that will handle progress log """ if not del_snapshots: return if not log: log = lambda x: self.setTakeSnapshotMessage(0, x) if self.config.snapshotsMode() in ['ssh', 'ssh_encfs'] and self.config.smartRemoveRunRemoteInBackground(): logger.info('[smart remove] remove snapshots in background: %s' % del_snapshots, self) lckFile = os.path.normpath( os.path.join( del_snapshots[0].path(use_mode=['ssh', 'ssh_encfs']), os.pardir, 'smartremove.lck' ) ) maxLength = self.config.sshMaxArgLength() if not maxLength: import sshMaxArg user_host = '%s@%s' % (self.config.sshUser(), self.config.sshHost()) maxLength = sshMaxArg.probe_max_ssh_command_size(self.config) self.config.setSshMaxArgLength(maxLength) self.config.save() sshMaxArg.report_result(user_host, maxLength) additionalChars = len(self.config.sshPrefixCmd(cmd_type = str)) head = 'screen -d -m bash -c "(' # create temp dir used for delete files with rsync head += 'TMP=\\$(mktemp -d); ' # make sure $TMP dir was created head += 'test -z \\\"\\$TMP\\\" && exit 1; ' # make sure $TMP is empty head += 'test -n \\\"\\$(ls \\$TMP)\\\" && exit 1; ' if logger.DEBUG: head += 'logger -t \\\"backintime smart-remove [$BASHPID]\\\" \\\"start\\\"; ' head += 'flock -x 9; ' if logger.DEBUG: head += 'logger -t \\\"backintime smart-remove [$BASHPID]\\\" \\\"got exclusive flock\\\"; ' tail = 'rmdir \\$TMP) 9>\\\"%s\\\""' %lckFile cmds = [] for sid in del_snapshots: remote = self.rsyncRemotePath(sid.path(use_mode = ['ssh', 'ssh_encfs']), use_mode = [], quote = '\\\"') rsync = ' '.join(tools.rsyncRemove(self.config, run_local = False)) rsync += ' \\\"\\$TMP/\\\" {}; '.format(remote) s = 'test -e \\\"%s\\\" && (' %sid.path(use_mode = ['ssh', 'ssh_encfs']) if logger.DEBUG: s += 'logger -t \\\"backintime smart-remove [$BASHPID]\\\" ' s += '\\\"snapshot %s still exist\\\"; ' %sid s += 'sleep 1; ' #add one second delay because otherwise you might not see serialized process with small snapshots s += rsync s += 'rmdir \\\"%s\\\"; ' %sid.path(use_mode = ['ssh', 'ssh_encfs']) if logger.DEBUG: s += 'logger -t \\\"backintime smart-remove [$BASHPID]\\\" ' s += '\\\"snapshot %s remove done\\\"' %sid s += '); ' cmds.append(s) for cmd in tools.splitCommands(cmds, head = head, tail = tail, maxLength = maxLength - additionalChars): tools.Execute(self.config.sshCommand([cmd,], quote = False, nice = False, ionice = False)).run() else: logger.info("[smart remove] remove snapshots: %s" %del_snapshots, self) for i, sid in enumerate(del_snapshots, 1): log(_('Smart removal') + ' %s/%s' %(i, len(del_snapshots))) self.remove(sid) def freeSpace(self, now): """ Remove old snapshots on based on different rules (only if enabled). First rule is to remove snapshots older than X years. Next will call :py:func:`smartRemove` to remove snapshots based on configurable intervals. Third rule is to remove the oldest snapshot until there is enough free space. Last rule will remove the oldest snapshot until there are enough free inodes. 'last_snapshot' symlink will be fixed when done. Args: now (datetime.datetime): date and time when takeSnapshot was started """ snapshots = listSnapshots(self.config, reverse = False) if not snapshots: logger.debug('No snapshots. Skip freeSpace', self) return last_snapshot = snapshots[-1] #remove old backups if self.config.removeOldSnapshotsEnabled(): self.setTakeSnapshotMessage(0, _('Removing old snapshots')) oldBackupId = SID(self.config.removeOldSnapshotsDate(), self.config) logger.debug("Remove snapshots older than: {}".format(oldBackupId.withoutTag), self) while True: if len(snapshots) <= 1: break if snapshots[0] >= oldBackupId: break if self.config.dontRemoveNamedSnapshots(): if snapshots[0].name: del snapshots[0] continue msg = 'Remove snapshot {} because it is older than {}' logger.debug(msg.format(snapshots[0].withoutTag, oldBackupId.withoutTag), self) self.remove(snapshots[0]) del snapshots[0] # smart remove enabled, keep_all, keep_one_per_day, keep_one_per_week, keep_one_per_month = self.config.smartRemove() if enabled: self.setTakeSnapshotMessage(0, _('Smart removal')) del_snapshots = self.smartRemoveList(now, keep_all, keep_one_per_day, keep_one_per_week, keep_one_per_month) self.smartRemove(del_snapshots) # try to keep min free space if self.config.minFreeSpaceEnabled(): self.setTakeSnapshotMessage(0, _('Trying to keep min free space')) minFreeSpace = self.config.minFreeSpaceMib() logger.debug("Keep min free disk space: {} MiB".format(minFreeSpace), self) snapshots = listSnapshots(self.config, reverse = False) while True: if len(snapshots) <= 1: break free_space = self.statFreeSpaceLocal(self.config.snapshotsFullPath()) if free_space is None: free_space = self.statFreeSpaceSsh() if free_space is None: logger.warning('Failed to get free space. Skipping', self) break if free_space >= minFreeSpace: break if self.config.dontRemoveNamedSnapshots(): if snapshots[0].name: del snapshots[0] continue msg = "free disk space: {} MiB. Remove snapshot {}" logger.debug(msg.format(free_space, snapshots[0].withoutTag), self) self.remove(snapshots[0]) del snapshots[0] #try to keep free inodes if self.config.minFreeInodesEnabled(): minFreeInodes = self.config.minFreeInodes() self.setTakeSnapshotMessage( 0, _('Trying to keep min {perc} free inodes') .format(perc=f'{minFreeInodes}%') ) logger.debug( "Keep min {perc}% free inodes".format(perc=minFreeInodes), self) snapshots = listSnapshots(self.config, reverse = False) while True: if len(snapshots) <= 1: break try: info = os.statvfs(self.config.snapshotsPath()) free_inodes = info.f_favail max_inodes = info.f_files except Exception as e: logger.debug('Failed to get free inodes for snapshot path %s: %s' % (self.config.snapshotsPath(), str(e)), self) break if free_inodes >= max_inodes * (minFreeInodes / 100.0): break if self.config.dontRemoveNamedSnapshots(): if snapshots[0].name: del snapshots[0] continue logger.debug("free inodes: %.2f%%. Remove snapshot %s" %((100.0 / max_inodes * free_inodes), snapshots[0].withoutTag), self) self.remove(snapshots[0]) del snapshots[0] #set correct last snapshot again if last_snapshot is not snapshots[-1]: self.createLastSnapshotSymlink(snapshots[-1]) def statFreeSpaceLocal(self, path): """ Get free space on filesystem containing ``path`` in MiB using :py:func:`os.statvfs()`. Depending on remote SFTP server this might fail on sshfs mounted shares. Args: path (str): full path Returns: int free space in MiB """ try: info = os.statvfs(path) if info.f_blocks != info.f_bavail: return info.f_frsize * info.f_bavail // (1024 * 1024) except Exception as e: logger.debug('Failed to get free space for %s: %s' %(path, str(e)), self) logger.warning('Failed to stat snapshot path', self) def statFreeSpaceSsh(self): """ Get free space on remote filesystem in MiB. This will call ``df`` on remote host and parse its output. Returns: int free space in MiB """ if self.config.snapshotsMode() not in ('ssh', 'ssh_encfs'): return None snapshots_path_ssh = self.config.sshSnapshotsFullPath() if not len(snapshots_path_ssh): snapshots_path_ssh = './' cmd = self.config.sshCommand(['df', snapshots_path_ssh], nice=False, ionice=False) df = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = df.communicate()[0] # Filesystem 1K-blocks Used Available Use% Mounted on # /tmp 127266564 115596412 5182296 96% / # ^^^^^^^ for line in output.split(b'\n'): m = re.match(r'^.*?\s+\d+\s+\d+\s+(\d+)\s+\d+%', line.decode(), re.M) if m: return int(int(m.group(1)) / 1024) logger.warning('Failed to get free space on remote', self) def filter(self, base_sid, base_path, snapshotsList, list_diff_only = False, flag_deep_check = False, list_equal_to = ''): """ Filter snapshots from ``snapshotsList`` based on whether ``base_path`` file is included and optional if the snapshot is unique or equal to ``list_equal_to``. Args: base_sid (SID): snapshot ID that contained the original file ``base_path`` base_path (str): path to file on root filesystem. snapshotsList (list): List of :py:class:`SID` objects that should be filtered list_diff_only (bool): if ``True`` only return unique snapshots. Which means if a file is exactly the same in different snapshots only the first snapshot will be listed flag_deep_check (bool): use md5sum to check uniqueness of files. More accurate but slow list_equal_to (str): full path to file. If not empty only return snapshots which have exactly the same file as this file Returns: list: filtered list of :py:class:`SID` objects """ snapshotsFiltered = [] base_full_path = base_sid.pathBackup(base_path) if not os.path.lexists(base_full_path): return [] allSnapshotsList = [RootSnapshot(self.config)] allSnapshotsList.extend(snapshotsList) #links if os.path.islink(base_full_path): targets = [] for sid in allSnapshotsList: path = sid.pathBackup(base_path) if os.path.lexists(path) and os.path.islink(path): if list_diff_only: target = os.readlink(path) if target in targets: continue targets.append(target) snapshotsFiltered.append(sid) return snapshotsFiltered #directories if os.path.isdir(base_full_path): for sid in allSnapshotsList: path = sid.pathBackup(base_path) if os.path.exists(path) and not os.path.islink(path) and os.path.isdir(path): snapshotsFiltered.append(sid) return snapshotsFiltered #files if not list_diff_only and not list_equal_to: for sid in allSnapshotsList: path = sid.pathBackup(base_path) if os.path.exists(path) and not os.path.islink(path) and os.path.isfile(path): snapshotsFiltered.append(sid) return snapshotsFiltered # check for duplicates uniqueness = tools.UniquenessSet(flag_deep_check, follow_symlink = False, list_equal_to = list_equal_to) for sid in allSnapshotsList: path = sid.pathBackup(base_path) if os.path.exists(path) and not os.path.islink(path) and os.path.isfile(path) and uniqueness.check(path): snapshotsFiltered.append(sid) return snapshotsFiltered #TODO: move this to config.Config -> Don't! def rsyncRemotePath(self, path, use_mode = ['ssh', 'ssh_encfs'], quote = '"'): """ Format the destination string for rsync depending on which profile is used. Args: path (str): destination path use_mode (list): list of modes in which the result should change to ``user@host:path`` instead of just ``path`` quote (str): use this to quote the path Returns: str: quoted ``path`` like '"/foo"' or if the current mode is using ssh and current mode is in ``use_mode`` a combination of user, host and ``path`` like ''user@host:"/foo"'' """ mode = self.config.snapshotsMode() if mode in ['ssh', 'ssh_encfs'] and mode in use_mode: user = self.config.sshUser() host = tools.escapeIPv6Address(self.config.sshHost()) return '%(u)s@%(h)s:%(q)s%(p)s%(q)s' % {'u': user, 'h': host, 'q': quote, 'p': path} else: return path def deletePath(self, sid, path): """ Delete ``path`` and all files and folder inside in snapshot ``sid``. Args: sid (SID): snapshot ID in which ``path`` should be deleted path (str): path to delete """ def errorHandler(fn, path, excinfo): """ Error handler for :py:func:`deletePath`. This will fix permissions and try again to remove the file. Args: fn (method): callable which failed before path (str): file to delete excinfo: NotImplemented """ dirname = os.path.dirname(path) st = os.stat(dirname) os.chmod(dirname, st.st_mode | stat.S_IWUSR) st = os.stat(path) os.chmod(path, st.st_mode | stat.S_IWUSR) fn(path) full_path = sid.pathBackup(path) dirname = os.path.dirname(full_path) dir_st = os.stat(dirname) os.chmod(dirname, dir_st.st_mode | stat.S_IWUSR) if os.path.isdir(full_path) and not os.path.islink(full_path): shutil.rmtree(full_path, onerror = errorHandler) else: st = os.stat(full_path) os.chmod(full_path, st.st_mode | stat.S_IWUSR) os.remove(full_path) os.chmod(dirname, dir_st.st_mode) def createLastSnapshotSymlink(self, sid): """ Create symlink 'last_snapshot' to snapshot ``sid`` Args: sid (SID): snapshot that should be linked. Returns: bool: ``True`` if successful """ if sid is None: return symlink = self.config.lastSnapshotSymlink() try: if os.path.islink(symlink): if os.path.basename(os.path.realpath(symlink)) == sid.sid: return True os.remove(symlink) if os.path.exists(symlink): logger.error('Could not remove symlink %s' %symlink, self) return False logger.debug('Create symlink %s => %s' %(symlink, sid), self) os.symlink(sid.sid, symlink) return True except Exception as e: logger.error('Failed to create symlink %s: %s' %(symlink, str(e)), self) return False def rsyncSuffix(self, includeFolders = None, excludeFolders = None): """ Create suffixes for rsync. Args: includeFolders (list): folders to include. list of tuples (item, int) Where ``int`` is ``0`` if ``item`` is a folder or ``1`` if ``item`` is a file excludeFolders (list): list of folders to exclude Returns: list: rsync include and exclude options """ #create exclude patterns string rsync_exclude = self.rsyncExclude(excludeFolders) #create include patterns list rsync_include, rsync_include2 = self.rsyncInclude(includeFolders) encode = self.config.ENCODE ret = ['--chmod=Du+wx'] ret.extend(['--exclude=' + i for i in (encode.exclude(self.config.snapshotsPath()), encode.exclude(self.config._LOCAL_DATA_FOLDER), encode.exclude(self.config._MOUNT_ROOT) )]) # TODO: fix bug #561: # after rsync_exclude we need to explicitly include files inside excluded # folders, recursive exclude folder-content again and finally add the # rest from rsync_include2 ret.extend(rsync_include) ret.extend(rsync_exclude) ret.extend(rsync_include2) ret.append('--exclude=*') ret.append(encode.chroot) return ret def rsyncExclude(self, excludeFolders = None): """ Format exclude list for rsync Args: excludeFolders (list): list of folders to exclude Returns: OrderedSet: rsync exclude options """ items = tools.OrderedSet() encode = self.config.ENCODE if excludeFolders is None: excludeFolders = self.config.exclude() for exclude in excludeFolders: exclude = encode.exclude(exclude) if exclude is None: continue items.add('--exclude=' + exclude) return items def rsyncInclude(self, includeFolders = None): """ Format include list for rsync. Returns a tuple of two include strings. First string need to come before exclude, second after exclude. Args: includeFolders (list): folders to include. list of tuples (item, int) where ``int`` is ``0`` if ``item`` is a folder or ``1`` if ``item`` is a file Returns: tuple: two item tuple of ``(OrderedSet('include1 opions'), OrderedSet('include2 options'))`` """ items1 = tools.OrderedSet() items2 = tools.OrderedSet() encode = self.config.ENCODE if includeFolders is None: includeFolders = self.config.include() for include_folder in includeFolders: folder = include_folder[0] if folder == "/": # If / is selected as included folder it should be changed to "" #folder = "" # because an extra / is added below. Patch thanks to Martin Hoefling items2.add('--include=/') items2.add('--include=/**') continue folder = encode.include(folder) if include_folder[1] == 0: items2.add('--include={}/**'.format(folder)) else: items2.add('--include={}'.format(folder)) folder = os.path.split(folder)[0] while True: if len(folder) <= 1: break items1.add('--include={}/'.format(folder)) folder = os.path.split(folder)[0] return (items1, items2) class FileInfoDict(dict): """ A :py:class:`dict` that maps a path (as :py:class:`bytes`) to a tuple (:py:class:`int`, :py:class:`bytes`, :py:class:`bytes`). """ def __init__(self): # default permissions for / # only used if fileinfo.bz2 does not contain a value for / # when it was created with version <= 1.1.12 # bugfix for https://github.com/bit-team/backintime/issues/708 self[b'/'] = (16877, b'root', b'root') def __setitem__(self, key, value): assert isinstance(key, bytes), "key '{}' is not bytes instance".format(key) assert isinstance(value, tuple), "value '{}' is not tuple instance".format(value) assert len(value) == 3, "value '{}' does not have 3 items".format(value) assert isinstance(value[0], int), "first value '{}' is not int instance".format(value[0]) assert isinstance(value[1], bytes), "second value '{}' is not bytes instance".format(value[1]) assert isinstance(value[2], bytes), "third value '{}' is not bytes instance".format(value[2]) super(FileInfoDict, self).__setitem__(key, value) class SID(object): """ Snapshot ID object used to gather all information for a snapshot See :py:class:`Snapshots` to understand the difference. Args: date (:py:class:`str`, :py:class:`datetime.date` or :py:class:`datetime.datetime`): used for creating this snapshot. str must be in snapshot ID format (e.g 20151218-173512-123) cfg (config.Config): current config Raises: ValueError: if ``date`` is :py:class:`str` instance and doesn't match the snapshot ID format (20151218-173512-123 or 20151218-173512) TypeError: if ``date`` is not :py:class:`str`, :py:class:`datetime.date` or :py:class:`datetime.datetime` type """ __cValidSID = re.compile(r'^\d{8}-\d{6}(?:-\d{3})?$') INFO = 'info' NAME = 'name' FAILED = 'failed' FILEINFO = 'fileinfo.bz2' LOG = 'takesnapshot.log.bz2' def __init__(self, date, cfg): self.config = cfg self.profileID = cfg.currentProfile() self.isRoot = False if isinstance(date, datetime.datetime): self.sid = '-'.join((date.strftime('%Y%m%d-%H%M%S'), self.config.tag(self.profileID))) self.date = date elif isinstance(date, datetime.date): self.sid = '-'.join((date.strftime('%Y%m%d-000000'), self.config.tag(self.profileID))) self.date = datetime.datetime.combine(date, datetime.datetime.min.time()) elif isinstance(date, str): if self.__cValidSID.match(date): self.sid = date self.date = datetime.datetime(*self.split()) elif date == 'last_snapshot': raise LastSnapshotSymlink() else: raise ValueError("'date' must be in snapshot ID format (e.g 20151218-173512-123)") else: raise TypeError("'date' must be an instance of str, datetime.date or datetime.datetime") def __repr__(self): return self.sid def __eq__(self, other): """ Compare snapshots based on self.sid Args: other (:py:class:`SID`, :py:class:`str`): an other :py:class:`SID` or str instance Returns: bool: ``True`` if other is equal """ if isinstance(other, SID): return self.sid == other.sid and self.profileID == other.profileID elif isinstance(other, str): return self.sid == other else: return NotImplemented def __ne__(self, other): return not self.__eq__(other) def __lt__(self, other): """ Sort snapshots (alphabetical order) based on self.sid Args: other (:py:class:`SID`, :py:class:`str`): an other :py:class:`SID` or str instance Returns: bool: ``True`` if other is lower """ if isinstance(other, SID): return self.sid < other.sid elif isinstance(other, str) and self.__cValidSID.match(other): return self.sid < other else: return NotImplemented def __le__(self, other): if isinstance(other, SID): return self.sid <= other.sid elif isinstance(other, str) and self.__cValidSID.match(other): return self.sid <= other else: return NotImplemented def __gt__(self, other): if isinstance(other, SID): return self.sid > other.sid elif isinstance(other, str) and self.__cValidSID.match(other): return self.sid > other else: return NotImplemented def __ge__(self, other): if isinstance(other, SID): return self.sid >= other.sid elif isinstance(other, str) and self.__cValidSID.match(other): return self.sid >= other else: return NotImplemented def __hash__(self): return hash(self.sid + self.profileID) def split(self): """ Split self.sid into a tuple of int's with Year, Month, Day, Hour, Minute, Second Returns: tuple: tuple of 6 int """ def split(s, e): return int(self.sid[s:e]) return (split(0, 4), split(4, 6), split(6, 8), split(9, 11), split(11, 13), split(13, 15)) @property def displayID(self): """ Snapshot ID in a user-readable format: YYYY-MM-DD HH:MM:SS Returns: str: formatted sID """ return "{:04}-{:02}-{:02} {:02}:{:02}:{:02}".format(*self.split()) @property def displayName(self): """ Combination of displayID, name and error indicator (if any) Returns: str: name """ ret = self.displayID name = self.name if name: ret += ' - {}'.format(name) if self.failed: ret += ' ({})'.format('WITH ERRORS !') return ret @property def tag(self): """ Snapshot ID's tag Returns: str: tag (last three digits) """ return self.sid[16:] @property def withoutTag(self): """ Snapshot ID without tag Returns: str: YYYYMMDD-HHMMSS """ return self.sid[0:15] def path(self, *path, use_mode = []): """ Current path of this snapshot automatically altered for remote/encrypted version of this path Args: *path (str): one or more folder/files to join at the end of the path. use_mode (list): list of modes that should alter this path. If the current mode is in this list, the path will automatically be altered for the remote/encrypted version of this path. Returns: str: full snapshot path """ path = [i.strip(os.sep) for i in path] current_mode = self.config.snapshotsMode(self.profileID) if 'ssh' in use_mode and current_mode == 'ssh': return os.path.join(self.config.sshSnapshotsFullPath(self.profileID), self.sid, *path) if 'ssh_encfs' in use_mode and current_mode == 'ssh_encfs': ret = os.path.join(self.config.sshSnapshotsFullPath(self.profileID), self.sid, *path) return self.config.ENCODE.remote(ret) return os.path.join(self.config.snapshotsFullPath(self.profileID), self.sid, *path) def pathBackup(self, *path, **kwargs): """ 'backup' folder inside snapshots path Args: *path (str): one or more folder/files to join at the end of the path. use_mode (list): list of modes that should alter this path. If the current mode is in this list, the path will automatically be altered for the remote/encrypted version of this path. Returns: str: full snapshot path """ return self.path('backup', *path, **kwargs) def makeDirs(self, *path): """ Create snapshot directory Args: *path (str): one or more folder/files to join at the end of the path Returns: bool: ``True`` if successful """ if not os.path.isdir(self.config.snapshotsFullPath(self.profileID)): logger.error('Snapshots path {} doesn\'t exist. Unable to make dirs for snapshot ID {}'.format( self.config.snapshotsFullPath(self.profileID), self.sid), self) return False return tools.makeDirs(self.pathBackup(*path)) def exists(self): """ ``True`` if the snapshot folder and the "backup" folder inside exist Returns: bool: ``True`` if exists """ return os.path.isdir(self.path()) and os.path.isdir(self.pathBackup()) def isExistingPathInsideSnapshotFolder(self, path): """ ``True`` if path is a file inside this snapshot Args: path (str): path from local filesystem (no snapshot path) Returns: bool: ``True`` if file exists """ fullPath = self.pathBackup(path) if not os.path.exists(fullPath): return False if not os.path.islink(fullPath): return True basePath = self.pathBackup() target = os.readlink(fullPath) target = os.path.join(os.path.abspath(os.path.dirname(fullPath)), target) return target.startswith(basePath) @property def name(self): """ Name of this snapshot Args: name (str): new name of the snapshot Returns: str: name of this snapshot """ nameFile = self.path(self.NAME) if not os.path.isfile(nameFile): return '' try: with open(nameFile, 'rt') as f: return f.read() except Exception as e: logger.debug('Failed to get snapshot {} name: {}'.format( self.sid, str(e)), self) @name.setter def name(self, name): nameFile = self.path(self.NAME) self.makeWritable() try: with open(nameFile, 'wt') as f: f.write(name) except Exception as e: logger.debug('Failed to set snapshot {} name: {}'.format( self.sid, str(e)), self) @property def lastChecked(self): """ Date when snapshot has finished last time. This can be the end of creation of this snapshot or the last time when this snapshot was checked against source without changes. Returns: str: date and time of last check (YYYY-MM-DD HH:MM:SS) """ info = self.path(self.INFO) if os.path.exists(info): return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(os.path.getatime(info))) return self.displayID #using @property.setter would be confusing here as there is no value to give def setLastChecked(self): """ Set info files atime to current time to indicate this snapshot was checked against source without changes right now. """ info = self.path(self.INFO) if os.path.exists(info): os.utime(info, None) @property def failed(self): """ This snapshot has failed Args: enable (bool): set or remove flag Returns: bool: ``True`` if flag is set """ failedFile = self.path(self.FAILED) return os.path.isfile(failedFile) @failed.setter def failed(self, enable): failedFile = self.path(self.FAILED) if enable: self.makeWritable() try: with open(failedFile, 'wt') as f: f.write('') except Exception as e: logger.debug('Failed to mark snapshot {} failed: {}'.format( self.sid, str(e)), self) elif os.path.exists(failedFile): os.remove(failedFile) @property def info(self): """ Load/save "info" file which contains additional information about this snapshot (using configfile.ConfigFile) Args: i (configfile.ConfigFile): info that should be saved. Returns: configfile.ConfigFile: snapshots information """ i = configfile.ConfigFile() i.load(self.path(self.INFO)) return i @info.setter def info(self, i): assert isinstance(i, configfile.ConfigFile), 'i is not configfile.ConfigFile type: {}'.format(i) i.save(self.path(self.INFO)) @property def fileInfo(self): """ Load/save "fileinfo.bz2" Args: d (FileInfoDict): dict of: {path: (permission, user, group)} Returns: FileInfoDict: dict of: {path: (permission, user, group)} """ d = FileInfoDict() infoFile = self.path(self.FILEINFO) if not os.path.isfile(infoFile): return d try: with bz2.BZ2File(infoFile, 'rb') as fileinfo: for line in fileinfo: line = line.strip(b'\n') if not line: continue index = line.find(b'/') if index < 0: continue f = line[index:] if not f: continue info = line[:index].strip().split(b' ') if len(info) == 3: d[f] = (int(info[0]), info[1], info[2]) #perms, user, group except (FileNotFoundError, PermissionError) as e: logger.error('Failed to load {} from snapshot {}: {}'.format( self.FILEINFO, self.sid, str(e)), self) return d @fileInfo.setter def fileInfo(self, d): assert isinstance(d, FileInfoDict), 'd is not FileInfoDict type: {}'.format(d) try: with bz2.BZ2File(self.path(self.FILEINFO), 'wb') as f: for path, info in d.items(): f.write(b' '.join((str(info[0]).encode('utf-8', 'replace'), info[1], info[2], path)) + b'\n') except PermissionError as e: logger.error('Failed to write {}: {}'.format(self.FILEINFO, str(e))) # TODO use @property decorator? IMHO not because it is not a "getter" but processes data # TODO Should have an action name like "loadLogFile" def log(self, mode = None, decode = None): """ Load log from "takesnapshot.log.bz2" Args: mode (int): Mode used for filtering. Take a look at :py:class:`snapshotlog.LogFilter` decode (encfstools.Decode): instance used for decoding lines or ``None`` Yields: str: filtered and decoded log lines """ logFile = self.path(self.LOG) logFilter = snapshotlog.LogFilter(mode, decode) try: with bz2.BZ2File(logFile, 'rb') as f: if logFilter.header: yield logFilter.header for line in f.readlines(): line = logFilter.filter(line.decode('utf-8').rstrip('\n')) if not line is None: yield line except Exception as e: msg = ('Failed to get snapshot log from {}:'.format(logFile), str(e)) logger.debug(' '.join(msg), self) for line in msg: yield line def setLog(self, log): """ Write log to "takesnapshot.log.bz2" Args: log: full snapshot log """ if isinstance(log, str): log = log.encode('utf-8', 'replace') logFile = self.path(self.LOG) try: with bz2.BZ2File(logFile, 'wb') as f: f.write(log) except Exception as e: logger.error('Failed to write log into compressed file {}: {}'.format( logFile, str(e)), self) def makeWritable(self): """ Make the snapshot path writable so we can change files inside Returns: bool: ``True`` if successful """ path = self.path() rw = os.stat(path).st_mode | stat.S_IWUSR return os.chmod(path, rw) class GenericNonSnapshot(SID): @property def displayID(self): return self.name @property def displayName(self): return self.name @property def tag(self): return self.name @property def withoutTag(self): return self.name class NewSnapshot(GenericNonSnapshot): """ Snapshot ID object for 'new_snapshot' folder Args: cfg (config.Config): current config """ NEWSNAPSHOT = 'new_snapshot' SAVETOCONTINUE = 'save_to_continue' def __init__(self, cfg): self.config = cfg self.profileID = cfg.currentProfile() self.isRoot = False self.sid = self.NEWSNAPSHOT self.date = datetime.datetime(1, 1, 1) self.__le__ = self.__lt__ self.__ge__ = self.__gt__ def __lt__(self, other): return False def __gt__(self, other): return True @property def name(self): """ Name of this snapshot Returns: str: name of this snapshot """ return self.sid @property def saveToContinue(self): """ Check if 'save_to_continue' flag is set Args: enable (bool): set or remove flag Returns: bool: ``True`` if flag is set """ return os.path.exists(self.path(self.SAVETOCONTINUE)) @saveToContinue.setter def saveToContinue(self, enable): flag = self.path(self.SAVETOCONTINUE) if enable: try: with open(flag, 'wt') as f: pass except Exception as e: logger.error("Failed to set 'save_to_continue' flag: %s" %str(e)) # should be "safe", throughout elif os.path.exists(flag): try: os.remove(flag) except Exception as e: logger.error("Failed to remove 'save_to_continue' flag: %s" %str(e)) # should be "safe", throughout @property def hasChanges(self): """ Check if there where changes in previous sessions. Returns: bool: ``True`` if there where changes """ log = snapshotlog.SnapshotLog(self.config, self.profileID) c = re.compile(r'^\[C\] ') for line in log.get(mode = snapshotlog.LogFilter.CHANGES): if c.match(line): return True return False class RootSnapshot(GenericNonSnapshot): """ Snapshot ID for the filesystem root folder ('/') Args: cfg (config.Config): current config """ def __init__(self, cfg): self.config = cfg self.profileID = cfg.currentProfile() self.isRoot = True self.sid = '/' self.date = datetime.datetime(datetime.MAXYEAR, 12, 31) self.__le__ = self.__lt__ self.__ge__ = self.__gt__ def __lt__(self, other): return False def __gt__(self, other): return True @property def name(self): """ Name of this snapshot Returns: str: name of this snapshot """ return _('Now') def path(self, *path, use_mode = []): """ Current path of this snapshot automatically altered for remote/encrypted version of this path Args: *path (str): one or more folder/files to join at the end of the path. use_mode (list): list of modes that should alter this path. If the current mode is in this list, the path will automatically altered for the remote/encrypted version of this path. Returns: str: full snapshot path """ current_mode = self.config.snapshotsMode(self.profileID) if 'ssh_encfs' in use_mode and current_mode == 'ssh_encfs': if path: path = self.config.ENCODE.remote(os.path.join(*path)) return os.path.join(self.config.ENCODE.chroot, path) else: return os.path.join(os.sep, *path) def iterSnapshots(cfg, includeNewSnapshot = False): """ A generator to iterate over snapshots in current snapshot path. Args: cfg (config.Config): current config includeNewSnapshot (bool): include a NewSnapshot instance if 'new_snapshot' folder is available. Yields: SID: snapshot IDs """ path = cfg.snapshotsFullPath() if not os.path.exists(path): return None for item in os.listdir(path): if item == NewSnapshot.NEWSNAPSHOT: newSid = NewSnapshot(cfg) if newSid.exists() and includeNewSnapshot: yield newSid continue try: sid = SID(item, cfg) if sid.exists(): yield sid # REFACTOR! # LastSnapshotSymlink is an exception instance and could be caught # explicit. But not sure about its purpose. except Exception as e: if not isinstance(e, LastSnapshotSymlink): logger.debug( "'{}' is not a snapshot ID: {}".format(item, str(e))) def listSnapshots(cfg, includeNewSnapshot = False, reverse = True): """ List of snapshots in current snapshot path. Args: cfg (config.Config): current config (config.Config instance) includeNewSnapshot (bool): include a NewSnapshot instance if 'new_snapshot' folder is available reverse (bool): sort reverse Returns: list: list of :py:class:`SID` objects """ ret = list(iterSnapshots(cfg, includeNewSnapshot)) ret.sort(reverse = reverse) return ret def lastSnapshot(cfg): """ Most recent snapshot. Args: cfg (config.Config): current config (config.Config instance) Returns: SID: most recent snapshot ID """ sids = listSnapshots(cfg) if sids: return sids[0] if __name__ == '__main__': config = config.Config() snapshots = Snapshots(config) snapshots.backup() backintime-1.5.2/common/sshMaxArg.py000066400000000000000000000142111465446530500173770ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (C) 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. # # 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 module determines the maximum possible length of an SSH command. It can also can run as a stand alone script. The solution is based on https://www.theeggeadventure.com/wikimedia/index.php/Ssh_argument_length """ import random import string import subprocess import socket import argparse # must be divisible by 8 _INITIAL_SSH_COMMAND_SIZE = 1048320 def probe_max_ssh_command_size(config, ssh_command_size=_INITIAL_SSH_COMMAND_SIZE, size_offset=_INITIAL_SSH_COMMAND_SIZE): """Determine the maximum length of SSH commands for the current config Try a SSH command with length ``ssh_command_size``. The command is decreased by ``size_offset`` if it was too long or increased if it worked. The function calls itself recursively until it finds the maximum possible length. The offset ``size_offset`` is bisect in each try. Args: config (config.Config): Back In Time config instance including the details about the current SSH snapshot profile. The current profile must use the SSH mode. ssh_command_size (int): Initial length used for the test argument. size_offset (int): Offset for increase or decrease ``ssh_command_size``. Returns: (int): The maximum possible SSH command length Raises: Exception: If there are unhandled cases or the recurse ends in an undefined state. OSError: If there are unhandled cases. """ size_offset = round(size_offset / 2) # random string of desired length command_string = ''.join(random.choices( string.ascii_uppercase+string.digits, k=ssh_command_size)) # use that string in a printf statement via SSH ssh = config.sshCommand( cmd=['printf', command_string], nice=False, ionice=False, prefix=False) try: proc = subprocess.Popen(ssh, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) out, err = proc.communicate() except OSError as err: # Only handle "Argument to long error" (E2BIG) if err.errno != 7: raise err report_test( ssh_command_size, f'Python exception: "{err.strerror}". Decrease ' f'by {size_offset:,} and try again.') # test again with new ssh_command_size return probe_max_ssh_command_size( config, ssh_command_size - size_offset, size_offset) else: # Successful SSH command if out == command_string: # no increases possible anymore if size_offset == 0: report_test(ssh_command_size, 'Found correct length. Adding ' f'length of "{ssh[-2]}" to it.') # the final command size return ssh_command_size + len(ssh[-2]) # length of "printf" # there is room to increase the length report_test(ssh_command_size, f'Can be longer. Increase by {size_offset:,} ' 'and try again.') # increase by "size_offset" and try again return probe_max_ssh_command_size( config, ssh_command_size + size_offset, size_offset) # command string was too long elif 'Argument list too long' in err: report_test(ssh_command_size, f'stderr: "{err.strip()}". Decrease ' f'by {size_offset:,} and try again.') # reduce by "size_offset" and try again return probe_max_ssh_command_size( config, ssh_command_size - size_offset, size_offset) raise Exception('Unhandled case.\n' f'{ssh[:-1]}\nout="{out}"\nerr="{err}"\n' f'ssh_command_size={ssh_command_size:,}\nsize_offset={size_offset:,}') def report_test(ssh_command_size, msg): print(f'Tried length {ssh_command_size:,}... {msg}') def report_result(host, max_ssh_cmd_size): print(f'Maximum SSH command length between "{socket.gethostname()}" ' f'and "{host}" is {max_ssh_cmd_size:,}.') if __name__ == '__main__': parser = argparse.ArgumentParser( description='Check the maximal ssh command size for all ssh profiles in the configurations', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('SSH_COMMAND_SIZE', type=int, nargs='?', default=_INITIAL_SSH_COMMAND_SIZE, help='Start checking with SSH_COMMAND_SIZE as length') args = parser.parse_args() import config cfg = config.Config() profiles = cfg.profiles() # list of profile IDs # loop over all profiles in the configuration for profile_ID in profiles: cfg.setCurrentProfile(profile_ID) print(f"Profile {profile_ID} - {cfg.profileName()}: Mode = {cfg.snapshotsMode()}") if cfg.snapshotsMode() == "ssh": ssh_command_size = probe_max_ssh_command_size(cfg, args.SSH_COMMAND_SIZE) report_result(cfg.sshHost(), ssh_command_size) backintime-1.5.2/common/sshtools.py000066400000000000000000001263671465446530500174000ustar00rootroot00000000000000# Copyright (C) 2012-2022 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 subprocess import string import random import tempfile import socket import re import atexit import signal from pathlib import Path from time import sleep import logger import tools import password_ipc from mount import MountControl from exceptions import MountException, NoPubKeyLogin, KnownHost import bcolors import version class SSH(MountControl): """ This is a backend for the mount API :py:class:`mount.MountControl`. This will mount the remote path with ``sshfs``, prepare the remote path and check that everything is set up correctly for `Back In Time` to run snapshots through SSH. This class will only mount the remote path. The real takeSnapshot process will use rsync over ssh. Other commands run remote over ssh. Args: cfg (config.Config): current config (handled by inherited :py:class:`mount.MountControl`) user (str): User name on remote host host (str): Name or IP Address of remote host port (int): Port used by SSHd on remote host path (str): remote path where snapshots are stored. Can be either relative from remote users homedir or an absolute path cipher (str): Cipher used to encrypt the network transfer private_key_file (str): Private key which is able to log on with Public/Private Key-Method on remote host nice (bool): use ``nice -n 19`` to run commands with low CPU priority on remote host ionice (bool): use ``ionice -c2 -n7`` to run commands with low IO priority on remote host nocache (bool): use ``nocache`` to deactivate RAM caching of files on remote host password (str): password to unlock the private key profile_id (str): profile ID that should be used (handled by inherited :py:class:`mount.MountControl`) hash_id (str): crc32 hash used to identify identical mountpoints (handled by inherited :py:class:`mount.MountControl`) tmp_mount (bool): if ``True`` mount to a temporary destination (handled by inherited :py:class:`mount.MountControl`) parent (QWidget): parent widget for QDialogs or ``None`` if there is no parent (handled by inherited :py:class:`mount.MountControl`) symlink (bool): if ``True`` set symlink to mountpoint (handled by inherited :py:class:`mount.MountControl`) mode (str): one of ``local``, ``local_encfs``, ``ssh`` or ``ssh_encfs`` (handled by inherited :py:class:`mount.MountControl`) hash_collision (int): global value used to prevent hash collisions on mountpoints (handled by inherited :py:class:`mount.MountControl`) Note: All Arguments are optional. Default values will be fetched from :py:class:`config.Config`. But after changing Settings we need to test the new values **before** storing them into :py:class:`config.Config`. This is why all values will be added as arguments. """ def __init__(self, *args, **kwargs): # init MountControl super(SSH, self).__init__(*args, **kwargs) # Workaround for linters self.user = None self.host = None self.port = None self.cipher = None self.proxy_user = None self.proxy_host = None self.proxy_port = None self.nice = None self.ionice = None self.nocache = None self.private_key_file = None self.password = None self.setattrKwargs( 'user', self.config.sshUser(self.profile_id), **kwargs) self.setattrKwargs( 'host', self.config.sshHost(self.profile_id), **kwargs) self.setattrKwargs( 'port', self.config.sshPort(self.profile_id), **kwargs) self.setattrKwargs( 'path', self.config.sshSnapshotsPath(self.profile_id), **kwargs) self.setattrKwargs( 'cipher', self.config.sshCipher(self.profile_id), **kwargs) self.setattrKwargs( 'private_key_file', self.config.sshPrivateKeyFile(self.profile_id), **kwargs) self.setattrKwargs( 'proxy_user', self.config.sshProxyUser(self.profile_id), **kwargs) self.setattrKwargs( 'proxy_host', self.config.sshProxyHost(self.profile_id), **kwargs) self.setattrKwargs( 'proxy_port', self.config.sshProxyPort(self.profile_id), **kwargs) self.setattrKwargs( 'nice', self.config.niceOnRemote(self.profile_id), store=False, **kwargs) self.setattrKwargs( 'ionice', self.config.ioniceOnRemote(self.profile_id), store=False, **kwargs) self.setattrKwargs( 'nocache', self.config.nocacheOnRemote(self.profile_id), store=False, **kwargs) self.setattrKwargs('password', None, store=False, **kwargs) if not self.path: self.path = './' self.setDefaultArgs() # config strings used in ssh-calls self.user_host_path \ = '%s@%s:%s' % (self.user, tools.escapeIPv6Address(self.host), self.path) self.user_host = '%s@%s' % (self.user, self.host) self.mountproc = 'sshfs' self.symlink_subfolder = None self.log_command = '%s: %s' % (self.mode, self.user_host_path) self.private_key_fingerprint = sshKeyFingerprint(self.private_key_file) if not self.private_key_fingerprint: logger.warning('Couldn\'t get fingerprint for private ' 'key %(path)s. ' 'Most likely because the public key %(path)s.pub ' 'wasn\'t found. Using fallback to private keys ' 'path instead. But this can make troubles with ' 'passphrase-less keys.' % {'path': self.private_key_file}, self) self.private_key_fingerprint = self.private_key_file self.unlockSshAgent() def _mount(self): """ Backend mount method. This will call ``sshfs`` to mount the remote path. Raises: exceptions.MountException: if mount wasn't successful """ sshfs = [self.mountproc] sshfs += self.config.sshDefaultArgs(self.profile_id) sshfs += ['-p', str(self.port)] if not self.cipher == 'default': sshfs.extend(['-o', 'Ciphers=%s' % self.cipher]) sshfs.extend(['-o', 'idmap=user', '-o', 'cache_dir_timeout=2', '-o', 'cache_stat_timeout=2']) sshfs.extend([self.user_host_path, self.currentMountpoint]) # bugfix: sshfs doesn't mount if locale in LC_ALL is not available on # remote host # LANG or other environment variable are no problem. env = os.environ.copy() if 'LC_ALL' in list(env.keys()): env['LC_ALL'] = 'C' logger.debug('Call mount command: %s' % ' '.join(sshfs), self) # SSH Proxy (aka Jump host) if self.proxy_host: sshfs.extend([ '-o', 'ssh_command=ssh -J ' f'{self.proxy_user}@{self.proxy_host}:{self.proxy_port}' ]) logger.debug(f'Execute SSHFS command {sshfs}.') proc = subprocess.Popen(sshfs, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, universal_newlines=True) err = proc.communicate()[1] if proc.returncode == 0: return raise MountException( "{}\n\n{}".format( _("Can't mount {sshfs}").format(sshfs=" ".join(sshfs)), err ) ) def preMountCheck(self, first_run=False): """ Check that everything is prepared and ready for successfully mount the remote path. Default is to run a light version of checks which will only make sure the remote host is online, ``sshfs`` is installed and the remote folder is available. After changing settings this should be run with ``first_run = True`` to run a full check with all tests. Args: first_run (bool): run a full test with all checks Raises: exceptions.MountException: if one test failed an we can not mount the remote path """ self.checkPingHost() self.checkFuse() if first_run: self.unlockSshAgent(force=True) self.checkKnownHosts() self.checkLogin() if first_run: self.checkCipher() self.checkRemoteFolder() if first_run: self.checkRemoteCommands() return True def startSshAgent(self): """ Start a new ``ssh-agent`` if it is not already running. Raises: exceptions.MountException: if starting ``ssh-agent`` failed """ SOCK = 'SSH_AUTH_SOCK' PID = 'SSH_AGENT_PID' if os.getenv(SOCK, '') and os.getenv(PID, ''): logger.debug( 'ssh-agent already running. Skip starting a new one.', self) return sshAgent = tools.which('ssh-agent') if not sshAgent: raise MountException( _('ssh-agent not found. Please make sure it is installed.')) if isinstance(sshAgent, str): sshAgent = [sshAgent, ] sa = subprocess.Popen(sshAgent, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) out, err = sa.communicate() if sa.returncode: raise MountException( 'Failed to start ssh-agent: [{}] {}' .format(sa.returncode, err)) m = re.match(r'.*{}(?:=|\s)([^;]+);.*{}(?:=|\s)(\d+);' .format(SOCK, PID), out, re.DOTALL | re.MULTILINE) if m: logger.debug('ssh-agent started successful: {}={} | {}={}' .format(SOCK, m.group(1), PID, m.group(2)), self) os.environ[SOCK] = m.group(1) os.environ[PID] = m.group(2) atexit.register(os.kill, int(m.group(2)), signal.SIGKILL) else: raise MountException( 'No matching output from ssh-agent: {} | {}'.format(out, err)) def unlockSshAgent(self, force=False): """ Unlock the private key in ``ssh-agent`` which will provide it for all other commands. The password to unlock the key will be provided by ``backintime-askpass``. Args: force (bool): force to unlock the key by removing it first and add it again to make sure, the given values are correct Raises: exceptions.MountException: if unlock failed """ self.startSshAgent() env = os.environ.copy() env['SSH_ASKPASS'] = 'backintime-askpass' env['ASKPASS_PROFILE_ID'] = self.profile_id env['ASKPASS_MODE'] = self.mode if force: # remove private key first so we can check if the given # password is valid logger.debug( 'Remove private key %s from ssh agent' % self.private_key_file, self) proc = subprocess.Popen(['ssh-add', '-d', self.private_key_file], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, universal_newlines=True) proc.communicate() proc = subprocess.Popen(['ssh-add', '-l'], stdout=subprocess.PIPE, universal_newlines=True) output = proc.communicate()[0] if force or not output.find(self.private_key_fingerprint) >= 0: logger.debug( 'Add private key %s to ssh agent' % self.private_key_file, self) password_available = any([ self.config.passwordSave(self.profile_id), self.config.passwordUseCache(self.profile_id), not self.password is None ]) logger.debug('Password available: %s' % password_available, self) if not password_available and not tools.checkXServer(): # we need to unlink stdin from ssh-add in order to make it # use our own backintime-askpass. # But because of this we can NOT use getpass inside # backintime-askpass if password is not saved and there is no # x-server. # So, let's just keep ssh-add asking for the password in # that case. alarm = tools.Alarm() alarm.start(10) try: proc = subprocess.call(['ssh-add', self.private_key_file]) alarm.stop() except tools.Timeout: pass else: if self.password: logger.debug('Provide password through temp FIFO', self) thread = password_ipc.TempPasswordThread(self.password) env['ASKPASS_TEMP'] = thread.temp_file thread.start() proc = subprocess.Popen(['ssh-add', self.private_key_file], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, preexec_fn=os.setsid, universal_newlines=True) output, error = proc.communicate() if proc.returncode: logger.error('Failed to unlock SSH private key %s: %s' % (self.private_key_file, error), self) if self.password: thread.stop() proc = subprocess.Popen(['ssh-add', '-l'], stdout=subprocess.PIPE, universal_newlines=True) output = proc.communicate()[0] if not output.find(self.private_key_fingerprint) >= 0: logger.debug( 'Was not able to unlock private key %s' % self.private_key_file, self) raise MountException( _('Could not unlock ssh private key. Wrong password ' 'or password not available for cron.')) else: logger.debug('Private key %s is already unlocked in ssh agent' % self.private_key_file, self) def checkLogin(self): """ Try to login to remote host with public/private-key-method (passwordless). Raises: exceptions.NoPubKeyLogin: if login failed """ logger.debug('Check login', self) # Custom SSH arguments custom_ssh_args = [ '-o', 'PreferredAuthentications=publickey', '-p', str(self.port), self.user_host ] # Create SSH command ssh = self.config.sshCommand(cmd=['exit'], custom_args=custom_ssh_args, port=False, user_host=False, nice=False, ionice=False, profile_id=self.profile_id) proc = subprocess.Popen(ssh, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, universal_newlines=True) err = proc.communicate()[1] if proc.returncode: raise NoPubKeyLogin( 'Password-less authentication for %(user)s@%(host)s ' 'failed. Look at \'man backintime\' for further ' 'instructions.' % { 'user': self.user, 'host': self.host} + '\n\n' + err) def checkCipher(self): """ Try to login to remote host with the chosen cipher. This should make sure both `localhost` and the remote host support the chosen cipher. Raises: exceptions.MountException: if login with the cipher failed """ if not self.cipher == 'default': logger.debug('Check cipher', self) ssh = self.config.sshCommand(cmd=['exit'], custom_args=[ '-o', 'Ciphers=%s' % self.cipher, '-p', str(self.port), self.user_host ], port=False, cipher=False, user_host=False, nice=False, ionice=False, profile_id=self.profile_id) proc = subprocess.Popen(ssh, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, universal_newlines=True) err = proc.communicate()[1] if proc.returncode: logger.debug( 'Ciper %s is not supported' % self.config.SSH_CIPHERS[self.cipher], self) msg = _('Cipher {cipher} failed for {host}.').format( cipher=self.config.SSH_CIPHERS[self.cipher], host=self.host) raise MountException(f'{msg}:\n{err}') def benchmarkCipher(self, size=40): """ Rudimental benchmark to compare transfer speed of all available ciphers. Args: size (int): size of the testfile in MiB """ temp = tempfile.mkstemp()[1] print('create random data file') subprocess.call([ 'dd', 'if=/dev/urandom', 'of=%s' % temp, 'bs=1M', 'count=%s' % size ]) keys = list(self.config.SSH_CIPHERS.keys()) keys.sort() for cipher in keys: if cipher == 'default': continue print('%s%s:%s' % (bcolors.BOLD, cipher, bcolors.ENDC)) for i in range(2): # scp uses -P instead of -p for port subprocess.call([ 'scp', '-P', str(self.port), '-c', cipher, temp, self.user_host_path ]) ssh = self.config.sshCommand( cmd=['rm', os.path.join(self.path, os.path.basename(temp))], custom_args=['-p', str(self.port), self.user_host], port=False, cipher=False, user_host=False, nice=False, ionice=False, profile_id=self.profile_id) subprocess.call(ssh) os.remove(temp) def checkKnownHosts(self): """ Check if the remote host is in current users ``known_hosts`` file. Raises: exceptions.KnownHost: if the remote host wasn't found in ``known_hosts`` file """ logger.debug('Check known hosts file', self) for host in (self.host, '[%s]:%s' % (self.host, self.port)): proc = subprocess.Popen(['ssh-keygen', '-F', host], stdout=subprocess.PIPE, universal_newlines=True) # subprocess.check_output doesn't exist # in Python 2.6 (Debian squeeze default) output = proc.communicate()[0] if output.find('Host %s found' % host) >= 0: logger.debug( 'Host %s was found in known hosts file' % host, self) return True logger.debug('Host %s is not in known hosts file' % self.host, self) raise KnownHost('%s not found in ssh_known_hosts.' % self.host) def checkRemoteFolder(self): """ Check the remote path. If the remote path doesn't exist this will create it. If it already exist this will check, that it is a folder and has correct permissions. Raises: exceptions.MountException: if remote path couldn't be created or doesn't have correct permissions. """ logger.debug('Check remote folder', self) cmd = 'd=0;' # path doesn't exist. set d=1 to indicate cmd += 'test -e "%s" || d=1;' % self.path # create path, get errorcode from mkdir cmd += 'test $d -eq 1 && mkdir "%s"; err=$?;' % self.path # return errorcode from mkdir cmd += 'test $d -eq 1 && exit $err;' # path is no directory cmd += 'test -d "%s" || exit 11;' % self.path # path is not writable cmd += 'test -w "%s" || exit 12;' % self.path # path is not executable cmd += 'test -x "%s" || exit 13;' % self.path # everything is fine cmd += 'exit 20' ssh = self.config.sshCommand( cmd=[cmd], custom_args=['-p', str(self.port), self.user_host], port=False, user_host=False, nice=False, ionice=False, profile_id=self.profile_id) logger.debug('Call command: %s' % ' '.join(ssh), self) proc = subprocess.Popen(ssh, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) proc.communicate() if proc.returncode: logger.debug('Command returncode: %s' % proc.returncode, self) if proc.returncode == 20: # clean exit pass elif proc.returncode == 11: raise MountException('{}:\n{}'.format( _('Remote path exists but is not a directory.'), self.path)) elif proc.returncode == 12: raise MountException('{}:\n{}'.format( _('Remote path is not writable.'), self.path)) elif proc.returncode == 13: raise MountException('{}:\n{}'.format( _('Remote path is not executable.'), self.path)) else: raise MountException('{}:\n{}'.format( _("Couldn't create remote path."), self.path)) else: # returncode is 0 logger.info('Create remote folder %s' % self.path, self) def checkPingHost(self): """ Check if the remote host is online. Other than methods name may let suppose this does not use Ping (``ICMP``) but try to open a connection to the configured port on the remote host. In this way it will even work on remote hosts which have ``ICMP`` disabled. If connection failed it will retry five times before failing. Raises: exceptions.MountException: if connection failed most probably because remote host is offline """ if not self.config.sshCheckPingHost(self.profile_id): return if self.proxy_host: ping_host = self.proxy_host ping_port = self.proxy_port else: ping_host = self.host ping_port = self.port logger.debug(f'Check ping host "{ping_host}:{ping_port}"', self) versionString = 'SSH-2.0-backintime_{}\r\n'.format( version.__version__).encode() count = 0 while count < 5: try: with socket.create_connection( (ping_host, ping_port), 2.0) as s: result = s.connect_ex(s.getpeername()) s.sendall(versionString) except: # Refactor: too broad exception result = -1 if result == 0: logger.debug( f'Host "{ping_host}:{ping_port}" is available', self) return count += 1 sleep(0.2) if result != 0: proxy_msg = f' via proxy "{ping_host}:{ping_port}"' \ if self.proxy_host else '' msg = f'Ping {self.host}{proxy_msg} failed. ' \ 'Host is down or wrong address.' logger.debug(msg, self) raise MountException(msg) def checkRemoteCommands(self, retry=False): """ Try out all relevant commands used by `Back In Time` on the remote host to make sure snapshots will be successful with the remote host. This will also check that hard-links are supported on the remote host. This check can be disabled with :py:func:`config.Config.sshCheckCommands` Args: retry (bool): retry to run the commands if it failed because the command string was to long Raises: exceptions.MountException: if a command is not supported on remote host or if hard-links are not supported """ if not self.config.sshCheckCommands(): return logger.debug('Check remote commands', self) def maxArg(): if retry: raise MountException( "Checking commands on remote host didn't return any " "output. We already checked the maximum argument length " "but it seems like there is another problem") logger.warning( 'Looks like the command was to long for remote SSHd. ' 'We will test max arg length now and retry.', self) import sshMaxArg max_arg_size = sshMaxArg.probe_max_ssh_command_size(self.config) sshMaxArg.report_result(self.host, max_arg_size) self.config.setSshMaxArgLength(max_arg_size, self.profile_id) return self.checkRemoteCommands(retry=True) remote_tmp_dir_1 = os.path.join(self.path, 'tmp_%s' % self.randomId()) remote_tmp_dir_2 = os.path.join(self.path, 'tmp_%s' % self.randomId()) with tempfile.TemporaryDirectory() as tmp: tmp_file = os.path.join(tmp, 'a') with open(tmp_file, 'wt') as f: f.write('foo') # check rsync rsync1 = tools.rsyncPrefix( self.config, no_perms=False, progress=False) rsync1.append(tmp_file) rsync1.append('%s@%s:%s/' % ( self.user, tools.escapeIPv6Address(self.host), remote_tmp_dir_1)) # check remote rsync hard-link support rsync2 = tools.rsyncPrefix( self.config, no_perms=False, progress=False) rsync2.append( '--link-dest=../%s' % os.path.basename(remote_tmp_dir_1)) rsync2.append(tmp_file) rsync2.append('%s@%s:%s/' % ( self.user, tools.escapeIPv6Address(self.host), remote_tmp_dir_2)) for cmd in (rsync1, rsync2): logger.debug('Check rsync command: %s' % cmd, self) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) out, err = proc.communicate() if err or proc.returncode: logger.debug(f'rsync command returned error: {err}', self) raise MountException( "Remote host {host} doesn't support '{command}:\n" "{err}\n" "Look at 'man backintime' for further instructions." .format( host=self.host, command=cmd, err=err) ) # check cp chmod find and rm head = 'tmp1="%s"; tmp2="%s"; ' % (remote_tmp_dir_1, remote_tmp_dir_2) # first define a function to clean up and exit head += 'cleanup(){ ' head += 'test -e "$tmp1/a" && rm "$tmp1/a" >/dev/null 2>&1; ' head += 'test -e "$tmp2/a" && rm "$tmp2/a" >/dev/null 2>&1; ' head += 'test -e smr.lock && rm smr.lock >/dev/null 2>&1; ' head += 'test -e "$tmp1" && rmdir "$tmp1" >/dev/null 2>&1; ' head += 'test -e "$tmp2" && rmdir "$tmp2" >/dev/null 2>&1; ' head += 'test -n "$tmp3" && test -e "$tmp3" && rmdir "$tmp3" >/dev/null 2>&1; ' head += 'exit $1; }; ' tail = [] # list inodes cmd = 'ls -i "$tmp1/a"; ls -i "$tmp2/a"; ' tail.append(cmd) # try nice -n 19 if self.nice: cmd = 'echo \"nice -n 19\"; nice -n 19 true >/dev/null; err_nice=$?; ' cmd += 'test $err_nice -ne 0 && cleanup $err_nice; ' tail.append(cmd) # try ionice -c2 -n7 if self.ionice: cmd = 'echo \"ionice -c2 -n7\"; ionice -c2 -n7 true >/dev/null; err_nice=$?; ' cmd += 'test $err_nice -ne 0 && cleanup $err_nice; ' tail.append(cmd) # try nocache if self.nocache: cmd = 'echo \"nocache\"; nocache true >/dev/null; err_nocache=$?; ' cmd += 'test $err_nocache -ne 0 && cleanup $err_nocache; ' tail.append(cmd) # try screen, bash and flock used by smart-remove running in background if self.config.smartRemoveRunRemoteInBackground(self.profile_id): cmd = 'echo \"screen -d -m bash -c ...\"; screen -d -m bash -c \"true\" >/dev/null; err_screen=$?; ' cmd += 'test $err_screen -ne 0 && cleanup $err_screen; ' tail.append(cmd) cmd = 'echo \"(flock -x 9) 9>smr.lock\"; bash -c \"(flock -x 9) 9>smr.lock\" >/dev/null; err_flock=$?; ' cmd += 'test $err_flock -ne 0 && cleanup $err_flock; ' tail.append(cmd) cmd = 'echo \"rmdir \\$(mktemp -d)\"; tmp3=$(mktemp -d); test -z "$tmp3" && cleanup 1; rmdir $tmp3 >/dev/null; err_rmdir=$?; ' cmd += 'test $err_rmdir -ne 0 && cleanup $err_rmdir; ' tail.append(cmd) # if we end up here, everything should be fine cmd = 'echo \"done\"; cleanup 0' tail.append(cmd) maxLength = self.config.sshMaxArgLength(self.profile_id) additionalChars = len('echo ""') \ + len(self.config.sshPrefixCmd(self.profile_id, cmd_type=str)) output = '' err = '' returncode = 0 for cmd in tools.splitCommands(tail, head=head, maxLength=maxLength-additionalChars): if cmd.endswith('; '): cmd += 'echo ""' c = self.config.sshCommand( cmd=[cmd], custom_args=['-p', str(self.port), self.user_host], port=False, user_host=False, nice=False, ionice=False, profile_id=self.profile_id) try: logger.debug('Call command: %s' % ' '.join(c), self) proc = subprocess.Popen(c, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) ret = proc.communicate() except OSError as e: # Argument list too long if e.errno == 7: logger.debug( 'Argument list too long (Python exception)', self) return maxArg() else: raise logger.debug('Command stdout: %s' % ret[0], self) logger.debug('Command stderr: %s' % ret[1], self) logger.debug('Command returncode: %s' % proc.returncode, self) output += ret[0].strip('\n') + '\n' err += ret[1].strip('\n') + '\n' returncode += proc.returncode if proc.returncode: break output_split = output.strip('\n').split('\n') while True: if output_split and not output_split[-1]: output_split = output_split[:-1] else: break if not output_split: return maxArg() if returncode or not output_split[-1].startswith('done'): for command in ('rm', 'nice', 'ionice', 'nocache', 'screen', '(flock'): if output_split[-1].startswith(command): command = f"'{output_split[-1]}':\n{err}" msg = _("Remote host {host} doesn't support {command}") \ .format(host=self.host, command=command) raise MountException('{}\n{}'.format( msg, _("Look at 'man backintime' for further instructions") ) ) msg = _('Check commands on host {host} returned unknown error') \ .format(host=self.host) raise MountException('{}:\n{}n{}'.format( msg, err, _("Look at 'man backintime' for further instructions"))) inodes = [] for tmp in (remote_tmp_dir_1, remote_tmp_dir_2): for line in output_split: m = re.match(r'^(\d+).*?%s' % tmp, line) if m: inodes.append(m.group(1)) logger.debug('remote inodes: ' + ' | '.join(inodes), self) if len(inodes) == 2 and inodes[0] != inodes[1]: raise MountException( _("Remote host {host} doesn't support hardlinks") .format(host=self.host)) def randomId(self, size=6, chars=string.ascii_uppercase + string.digits): """ Create a random string. Args: size (int): length of the string chars (str): characters used as basis for the random string Returns: str: random string with length ``size`` """ return ''.join(random.choice(chars) for x in range(size)) def sshKeyGen(keyfile): """ Generate a new ssh-key pair (private and public key) in ``keyfile`` and ``keyfile``.pub Args: keyfile (str): path for private key file Returns: bool: True if successful; False if ``keyfile`` already exist or if there was an error """ if os.path.exists(keyfile): logger.warning( 'SSH keyfile "{}" already exist. Skip creating a new one' .format(keyfile)) return False cmd = ['ssh-keygen', '-t', 'rsa', '-N', '', '-f', keyfile] proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, universal_newlines=True) out, err = proc.communicate() if proc.returncode: logger.error('Failed to create a new ssh-key: {}'.format(err)) else: logger.info('Successfully created new ssh-key "{}"'.format(keyfile)) return not proc.returncode def sshCopyIdCommand( pubkey, user, host, port='22', proxy_user=None, proxy_host=None, proxy_port='22', cipher=None ): """ Generate a ssh-copy-id command to copy the given public ssh-key to a remote host. Args: pubkey (str): path to the public key file user (str): remote user host (str): remote host port (str): ssh port on remote host proxy_user (str): proxy host user proxy_host (str): proxy host proxy_port (str): proxy host port cipher (str): cipher used for ssh Returns: list: The ssh-copy-id command as a list. Raises: FileNotFoundError: If public key file not exist. """ if not Path(pubkey).exists(): msg = f'SSH public key "{pubkey}" does not exist.' logger.error(msg) raise FileNotFoundError(msg) cmd = [ 'ssh-copy-id', # key file '-i', pubkey, # port '-p', str(port), ] # cipher if cipher and cipher != 'default': cmd.extend(['-o', 'Ciphers={}'.format(cipher)]) # proxy if proxy_host: proxy_jump = f'{proxy_user}@{proxy_host}:{proxy_port}' cmd.extend(['-o', f'ProxyJump={proxy_jump}']) cmd.append(f'{user}@{host}') logger.debug(f'ssh-copy-id command {cmd}') return cmd def sshCopyId( pubkey, user, host, port='22', proxy_user=None, proxy_host=None, proxy_port=None, askPass='backintime-askpass', cipher=None ): """ Copy SSH public key ``pubkey`` to remote ``host``. Args: pubkey (str): path to the public key file user (str): remote user host (str): remote host port (str): ssh port on remote host askPass (str): program used to pipe password into ssh cipher (str): cipher used for ssh Returns: bool: True if successful """ cmd = sshCopyIdCommand( pubkey, user, host, port, proxy_user, proxy_host, proxy_port, cipher ) env = os.environ.copy() env['SSH_ASKPASS'] = askPass env['ASKPASS_MODE'] = 'USER' env['ASKPASS_PROMPT'] = '{}\n{}:'.format( _('Copy public ssh-key "{pubkey}" to remote host "{host}".').format( pubkey=pubkey, host=host), _('Please enter a password for "{user}".').format(user=user) ) proc = subprocess.Popen(cmd, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, preexec_fn=os.setsid, # cut of ssh from current # terminal to make it use # backintime-askpass universal_newlines=True) out, err = proc.communicate() if proc.returncode: logger.error('Failed to copy ssh-key "{}" to "{}@{}": [{}] {}' .format(pubkey, user, host, proc.returncode, err)) else: logger.info('Successfully copied ssh-key "{}" to "{}@{}"' .format(pubkey, user, host)) return not proc.returncode def sshKeyFingerprint(path): """ Get the hex fingerprint from a given ssh key. Args: path (str): full path to key file Returns: str: hex fingerprint from key """ if not os.path.exists(path): return cmd = ['ssh-keygen', '-l', '-f', path] proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) output = proc.communicate()[0] m = re.match(r'\d+\s+(SHA256:\S+|[a-fA-F0-9:]+)\s.*', output.decode()) if m: return m.group(1) def sshHostKey(host, port='22'): """ Get the remote host key from ``host``. Args: host (str): host name or IP address port (str): port number of remote ssh-server Returns: tuple: three item tuple with (fingerprint, hashed host key, key type) """ for t in ('ecdsa', 'rsa'): cmd = ['ssh-keyscan', '-t', t, '-p', port, host] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) hostKey = proc.communicate()[0].strip() if hostKey: break if hostKey: logger.debug('Found {} key for host "{}"'.format(t.upper(), host)) with tempfile.TemporaryDirectory() as tmp: keyFile = os.path.join(tmp, 'key') with open(keyFile, 'wb') as f: f.write(hostKey + b'\n') hostKeyFingerprint = sshKeyFingerprint(keyFile) cmd = ['ssh-keygen', '-H', '-f', keyFile] proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) proc.communicate() with open(keyFile, 'rt') as f: hostKeyHash = f.read().strip() return (hostKeyFingerprint, hostKeyHash, t.upper()) return (None, None, None) def writeKnownHostsFile(key): """ Write host key ``key`` into `~/.ssh/known_hosts`. Args: key (str): host key """ sshDir = os.path.expanduser('~/.ssh') knownHostFile = os.path.join(sshDir, 'known_hosts') if not os.path.isdir(sshDir): tools.mkdir(sshDir, 0o700) with open(knownHostFile, 'at') as f: logger.info('Write host key to {}'.format(knownHostFile)) f.write(key + '\n') backintime-1.5.2/common/test/000077500000000000000000000000001465446530500161105ustar00rootroot00000000000000backintime-1.5.2/common/test/__init__.py000066400000000000000000000000001465446530500202070ustar00rootroot00000000000000backintime-1.5.2/common/test/config000066400000000000000000000012671465446530500173060ustar00rootroot00000000000000config.version=6 profile1.snapshots.include.1.type=0 profile1.snapshots.include.1.value=/tmp/test profile1.snapshots.include.size=1 profile1.snapshots.no_on_battery=false profile1.snapshots.notify.enabled=true profile1.snapshots.path=/tmp/snapshots profile1.snapshots.path.host=test-host profile1.snapshots.path.profile=1 profile1.snapshots.path.user=test-user profile1.snapshots.preserve_acl=false profile1.snapshots.preserve_xattr=false profile1.snapshots.remove_old_snapshots.enabled=true profile1.snapshots.remove_old_snapshots.unit=80 profile1.snapshots.remove_old_snapshots.value=10 profile1.snapshots.rsync_options.enabled=false profile1.snapshots.rsync_options.value= profiles.version=1 backintime-1.5.2/common/test/dummy_test_process.sh000077500000000000000000000015351465446530500224030ustar00rootroot00000000000000#!/bin/sh # Back In Time # Copyright (C) 2016-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. # # 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 is just a test dummy to simulate a running process while true; do sleep 2 done backintime-1.5.2/common/test/generic.py000066400000000000000000000305241465446530500201020ustar00rootroot00000000000000# Back In Time # Copyright (C) 2016-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. # # 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 module offers some helpers and tools for unittesting. Most of the content are `unittest.TestCase` derived classed doing basic setup for the testing environment. They are dealing with snapshot path's, SSH, config files and things like set. """ import os import pathlib import sys import unittest import socket from unittest.mock import patch from tempfile import TemporaryDirectory, NamedTemporaryFile from contextlib import contextmanager sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import logger import tools # Needed because backintime.startApp() is not invoked. tools.initiate_translation(None) import config import snapshots # mock notifyplugin to suppress notifications tools.registerBackintimePath('qt', 'plugins') TMP_FLOCK = NamedTemporaryFile(prefix='backintime', suffix='.flock') # A simple (local) RSA key pair via "ssh-keygen" and activate it # via "ssh-copy-id localhost". PRIV_KEY_FILE = pathlib.Path.home() / '.ssh' / 'id_rsa' PUBLIC_KEY_FILE = PRIV_KEY_FILE.with_suffix('.pub') AUTHORIZED_KEYS_FILE = pathlib.Path.home() / '.ssh' / 'authorized_keys' DUMMY = 'dummy_test_process.sh' if all([PUBLIC_KEY_FILE.exists(), AUTHORIZED_KEYS_FILE.exists()]): with PUBLIC_KEY_FILE.open('rb') as pub: with AUTHORIZED_KEYS_FILE.open('rb') as auth: KEY_IN_AUTH = pub.read() in auth.readlines() else: KEY_IN_AUTH = False # check if port 22 on localhost is available # sshd should be there... try: with socket.create_connection(('localhost', '22'), 2.0) as s: sshdPortAvailable = not bool(s.connect_ex(s.getpeername())) except ConnectionRefusedError: sshdPortAvailable = False SKIP_SSH_TEST_MESSAGE = 'Skip as this test requires a local ssh server, ' \ 'public and private keys installed' LOCAL_SSH = all([ # Server process running? tools.processExists('sshd'), # Privat keyfile (id_rsa) PRIV_KEY_FILE.is_file(), # Key known (copied via "ssh-copy-id") KEY_IN_AUTH, # SSH port (22) available at the server sshdPortAvailable ]) ON_TRAVIS = os.environ.get('TRAVIS', 'None').lower() == 'true' ON_RTD = os.environ.get('READTHEDOCS', 'None').lower() == 'true' # Temporary workaround (buhtz: 2023-09) # Not all components of the code are able to handle Path objects PRIV_KEY_FILE = str(PRIV_KEY_FILE) PUBLIC_KEY_FILE = str(PUBLIC_KEY_FILE) AUTHORIZED_KEYS_FILE = str(AUTHORIZED_KEYS_FILE) class TestCase(unittest.TestCase): """Base class for Back In Time unit- and integration testing. In summary following is set via 'setUp()' and '__init__()': - Initialize logging. - Set path to config file (not open it). - Set path the "backup source" directory. """ def __init__(self, methodName): """Initialize logging and set the path of the config file. The config file in the "test" folder is used. Args: methodName: Unknown. """ # note by buhtz: This is not recommended. Unittest module handle # that itself. The default locale while unittesting is "C". # Need further investigation. os.environ['LANGUAGE'] = 'en_US.UTF-8' # Path to config file (in "common/test/config") self.cfgFile = os.path.abspath( os.path.join(__file__, os.pardir, 'config')) # Initialize logging logger.APP_NAME = 'BIT_unittest' logger.openlog() super(TestCase, self).__init__(methodName) def setUp(self): """ """ logger.DEBUG = '-v' in sys.argv # ? self.run = False # Not sure but this could be the "backup source" directory self.sharePathObj = TemporaryDirectory() self.sharePath = self.sharePathObj.name def tearDown(self): """ """ # BUHTZ 10/09/2022: In my understanding it is not needed and would be # done implicitly when the test class is destroyed. self.sharePathObj.cleanup() def callback(self, func, *args): """ """ func(*args) self.run = True # the next six assert methods are deprecated and can be replaced # by using Pythons in-build "pathlib.Path" def assertExists(self, *path): full_path = os.path.join(*path) if not os.path.exists(full_path): self.fail('File does not exist: {}'.format(full_path)) def assertNotExists(self, *path): full_path = os.path.join(*path) if os.path.exists(full_path): self.fail('File does unexpected exist: {}'.format(full_path)) def assertIsFile(self, *path): full_path = os.path.join(*path) if not os.path.isfile(full_path): self.fail('Not a File: {}'.format(full_path)) def assertIsNoFile(self, *path): full_path = os.path.join(*path) if os.path.isfile(full_path): self.fail('Unexpected File: {}'.format(full_path)) def assertIsDir(self, *path): full_path = os.path.join(*path) if not os.path.isdir(full_path): self.fail('Not a directory: {}'.format(full_path)) def assertIsLink(self, *path): full_path = os.path.join(*path) if not os.path.islink(full_path): self.fail('Not a symlink: {}'.format(full_path)) class TestCaseCfg(TestCase): """Testing base class opening the config file, creating the config instance and starting the notify plugin. The path to the config file was set by the inherited class :py:class:`generic.TestCase`. """ def setUp(self): super(TestCaseCfg, self).setUp() self.cfg = config.Config(self.cfgFile, self.sharePath) # mock notifyplugin to suppress notifications patcher = patch('notifyplugin.NotifyPlugin.message') self.mockNotifyPlugin = patcher.start() self.cfg.PLUGIN_MANAGER.load() class TestCaseSnapshotPath(TestCaseCfg): """Testing base class for snapshot test cases. It setup a temporary directory as the root for all snapshots. """ def setUp(self): """ """ super(TestCaseSnapshotPath, self).setUp() # The root of all snapshots. Like a "backup destination". # e.g. '/tmp/tmpf3mdnt8l' self.tmpDir = TemporaryDirectory() self.cfg.dict['profile1.snapshots.path'] = self.tmpDir.name # The full snapshot path combines the backup destination root # directory with hostname, username and the profile (backupjob) ID. # e.g. /tmp/tmpf3mdnt8l/backintime/test-host/test-user/1 self.snapshotPath = self.cfg.snapshotsFullPath() def tearDown(self): """ """ super(TestCaseSnapshotPath, self).tearDown() self.tmpDir.cleanup() class SnapshotsTestCase(TestCaseSnapshotPath): """Testing base class for snapshot testing unittest classes. Create the snapshot path and a :py:class:`Snapshot` instance of it. """ def setUp(self): """ """ super(SnapshotsTestCase, self).setUp() # e.g. /tmp/tmpf3mdnt8l/backintime/test-host/test-user/1 os.makedirs(self.snapshotPath) self.sn = snapshots.Snapshots(self.cfg) # use a tmp-file for flock because test_flockExclusive would deadlock # otherwise if a regular snapshot is running in background self.sn.GLOBAL_FLOCK = TMP_FLOCK.name class SnapshotsWithSidTestCase(SnapshotsTestCase): """Testing base class creating a concrete SID object. Backup content (folder and file) is created in that snapshot like the snapshot was taken in the past. """ def setUp(self): """ """ super(SnapshotsWithSidTestCase, self).setUp() # A snapthos "data object" self.sid = snapshots.SID('20151219-010324-123', self.cfg) # Create test files and folders # e.g. /tmp/tmp9rstvbsx/backintime/test-host/test-user/1/ # 20151219-010324-123/backup/foo/bar # 20151219-010324-123/backup/foo/bar/baz self.sid.makeDirs() self.testDir = 'foo/bar' self.testDirFullPath = self.sid.pathBackup(self.testDir) self.testFile = 'foo/bar/baz' self.testFileFullPath = self.sid.pathBackup(self.testFile) self.sid.makeDirs(self.testDir) with open(self.sid.pathBackup(self.testFile), 'wt'): pass class SSHTestCase(TestCaseCfg): """Base class for test cases using the SSH features. Running this test requires that user has public / private key pair created and SSH server (at localhost) running. """ def setUp(self): """ """ super(SSHTestCase, self).setUp() logger.DEBUG = '-v' in sys.argv self.cfg.setSnapshotsMode('ssh') self.cfg.setSshHost('localhost') self.cfg.setSshPrivateKeyFile(PRIV_KEY_FILE) # use a TemporaryDirectory for remote snapshot path # self.tmpDir = TemporaryDirectory(prefix='bit_test_', suffix=' with blank') self.tmpDir = TemporaryDirectory() self.remotePath = os.path.join(self.tmpDir.name, 'foo') self.remoteFullPath = os.path.join( self.remotePath, 'backintime', *self.cfg.hostUserProfile()) self.cfg.setSshSnapshotsPath(self.remotePath) self.mount_kwargs = {} def tearDown(self): """ """ super(SSHTestCase, self).tearDown() self.tmpDir.cleanup() class SSHSnapshotTestCase(SSHTestCase): """Testing base class for test cases using a snapshot and SSH. BUHTZ 2022-10-09: Seems exactly the same then `SnapshotsTestCase` except the inheriting class. """ def setUp(self): """ """ super(SSHSnapshotTestCase, self).setUp() self.snapshotPath = self.cfg.sshSnapshotsFullPath() os.makedirs(self.snapshotPath) self.sn = snapshots.Snapshots(self.cfg) # use a tmp-file for flock because test_flockExclusive would deadlock # otherwise if a regular snapshot is running in background self.sn.GLOBAL_FLOCK = TMP_FLOCK.name class SSHSnapshotsWithSidTestCase(SSHSnapshotTestCase): """Testing base class for test cases using an existing snapshot (SID) and SSH. BUHTZ 2022-10-09: Seems exactly the same then `SnapshotsWithSidTestCase` except the inheriting class. """ def setUp(self): """ """ super(SSHSnapshotsWithSidTestCase, self).setUp() self.sid = snapshots.SID('20151219-010324-123', self.cfg) self.remoteSIDBackupPath = os.path.join( self.snapshotPath, self.sid.sid, 'backup') os.makedirs(self.remoteSIDBackupPath) self.testDir = 'foo/bar' self.testDirFullPath = os.path.join( self.remoteSIDBackupPath, self.testDir) self.testFile = 'foo/bar/baz' self.testFileFullPath = os.path.join( self.remoteSIDBackupPath, self.testFile) os.makedirs(self.testDirFullPath) with open(self.testFileFullPath, 'wt'): pass def create_test_files(path): """Create folders and files. Args: path: Destination folder. That is the resulting folder structure :: foo └── bar ├── baz ├── file with spaces └── test """ os.makedirs(os.path.join(path, 'foo', 'bar')) with open(os.path.join(path, 'foo', 'bar', 'baz'), 'wt') as f: f.write('foo') with open(os.path.join(path, 'test'), 'wt') as f: f.write('bar') with open(os.path.join(path, 'file with spaces'), 'wt') as f: f.write('asdf') @contextmanager def mockPermissions(path, mode=0o000): """ """ st = os.stat(path) os.chmod(path, mode) yield # fix permissions so it can be removed os.chmod(path, st.st_mode) backintime-1.5.2/common/test/mock_askpass000077500000000000000000000000301465446530500205050ustar00rootroot00000000000000#!/bin/sh echo "travis" backintime-1.5.2/common/test/test_applicationinstance.py000066400000000000000000000252151465446530500235560ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 subprocess import os import sys import unittest from unittest.mock import patch from threading import Thread from test import generic sys.path.append(os.path.join(os.path.dirname(__file__), "..")) from applicationinstance import ApplicationInstance import tools class TestApplicationInstance(generic.TestCase): """ """ def setUp(self): """Preparing unittests including the instantiation of an ``ÀpplicationInstance``. """ super(TestApplicationInstance, self).setUp() self.temp_file = '/tmp/temp.txt' self.file_name = "/tmp/file_with_pid" self.app_instance = ApplicationInstance( pidFile=os.path.abspath(self.file_name), autoExit=False) self.subproc = None def tearDown(self): """Delete temporary files and kill subprocesses. """ super(TestApplicationInstance, self).tearDown() for f in (self.temp_file, self.file_name): if os.path.exists(f): os.remove(f) self._killProcess() def _createProcess(self): """Start a shell script and return ins PID.""" # path to the shell script dummyPath = os.path.join(os.path.dirname(__file__), generic.DUMMY) self.subproc = subprocess.Popen(dummyPath) return self.subproc.pid def _killProcess(self): if self.subproc: self.subproc.kill() self.subproc.wait() self.subproc = None def test_create_and_remove_pid_file(self): # create pid file self.app_instance.startApplication() self.assertIsFile(self.file_name) # remove pid file self.app_instance.exitApplication() self.assertIsNoFile(self.file_name) def test_pid_file_content(self): """Content of pid file fits to current process.""" self.app_instance.startApplication() this_pid = os.getpid() this_procname = tools.processName(this_pid) expected_file_content = f'{this_pid}\n{this_procname}' with open(self.file_name, 'rt') as file_with_pid: pid_file_content = file_with_pid.read() self.assertEqual(pid_file_content, expected_file_content) @patch('builtins.open') def test_write_pid_fail(self, mock_open): """The test is not clear. Because of the OSError() a log error will be generated. But this isn't tested here. I assume the expected behavior is just that nothing bad happens because the OSError was caught. """ mock_open.side_effect = OSError() self.app_instance.startApplication() def test_existing_process_with_correct_procname(self): """ Test the check function with an existing process with correct process name """ pid = self._createProcess() procname = tools.processName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) # Execute test self.assertFalse(self.app_instance.check()) self.assertTrue(self.app_instance.busy()) def test_existing_process_with_correct_proc_cmdline(self): """ Test the check function with an existing process with correct process cmdline (for backwards compatibility) """ # start an extern shell script pid = self._createProcess() procname = tools.processCmdline(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) # Execute test self.assertFalse(self.app_instance.check()) def test_no_pid_file(self): self.assertTrue(self.app_instance.check()) def test_existing_process_with_wrong_procname(self): """ Test the check function with an existing process with wrong process name """ pid = self._createProcess() procname = tools.processName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname + "DELETE") # Execute test self.assertTrue(self.app_instance.check()) def test_existing_process_with_wrong_pid(self): """ Test the check function with an existing process with wrong pid """ pid = self._createProcess() procname = tools.processName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write("987654321\n") file_with_pid.write(procname) # Execute test self.assertTrue(self.app_instance.check()) def test_killing_existing_process(self): """ Test the check function with an existing process with correct process name """ pid = self._createProcess() procname = tools.processName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) self.assertFalse(self.app_instance.check()) self._killProcess() # Execute test self.assertTrue(self.app_instance.check()) def test_non_existing_process(self): """ Test the check function with a non existing process """ # GIVE # # create file with fake pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write("987654321\n") file_with_pid.write("FAKE_PROCNAME") # Execute test self.assertTrue(self.app_instance.check()) def test_leftover_empty_lockfile(self): with open(self.file_name, 'wt')as f: pass self.assertTrue(self.app_instance.check()) def write_after_flock(self, pid_file): inst = ApplicationInstance(os.path.abspath(pid_file), autoExit = False, flock = True) with open(self.temp_file, 'wt') as f: f.write('foo') inst.flockUnlock() def test_thread_write_without_flock(self): thread = Thread(target = self.write_after_flock, args = (self.file_name,)) thread.start() #wait for the thread to finish thread.join() self.assertExists(self.temp_file) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo') def test_flock_exclusive(self): self.app_instance.flockExclusiv() thread = Thread(target = self.write_after_flock, args = (self.file_name,)) thread.start() #give the thread some time thread.join(0.01) self.assertNotExists(self.temp_file) self.app_instance.flockUnlock() #wait for the thread to finish thread.join() self.assertExists(self.temp_file) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo') @patch('builtins.open') def test_flock_exclusive_fail(self, mock_open): """Dev note (buhtz: 2023-09) Nothing is tested here. Looking into flockExclusive() there is only the opportunity to test for ERROR log output. Log output shouldn't be tested. The behavior to test is unclear. Proposal: - Remove the test. - Refactor flockExclusive() and related code to make its behavior clear. """ mock_open.side_effect = OSError() self.app_instance.flockExclusiv() def test_auto_flock(self): self.app_instance = ApplicationInstance(os.path.abspath(self.file_name), autoExit = False, flock = True) thread = Thread(target = self.write_after_flock, args = (self.file_name,)) thread.start() #give the thread some time thread.join(0.01) self.assertNotExists(self.temp_file) self.app_instance.startApplication() #wait for the thread to finish thread.join() self.assertExists(self.temp_file) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo') def test_autoExit_unique_process(self): self.app_instance = ApplicationInstance(os.path.abspath(self.file_name), autoExit = True) self.assertExists(self.file_name) this_pid = os.getpid() this_procname = tools.processName(this_pid) with open(self.file_name, 'rt') as file_with_pid: self.assertEqual(file_with_pid.read(), '{}\n{}'.format(this_pid, this_procname)) def test_autoExit_other_running_process(self): pid = self._createProcess() procname = tools.processName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) with self.assertRaises(SystemExit): self.app_instance = ApplicationInstance(os.path.abspath(self.file_name), autoExit = True) def test_readPidFile(self): with open(self.file_name, "wt") as f: f.write('123\nfoo') self.assertEqual(self.app_instance.readPidFile(), (123, 'foo')) # ValueError with open(self.file_name, "wt") as f: f.write('foo\nbar') self.assertEqual(self.app_instance.readPidFile(), (0, 'bar')) @patch('builtins.open') def test_readPidFile_fail(self, mock_open): mock_open.side_effect = OSError() self.assertEqual(self.app_instance.readPidFile(), (0, '')) backintime-1.5.2/common/test/test_argparser.py000066400000000000000000000244451465446530500215200ustar00rootroot00000000000000# Copyright (C) 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. # # 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 unittest import os import sys import itertools from test import generic sys.path.append(os.path.join(os.path.dirname(__file__), "..")) import backintime def shuffleArgs(*args): """ Return every possible combination of arguments. Those arguments which need to keep in line have to be inside a tuple. args: two or more arguments (str) """ for i in itertools.permutations(args): ret = [] for j in i: if isinstance(j, (tuple, list)): ret.extend(j) else: ret.append(j) yield ret class TestArgParser(generic.TestCase): def setUp(self): super(TestArgParser, self).setUp() backintime.createParsers() def tearDown(self): super(TestArgParser, self).tearDown() global parsers parsers = {} def test_invalid_arg(self): with self.assertRaises(SystemExit): backintime.argParse(['not_existing_command']) with self.assertRaises(SystemExit): backintime.argParse(['--not_existing_argument']) def test_config(self): args = backintime.argParse(['--config', '/tmp/config']) self.assertIn('config', args) self.assertEqual(args.config, '/tmp/config') def test_quiet(self): args = backintime.argParse(['--quiet',]) self.assertIn('quiet', args) self.assertTrue(args.quiet) def test_debug(self): args = backintime.argParse(['--debug',]) self.assertIn('debug', args) self.assertTrue(args.debug) def test_config_no_path(self): with self.assertRaises(SystemExit): backintime.argParse(['--config']) ############################################################################ ### Backup ### ############################################################################ def test_cmd_backup(self): args = backintime.argParse(['backup']) self.assertIn('command', args) self.assertEqual(args.command, 'backup') self.assertIn('func', args) self.assertIs(args.func, backintime.backup) def test_cmd_backup_backwards_compatiblity_alias(self): args = backintime.argParse(['--backup']) self.assertIn('func', args) self.assertIs(args.func, backintime.aliasParser) self.assertIn('replace', args) self.assertEqual(args.replace, '--backup') self.assertIn('alias', args) self.assertEqual(args.alias, 'backup') def test_cmd_backup_profile(self): for argv in shuffleArgs('backup', ('--profile', 'foo')): with self.subTest(argv = argv): #workaround for py.test3 2.5.1 doesn't support subTest msg = 'argv = %s' %argv args = backintime.argParse(argv) self.assertIn('command', args, msg) self.assertEqual(args.command, 'backup', msg) self.assertIn('profile', args, msg) self.assertEqual(args.profile, 'foo', msg) def test_cmd_backup_profile_id(self): args = backintime.argParse(['backup', '--profile-id', '2']) self.assertIn('command', args) self.assertEqual(args.command, 'backup') self.assertIn('profile_id', args) self.assertEqual(args.profile_id, 2) def test_cmd_backup_profile_and_profile_id(self): with self.assertRaises(SystemExit): backintime.argParse(['backup', '--profile', 'foo', '--profile-id', '2']) def test_cmd_backup_quiet(self): args = backintime.argParse(['backup', '--quiet']) self.assertIn('command', args) self.assertEqual(args.command, 'backup') self.assertIn('quiet', args) self.assertTrue(args.quiet) def test_cmd_backup_multi_args(self): for argv in shuffleArgs('--quiet', 'backup', ('--profile', 'foo'), '--checksum', ('--config', 'bar')): with self.subTest(argv = argv): #workaround for py.test3 2.5.1 doesn't support subTest msg = 'argv = %s' %argv args = backintime.argParse(argv) self.assertIn('command', args, msg) self.assertEqual(args.command, 'backup', msg) self.assertIn('profile', args, msg) self.assertEqual(args.profile, 'foo', msg) self.assertIn('quiet', args, msg) self.assertTrue(args.quiet, msg) self.assertIn('checksum', args, msg) self.assertTrue(args.checksum, msg) self.assertIn('config', args, msg) self.assertEqual(args.config, 'bar', msg) ############################################################################ ### Restore ### ############################################################################ def test_cmd_restore(self): args = backintime.argParse(['restore']) self.assertIn('command', args) self.assertEqual(args.command, 'restore') self.assertIn('func', args) self.assertIs(args.func, backintime.restore) def test_cmd_restore_what_where_snapshot_id(self): args = backintime.argParse(['restore', '/home', '/tmp', '20151130-230501-984']) self.assertIn('command', args) self.assertEqual(args.command, 'restore') self.assertIn('WHAT', args) self.assertEqual(args.WHAT, '/home') self.assertIn('WHERE', args) self.assertEqual(args.WHERE, '/tmp') self.assertIn('SNAPSHOT_ID', args) self.assertEqual(args.SNAPSHOT_ID, '20151130-230501-984') def test_cmd_restore_what_where_snapshot_id_multi_args(self): for argv in shuffleArgs('--quiet', ('restore', '/home', '/tmp', '20151130-230501-984'), '--checksum', ('--profile-id', '2'), '--local-backup', '--delete', ('--config', 'foo')): with self.subTest(argv = argv): #workaround for py.test3 2.5.1 doesn't support subTest msg = 'argv = %s' %argv args = backintime.argParse(argv) self.assertIn('quiet', args, msg) self.assertTrue(args.quiet, msg) self.assertIn('checksum', args, msg) self.assertTrue(args.checksum, msg) self.assertIn('profile_id', args, msg) self.assertEqual(args.profile_id, 2, msg) self.assertIn('command', args, msg) self.assertEqual(args.command, 'restore', msg) self.assertIn('WHAT', args, msg) self.assertEqual(args.WHAT, '/home', msg) self.assertIn('WHERE', args, msg) self.assertEqual(args.WHERE, '/tmp', msg) self.assertIn('SNAPSHOT_ID', args, msg) self.assertEqual(args.SNAPSHOT_ID, '20151130-230501-984', msg) self.assertIn('local_backup', args, msg) self.assertTrue(args.local_backup, msg) self.assertIn('delete', args, msg) self.assertTrue(args.delete, msg) self.assertIn('config', args, msg) self.assertEqual(args.config, 'foo', msg) def test_cmd_restore_multi_args(self): for argv in shuffleArgs(('--profile-id', '2'), '--quiet', 'restore', '--checksum', '--local-backup', '--delete', ('--config', 'foo')): with self.subTest(argv = argv): #workaround for py.test3 2.5.1 doesn't support subTest msg = 'argv = %s' %argv args = backintime.argParse(argv) self.assertIn('quiet', args, msg) self.assertTrue(args.quiet, msg) self.assertIn('checksum', args, msg) self.assertTrue(args.checksum, msg) self.assertIn('profile_id', args, msg) self.assertEqual(args.profile_id, 2, msg) self.assertIn('command', args, msg) self.assertEqual(args.command, 'restore', msg) self.assertIn('local_backup', args, msg) self.assertTrue(args.local_backup, msg) self.assertIn('delete', args, msg) self.assertTrue(args.delete, msg) self.assertIn('config', args, msg) self.assertEqual(args.config, 'foo', msg) def test_cmd_restore_snapshot_id_index(self): args = backintime.argParse(['restore', '/home', '/tmp', '1']) self.assertIn('SNAPSHOT_ID', args) self.assertEqual(args.SNAPSHOT_ID, '1') def test_cmd_restore_empty_where(self): args = backintime.argParse(['restore', '/home', '', '20151130-230501-984']) self.assertIn('WHERE', args) self.assertEqual(args.WHERE, '') def test_cmd_restore_where_space_in_path(self): args = backintime.argParse(['restore', '/home', '/tmp/foo bar/baz', '20151130-230501-984']) self.assertIn('WHERE', args) self.assertEqual(args.WHERE, '/tmp/foo bar/baz') def test_cmd_restore_what_space_in_path(self): args = backintime.argParse(['restore', '/home/foo bar/baz', '/tmp', '20151130-230501-984']) self.assertIn('WHAT', args) self.assertEqual(args.WHAT, '/home/foo bar/baz') def test_cmd_restore_local_backup_and_no_local_backup(self): with self.assertRaises(SystemExit): backintime.argParse(('restore', '--local-backup', '--no-local-backup')) if __name__ == '__main__': unittest.main() backintime-1.5.2/common/test/test_backintime.py000066400000000000000000000236601465446530500216360ustar00rootroot00000000000000# Back In Time # Copyright (C) 2016 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 re import subprocess import sys import unittest from test import generic import json import config import version sys.path.append(os.path.join(os.path.dirname(__file__), '..')) class TestBackInTime(generic.TestCase): def setUp(self): super(TestBackInTime, self).setUp() @unittest.skip("--quiet is broken due to some non-filtered logger output") def test_quiet_mode(self): output = subprocess.getoutput("python3 backintime.py --quiet") self.assertEqual("", output) def test_local_snapshot_is_successful(self): """From BIT initialization through snapshot From BIT initialization all the way through successful snapshot on a local mount. test one of the highest level interfaces a user could work with - the command line ensures that argument parsing, functionality, and output all work as expected is NOT intended to replace individual method tests, which are incredibly useful as well. Development notes (by Buhtz, 2023): Multiple tests do compare return codes and output on stdout. It is NOT tested what is on the file system. The intention might be a system test. But the asserts not qualified to answer the important questions and observe the intended behavior. Heavy refactoring is needed. But because of the "level" of that tests it won't happen in the near future. Also maintenance costs of this tests are damn high because every tiny modification of BIT gives a false fail of this test. Development notes (by Buhtz, 2024-05): It is just dumb stdout parsing. I tend to remove this test because of the calculation of its value and its maintenance costs. """ # ensure that we see full diffs of assert output if there are any self.maxDiff = None # create pristine source directory with single file subprocess.getoutput("chmod -R a+rwx /tmp/test && rm -rf /tmp/test") os.mkdir('/tmp/test') with open('/tmp/test/testfile', 'w') as f: f.write('some data') # create pristine snapshot directory subprocess.getoutput( "chmod -R a+rwx /tmp/snapshots && rm -rf /tmp/snapshots") os.mkdir('/tmp/snapshots') # remove restored directory subprocess.getoutput("rm -rf /tmp/restored") # install proper destination filesystem structure and verify output proc = subprocess.Popen(["./backintime", "--config", "test/config", "--share-path", self.sharePath, "check-config", # do not overwrite users crontab "--no-crontab"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, error = proc.communicate() msg = 'Returncode: {}\nstderr: {}\nstdout: {}' \ .format(proc.returncode, error.decode(), output.decode()) self.assertEqual(proc.returncode, 0, msg) self.assertRegex(output.decode(), re.compile(r''' Back In Time Version: \d+.\d+.\d+.* Back In Time comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions; type `backintime --license' for details. (INFO: Update to config version \d+ )? \+--------------------------------\+ | Check/prepare snapshot path | \+--------------------------------\+ Check/prepare snapshot path: done \+--------------------------------\+ | Check config | \+--------------------------------\+ Check config: done Config .*test/config profile 'Main profile' is fine.''', re.MULTILINE)) # execute backup and verify output proc = subprocess.Popen(["./backintime", "--config", "test/config", "--share-path", self.sharePath, "backup"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, error = proc.communicate() msg = 'Returncode: {}\nstderr: {}\nstdout: {}' \ .format(proc.returncode, error.decode(), output.decode()) self.assertEqual(proc.returncode, 0, msg) self.assertRegex(output.decode(), re.compile(r''' Back In Time Version: \d+.\d+.\d+.* Back In Time comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions; type `backintime --license' for details. ''', re.MULTILINE)) # Workaround until refactoring was done (Buhtz, Feb.'23) # The log output completely goes to stderr. # Note: DBus warnings at the begin and end are already ignored by the # regex but if the BiT serviceHelper.py DBus daemon is not # installed at all the warnings also occur in the middle of below # expected INFO log lines so they are removed by filtering here. # The same goes with Gtk warnings. line_beginnings_to_exclude = [ "WARNING", "Warning", ] # Warnings currently known: # - "WARNING: D-Bus message:" # - "WARNING: Udev-based profiles cannot be changed or checked" # - "WARNING: Inhibit Suspend failed" # - "Warning: Ignoring XDG_SESSION_TYPE=wayland on Gnome. Use # QT_QPA_PLATFORM=wayland to run on Wayland anyway" line_contains_to_exclude = [ "Gtk-WARNING", "qt.qpa.plugin: Could not find the Qt platform plugin", 'qt.dbus.integration: Could not connect "org.freedesktop.IBus" ' 'to globalEngineChanged(QString)', ] # remove lines via startswith() filtered_log_output = filter( lambda line: not any([ line.startswith(ex) for ex in line_beginnings_to_exclude]), error.decode().split('\n') ) # remove lines via __contains__() filtered_log_output = filter( lambda line: not any([ ex in line for ex in line_contains_to_exclude]), filtered_log_output ) # remove empty lines filtered_log_output = filter( lambda line: line, filtered_log_output ) filtered_log_output = '\n'.join(filtered_log_output) self.assertRegex(filtered_log_output, re.compile(r'''INFO: Lock INFO: Take a new snapshot. Profile: 1 Main profile INFO: Call rsync to take the snapshot INFO: Save config file INFO: Save permissions INFO: Unlock''', re.MULTILINE)) # get snapshot id subprocess.check_output(["./backintime", "--config", "test/config", "snapshots-list"]) # execute restore and verify output proc = subprocess.Popen(["./backintime", "--config", "test/config", "--share-path", self.sharePath, "restore", "/tmp/test/testfile", "/tmp/restored", "0"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, error = proc.communicate() msg = 'Returncode: {}\nstderr: {}\nstdout: {}' \ .format(proc.returncode, error.decode(), output.decode()) self.assertEqual(proc.returncode, 0, msg) self.assertRegex(output.decode(), re.compile(r''' Back In Time Version: \d+.\d+.\d+.* Back In Time comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions; type `backintime --license' for details. ''', re.MULTILINE)) # The log output completely goes to stderr self.assertRegex( error.decode(), re.compile( r'''INFO: Restore: /tmp/test/testfile to: /tmp/restored.*''', re.MULTILINE ) ) # verify that files restored are the same as those backed up subprocess.check_output(["diff", "-r", "/tmp/test", "/tmp/restored"]) def test_diagnostics_arg(self): # "output" from stdout may currently be polluted with logging output # lines from INFO and DEBUG log output. # Logging output of WARNING and ERROR is already written to stderr # so `check_output` does work here (returns only stdout without # stderr). output = subprocess.check_output(["./backintime", "--diagnostics"]) # output = subprocess.getoutput("./backintime --diagnostics") diagnostics = json.loads(output) self.assertEqual(diagnostics["backintime"]["name"], config.Config.APP_NAME) self.assertEqual(diagnostics["backintime"]["version"], version.__version__) backintime-1.5.2/common/test/test_backup.py000066400000000000000000000225011465446530500207660ustar00rootroot00000000000000# Back In Time # Copyright (C) 2016-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. # # 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 sys import unittest from unittest.mock import patch, ANY from datetime import datetime from test import generic sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import backintime import config import snapshots import tools import logger from applicationinstance import ApplicationInstance from pluginmanager import PluginManager from mount import Mount from exceptions import MountException @patch('time.sleep') # speed up unittest class TestBackup(generic.SnapshotsTestCase): @patch('snapshots.Snapshots.takeSnapshot') def test_backup(self, takeSnapshot, sleep): takeSnapshot.return_value = [True, False] self.assertIs(self.sn.backup(), False) self.assertEqual(takeSnapshot.call_count, 1) self.assertIsInstance(takeSnapshot.call_args[0][0], snapshots.SID) self.assertIsInstance(takeSnapshot.call_args[0][1], datetime) self.assertIsInstance(takeSnapshot.call_args[0][2], list) @patch('subprocess.Popen', autospec=True) def test_backup_async(self, Popen_mock, sleep): """:py:func:`backintime.takeSnapshotAsync`: Tests if the command for async execution is created as expected but does not execute it. Uses only default arguments. """ # Deactivate debug mode (is True on TravisCI due to "make unittest-v") # otherwise the OuT adds "--debug" to the cmd args (fails assertions) # The value will be reset in the setup() of the test. logger.DEBUG = False self.assertIsNone( backintime.takeSnapshotAsync(self.cfg, checksum=False)) expected_call = [ 'backintime', '--config', self.cfgFile, '--share-path', self.cfg.DATA_FOLDER_ROOT, 'backup' ] Popen_mock.assert_called_once_with(expected_call, env=ANY) @patch('subprocess.Popen', autospec=True) def test_backup_async_with_checksum(self, Popen_mock, sleep): """:py:func:`backintime.takeSnapshotAsync`: Tests if the command for async execution is created as expected but does not execute it. Uses ``checksum=True`` as non-default argument. """ # Deactivate debug mode (is True on TravisCI due to "make unittest-v") # otherwise the OuT adds "--debug" to the cmd args (fails assertions) # The value will be reset in the setup() of the test. logger.DEBUG = False self.assertIsNone( backintime.takeSnapshotAsync(self.cfg, checksum=True)) expected_call = [ 'backintime', '--config', self.cfgFile, '--share-path', self.cfg.DATA_FOLDER_ROOT, '--checksum', 'backup' ] Popen_mock.assert_called_once_with(expected_call, env=ANY) @patch('subprocess.Popen', autospec=True) def test_backup_async_profile_2(self, Popen_mock, sleep): """:py:func:`backintime.takeSnapshotAsync`: Tests if the command for async execution is created as expected but does not execute it. Uses a non-default profile (created for the test only). """ # Deactivate debug mode (is True on TravisCI due to "make unittest-v") # otherwise the OuT adds "--debug" to the cmd args (fails assertions) # The value will be reset in the setup() of the test. logger.DEBUG = False # Create and use a (new) profile (not the default '1') new_profile_id = self.cfg.addProfile("Profile #2") self.cfg.setCurrentProfile(new_profile_id) self.assertIsNone( backintime.takeSnapshotAsync(self.cfg, checksum=False)) expected_call = [ 'backintime', '--profile-id', new_profile_id, '--config', self.cfgFile, '--share-path', self.cfg.DATA_FOLDER_ROOT, 'backup' ] Popen_mock.assert_called_once_with(expected_call, env=ANY) @patch('snapshots.Snapshots.takeSnapshot') def test_no_changes(self, takeSnapshot, sleep): takeSnapshot.return_value = [False, False] self.assertIs(self.sn.backup(), False) self.assertTrue(takeSnapshot.called) sid = takeSnapshot.call_args[0][0] newSnapshot = snapshots.NewSnapshot(self.cfg) self.assertFalse(newSnapshot.exists()) self.assertFalse(sid.exists()) @patch('snapshots.Snapshots.takeSnapshot') def test_with_errors(self, takeSnapshot, sleep): takeSnapshot.return_value = [False, True] self.assertIs(self.sn.backup(), True) @patch('tools.onBattery') @patch('snapshots.Snapshots.takeSnapshot') def test_no_backup_on_battery(self, takeSnapshot, onBattery, sleep): self.cfg.setNoSnapshotOnBattery(True) takeSnapshot.return_value = [True, False] # run on battery onBattery.return_value = True self.assertFalse(self.sn.backup(force=False)) self.assertFalse(takeSnapshot.called) # force run self.assertFalse(self.sn.backup(force=True)) self.assertTrue(takeSnapshot.called) takeSnapshot.reset_mock() # ignore Battery self.cfg.setNoSnapshotOnBattery(False) self.assertFalse(self.sn.backup(force=False)) self.assertTrue(takeSnapshot.called) @patch('snapshots.Snapshots.takeSnapshot') def test_scheduled(self, takeSnapshot, sleep): # first run without timestamp takeSnapshot.return_value = [True, False] self.assertFalse(self.sn.backup(force=False)) self.assertTrue(takeSnapshot.called) takeSnapshot.reset_mock() # second run doesn't use an anacron-like schedule tools.writeTimeStamp(self.cfg.anacronSpoolFile()) self.assertFalse(self.sn.backup(force=False)) self.assertTrue(takeSnapshot.called) takeSnapshot.reset_mock() # third run uses anacron-like schedule so it should not run self.cfg.setScheduleMode(self.cfg.REPEATEDLY) self.assertFalse(self.sn.backup(force=False)) self.assertFalse(takeSnapshot.called) # finally force self.assertFalse(self.sn.backup(force=True)) self.assertTrue(takeSnapshot.called) @patch.object(ApplicationInstance, 'check') @patch('snapshots.Snapshots.takeSnapshot') def test_already_running(self, takeSnapshot, check, sleep): check.return_value = False takeSnapshot.return_value = [True, False] self.assertIs(self.sn.backup(), True) self.assertFalse(takeSnapshot.called) @patch.object(PluginManager, 'processBegin') @patch('snapshots.Snapshots.takeSnapshot') def test_plugin_prevented_backup(self, takeSnapshot, processBegin, sleep): processBegin.return_value = False takeSnapshot.return_value = [True, False] self.assertIs(self.sn.backup(), True) self.assertFalse(takeSnapshot.called) @patch.object(config.Config, 'isConfigured') @patch('snapshots.Snapshots.takeSnapshot') def test_not_configured(self, takeSnapshot, isConfigured, sleep): isConfigured.return_value = False takeSnapshot.return_value = [True, False] self.assertIs(self.sn.backup(), True) self.assertFalse(takeSnapshot.called) @patch.object(Mount, 'mount') @patch('snapshots.Snapshots.takeSnapshot') def test_mount_exception(self, takeSnapshot, mount, sleep): mount.side_effect = MountException() takeSnapshot.return_value = [True, False] self.assertIs(self.sn.backup(), True) self.assertFalse(takeSnapshot.called) @patch.object(Mount, 'umount') @patch('snapshots.Snapshots.takeSnapshot') def test_umount_exception(self, takeSnapshot, umount, sleep): umount.side_effect = MountException() takeSnapshot.return_value = [True, False] self.assertIs(self.sn.backup(), False) @patch.object(config.Config, 'canBackup') @patch('snapshots.Snapshots.takeSnapshot') def test_cant_backup(self, takeSnapshot, canBackup, sleep): canBackup.return_value = False takeSnapshot.return_value = [True, False] self.assertIs(self.sn.backup(), True) self.assertFalse(takeSnapshot.called) @patch('snapshots.Snapshots.takeSnapshot') def test_takeSnapshot_exception_cleanup(self, takeSnapshot, sleep): takeSnapshot.side_effect = Exception('Boom') new = snapshots.NewSnapshot(self.cfg) new.makeDirs() self.assertTrue(new.exists()) with self.assertRaises(Exception): self.sn.backup() self.assertFalse(new.saveToContinue) self.assertTrue(new.failed) backintime-1.5.2/common/test/test_config.py000066400000000000000000000170661465446530500210000ustar00rootroot00000000000000# Back In Time # Copyright (C) 2016 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. """Tests about config module. """ import os import stat import sys import getpass import unittest from unittest import mock from test import generic from tempfile import TemporaryDirectory sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import config class TestConfig(generic.TestCaseCfg): def test_set_snapshots_path_test_writes(self): with TemporaryDirectory() as dirpath: self.assertTrue(self.cfg.setSnapshotsPath(dirpath)) def test_set_snapshots_path_fails_on_ro(self): with TemporaryDirectory() as dirpath: # set directory to read only with generic.mockPermissions(dirpath, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH): self.assertFalse(self.cfg.setSnapshotsPath(dirpath)) @mock.patch('os.chmod') def test_set_snapshots_path_permission_fail(self, mock_chmod): mock_chmod.side_effect = PermissionError() with TemporaryDirectory() as dirpath: self.assertTrue(self.cfg.setSnapshotsPath(dirpath)) class TestSshCommand(generic.SSHTestCase): @classmethod def setUpClass(cls): cls._user = getpass.getuser() def test_full_command(self): cmd = self.cfg.sshCommand(cmd=['echo', 'foo']) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', f'{self._user}@localhost', 'echo', 'foo' ] ) def test_custom_args(self): cmd = self.cfg.sshCommand( cmd=['echo', 'foo'], custom_args=['-o', 'PreferredAuthentications=publickey']) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', '-o', 'PreferredAuthentications=publickey', f'{self._user}@localhost', 'echo', 'foo' ] ) def test_cipher_aes256_cbc(self): self.cfg.setSshCipher('aes256-cbc') cmd = self.cfg.sshCommand(cmd=['echo', 'foo']) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', '-o', 'Ciphers=aes256-cbc', f'{self._user}@localhost', 'echo', 'foo' ] ) def test_cipher_disabled(self): cmd = self.cfg.sshCommand(cmd=['echo', 'foo'], cipher=False) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', f'{self._user}@localhost', 'echo', 'foo' ] ) def test_without_command(self): cmd = self.cfg.sshCommand() self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', f'{self._user}@localhost', ] ) def test_nice_and_ionice(self): self.cfg.setNiceOnRemote(True) self.cfg.setIoniceOnRemote(True) cmd = self.cfg.sshCommand(cmd=['echo', 'foo']) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', f'{self._user}@localhost', 'ionice', '-c2', '-n7', 'nice', '-n19', 'echo', 'foo' ] ) def test_nice_and_ionice_without_command(self): self.cfg.setNiceOnRemote(True) self.cfg.setIoniceOnRemote(True) cmd = self.cfg.sshCommand() self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', f'{self._user}@localhost', ] ) def test_quote(self): cmd = self.cfg.sshCommand(cmd=['echo', 'foo'], quote=True) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', f'{self._user}@localhost', "'", 'echo', 'foo', "'" ] ) def test_quote_without_command(self): cmd = self.cfg.sshCommand(quote=True) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', f'{self._user}@localhost', ] ) def test_prefix(self): self.cfg.setSshPrefix(True, 'echo bar') cmd = self.cfg.sshCommand(cmd=['echo', 'foo']) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', f'{self._user}@localhost', 'echo', 'bar', 'echo', 'foo' ] ) def test_prefix_false(self): # disable prefix cmd = self.cfg.sshCommand(cmd=['echo', 'foo'], prefix=False) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', '-p', '22', f'{self._user}@localhost', 'echo', 'foo' ] ) def test_disable_args(self): cmd = self.cfg.sshCommand(port=False, user_host=False) self.assertListEqual( cmd, [ 'ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', f'IdentityFile={generic.PRIV_KEY_FILE}', ] ) backintime-1.5.2/common/test/test_config_crontab.py000066400000000000000000000120521465446530500224760ustar00rootroot00000000000000# Back In Time # Copyright (C) 2024 Kosta Vukicevic, 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. # # 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. """Tests about Cron-related behavior of the config module. See also test_schedule.py for low-level-Cron-behavior implemented in schedule module.""" import unittest import pyfakefs.fake_filesystem_unittest as pyfakefs_ut import sys import os import tempfile import inspect from pathlib import Path from unittest import mock sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import config class Cron(unittest.TestCase): """Cron-related behavior of Config class.""" def test_cron_lines(self): """Creation of crontab lines per profile""" # Mock reading a config file with mock.patch('configfile.ConfigFile.append'): cfg = config.Config() # Profile 1 (default profile) cfg.setScheduleMode(12) # 12 = every two hours # Profile 2 profile_id = cfg.addProfile('NoSchedule') self.assertEqual(profile_id, '2') # Profile 3 profile_id = cfg.addProfile('Second') self.assertEqual(profile_id, '3') cfg.setScheduleMode(7, profile_id) # 7 = every 30 minutes result = cfg.profiles_cron_lines() self.assertEqual(len(result), 2) self.assertIn('*/2 * * *', result[0]) self.assertIn('backintime', result[0]) self.assertIn('backup-job', result[0]) self.assertNotIn('--profile-id', result[0]) self.assertIn('*/30 * * *', result[1]) self.assertIn('backintime', result[1]) self.assertIn('backup-job', result[1]) self.assertIn('--profile-id 3', result[1]) class CrontabDebug(pyfakefs_ut.TestCase): """Debug behavior when scheduled via crontab""" def setUp(self): """Setup a fake filesystem with a config file.""" self.setUpPyfakefs(allow_root_user=False) # cleanup() happens automatically self._temp_dir = tempfile.TemporaryDirectory(prefix='bit.') # Workaround: tempfile and pathlib not compatible yet self.temp_path = Path(self._temp_dir.name) self.config_fp = self._create_config_file(parent_path=self.temp_path) def _create_config_file(self, parent_path): """Minimal config file""" cfg_content = inspect.cleandoc(''' config.version=6 profile1.snapshots.include.1.type=0 profile1.snapshots.include.1.value=rootpath/source profile1.snapshots.include.size=1 profile1.snapshots.no_on_battery=false profile1.snapshots.notify.enabled=true profile1.snapshots.path=rootpath/destination profile1.snapshots.path.host=test-host profile1.snapshots.path.profile=1 profile1.snapshots.path.user=test-user profile1.snapshots.preserve_acl=false profile1.snapshots.preserve_xattr=false profile1.snapshots.remove_old_snapshots.enabled=true profile1.snapshots.remove_old_snapshots.unit=80 profile1.snapshots.remove_old_snapshots.value=10 profile1.snapshots.rsync_options.enabled=false profile1.snapshots.rsync_options.value= profiles.version=1 ''') # config file location config_fp = parent_path / 'config_path' / 'config' config_fp.parent.mkdir() config_fp.write_text(cfg_content, 'utf-8') return config_fp @mock.patch('tools.which', return_value='backintime') def test_crontab_contains_debug(self, mock_which): """ About mock_which: A workaround because the function gives false-negative when using a fake filesystem. """ conf = config.Config(str(self.config_fp)) # set and assert start conditions conf.setScheduleDebug(True) self.assertTrue(conf.scheduleDebug()) sut = conf._cron_cmd(profile_id='1') self.assertIn('--debug', sut) @mock.patch('tools.which', return_value='backintime') def test_crontab_without_debug(self, mock_which): """No debug output in crontab line. About mock_which: See test_crontab_with_debug(). """ conf = config.Config(str(self.config_fp)) # set and assert start conditions conf.setScheduleDebug(False) self.assertFalse(conf.scheduleDebug()) sut = conf._cron_cmd(profile_id='1') self.assertNotIn('--debug', sut) backintime-1.5.2/common/test/test_configfile.py000066400000000000000000000664521465446530500216430ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 sys from tempfile import NamedTemporaryFile import unittest from test import generic sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import configfile class TestConfigFile(generic.TestCase): """ Tests for the ConfigFile class in the configfile module """ def test_save(self): """ Saves the config file in the tmp directory """ with NamedTemporaryFile() as cfgFile: cf = configfile.ConfigFile() self.assertTrue(cf.save(cfgFile.name)) self.assertExists(cfgFile.name) #test fail routine in ConfigFile.save with NamedTemporaryFile() as fakeDir: self.assertFalse(cf.save(os.path.join(fakeDir.name, 'foo'))) def test_load(self): """ ConfigFile should be able to load its content from a previously saved ConfigFile object. """ with NamedTemporaryFile() as cfgFile: original_cf = configfile.ConfigFile() key = "config_key" value = "config_value" original_cf.setStrValue(key, value) original_cf.save(cfgFile.name) cf = configfile.ConfigFile() cf.load(cfgFile.name) self.assertEqual(len(cf.keys()), len(original_cf.keys())) for k in original_cf.keys(): with self.subTest(k = k): #workaround for py.test3 2.5.1 doesn't support subTest msg = 'k = %s' %k self.assertTrue(cf.hasKey(k), msg) self.assertEqual(original_cf.strValue(k), cf.strValue(k)) def test_remapKey(self): cfg = configfile.ConfigFile() cfg.dict = {'foo': '123', 'bar': '456'} #old key not in dict cfg.remapKey('notExistedKey', 'baz') self.assertEqual(cfg.strValue('foo'), '123') self.assertEqual(cfg.strValue('bar'), '456') #valid remap cfg.remapKey('foo', 'baz') self.assertEqual(cfg.strValue('foo'), '') self.assertEqual(cfg.strValue('baz'), '123') self.assertEqual(cfg.strValue('bar'), '456') #do not overwrite existing keys cfg.remapKey('baz', 'bar') self.assertEqual(cfg.strValue('baz'), '') self.assertEqual(cfg.strValue('bar'), '456') def test_remapKeyRegex(self): cfg = configfile.ConfigFile() cfg.dict = {'asdf.foo.qwertz': '123', 'foo.qwertz': '456', 'asdf.foo': '789', 'yxcv': 'jkl'} cfg.remapKeyRegex('foo', 'bar') self.assertEqual(cfg.strValue('asdf.bar.qwertz', 'WrongValue'), '123') self.assertEqual(cfg.strValue('bar.qwertz', 'WrongValue'), '456') self.assertEqual(cfg.strValue('asdf.bar', 'WrongValue'), '789') self.assertEqual(cfg.strValue('asdf.foo.qwertz', ''), '') self.assertEqual(cfg.strValue('foo.qwertz', ''), '') self.assertEqual(cfg.strValue('asdf.foo', ''), '') self.assertEqual(cfg.strValue('yxcv', 'WrongValue'), 'jkl') def test_hasKey(self): cfg = configfile.ConfigFile() cfg.dict = {'foo': 'bar'} self.assertTrue(cfg.hasKey('foo')) self.assertFalse(cfg.hasKey('non_existend_key')) ############################################################################ ### str_value ### ############################################################################ def test_strValue(self): cfg = configfile.ConfigFile() cfg.dict = {'foo': 'bar'} self.assertEqual(cfg.strValue('foo', 'default'), 'bar') def test_strValue_default(self): cfg = configfile.ConfigFile() self.assertEqual(cfg.strValue('non_existend_key', 'default'), 'default') def test_setStrValue(self): cfg = configfile.ConfigFile() cfg.setStrValue('foo', 'bar') self.assertDictEqual(cfg.dict, {'foo': 'bar'}) ############################################################################ ### int_value ### ############################################################################ def test_intValue(self): cfg = configfile.ConfigFile() cfg.dict = {'foo': '11'} self.assertEqual(cfg.intValue('foo', 22), 11) def test_intValue_default(self): cfg = configfile.ConfigFile() self.assertEqual(cfg.intValue('non_existend_key', 33), 33) def test_setIntValue(self): cfg = configfile.ConfigFile() cfg.setIntValue('foo', 44) self.assertDictEqual(cfg.dict, {'foo': '44'}) ############################################################################ ### bool_value ### ############################################################################ def test_boolValue(self): cfg = configfile.ConfigFile() cfg.dict = {'foo': 'true', 'bar': '1', 'baz': 'false', 'bla': '0'} self.assertEqual(cfg.boolValue('foo', False), True) self.assertEqual(cfg.boolValue('bar', False), True) self.assertEqual(cfg.boolValue('baz', True), False) self.assertEqual(cfg.boolValue('bla', True), False) def test_boolValue_default(self): cfg = configfile.ConfigFile() self.assertEqual(cfg.boolValue('non_existend_key', False), False) self.assertEqual(cfg.boolValue('non_existend_key', True), True) def test_setBoolValue(self): cfg = configfile.ConfigFile() cfg.setBoolValue('foo', True) cfg.setBoolValue('bar', False) self.assertDictEqual(cfg.dict, {'foo': 'true', 'bar': 'false'}) ############################################################################ ### listValue ### ############################################################################ def test_listValue_default(self): cfg = configfile.ConfigFile() self.assertListEqual(cfg.listValue('test', 'str:value', ['asdf']), ['asdf']) def test_listValue_int(self): cfg = configfile.ConfigFile() cfg.dict = {'aaa.size': '3', 'aaa.1.bla': '55', 'aaa.2.bla': '66', 'aaa.3.bla': '77'} self.assertListEqual(cfg.listValue('aaa', 'int:bla'), [55, 66, 77]) def test_listValue_str(self): cfg = configfile.ConfigFile() cfg.dict = {'bbb.size': '3', 'bbb.1.value': 'foo', 'bbb.2.value': 'bar', 'bbb.3.value': 'baz'} self.assertListEqual(cfg.listValue('bbb', 'str:value'), ['foo', 'bar', 'baz']) def test_listValue_bool(self): cfg = configfile.ConfigFile() cfg.dict = {'ccc.size': '2', 'ccc.1.foo': 'true', 'ccc.2.foo': 'false'} self.assertListEqual(cfg.listValue('ccc', 'bool:foo'), [True, False]) def test_listValue_tuple(self): cfg = configfile.ConfigFile() cfg.dict = {'ddd.size': '3', 'ddd.1.value': 'foo', 'ddd.1.type': '11', 'ddd.1.enabled': 'true', 'ddd.2.value': 'bar', 'ddd.2.type': '22', 'ddd.2.enabled': 'false', 'ddd.3.value': 'baz', 'ddd.3.type': '33', 'ddd.3.enabled': 'true'} self.assertListEqual(cfg.listValue('ddd', ('str:value', 'int:type', 'bool:enabled')), [('foo', 11, True), ('bar', 22, False), ('baz', 33, True)]) def test_listValue_tuple_missing_values(self): """ Don't include missing values. Bug #521 https://github.com/bit-team/backintime/issues/521 """ cfg = configfile.ConfigFile() cfg.dict = {'eee.size': '3', 'eee.1.value': 'foo', 'eee.1.enabled': 'true', 'eee.2.type': '22', 'eee.2.enabled': 'false', 'eee.3.value': 'baz', 'eee.3.type': '33'} self.assertListEqual(cfg.listValue('eee', ('str:value', 'int:type', 'bool:enabled')), [('foo', 0, True), ('baz', 33, False)]) def test_listValue_invalid_type(self): cfg = configfile.ConfigFile() cfg.dict = {'aaa.size': '3', 'aaa.1.value': '55', 'aaa.2.value': '66', 'aaa.3.value': '77'} with self.assertRaises(TypeError): cfg.listValue('aaa', 'non_existend_type:value') with self.assertRaises(TypeError): cfg.listValue('aaa', {'dict:value'}) with self.assertRaises(TypeError): cfg.listValue('aaa', 1) def test_listValue_wrong_size(self): """ Don't include empty values if size is wrong. Bug #521 https://github.com/bit-team/backintime/issues/521 """ cfg = configfile.ConfigFile() cfg.dict = {'bbb.size': '4', 'bbb.1.value': 'foo', 'bbb.2.value': 'bar', 'bbb.3.value': 'baz'} self.assertListEqual(cfg.listValue('bbb', 'str:value'), ['foo', 'bar', 'baz']) def test_listValue_zero_count(self): cfg = configfile.ConfigFile() cfg.dict = {'ccc.size': '2', 'ccc.0.value': 'foo', 'ccc.1.value': 'bar', 'ccc.2.value': 'baz'} self.assertListEqual(cfg.listValue('ccc', 'str:value'), ['bar', 'baz']) def test_listValue_missing_values(self): """ Don't include missing values. Bug #521 https://github.com/bit-team/backintime/issues/521 """ cfg = configfile.ConfigFile() cfg.dict = {'ddd.size': '4', 'ddd.1.value': 'foo', 'ddd.2.value': 'bar', 'ddd.4.value': 'baz'} self.assertListEqual(cfg.listValue('ddd', 'str:value'), ['foo', 'bar', 'baz']) def test_listValue_empty_list(self): cfg = configfile.ConfigFile() cfg.dict = {'eee.size': '0'} self.assertListEqual(cfg.listValue('eee', 'str:value', default = ['foo', 'bar']), []) ############################################################################ ### setListValue ### ############################################################################ def test_setListValue_int(self): cfg = configfile.ConfigFile() cfg.setListValue('aaa', 'int:bla', [55, 66, 77]) self.assertDictEqual(cfg.dict, {'aaa.size': '3', 'aaa.1.bla': '55', 'aaa.2.bla': '66', 'aaa.3.bla': '77'}) def test_setListValue_str(self): cfg = configfile.ConfigFile() cfg.setListValue('bbb', 'str:value', ['foo', 'bar', 'baz']) self.assertDictEqual(cfg.dict, {'bbb.size': '3', 'bbb.1.value': 'foo', 'bbb.2.value': 'bar', 'bbb.3.value': 'baz'}) def test_setListValue_bool(self): cfg = configfile.ConfigFile() cfg.setListValue('ccc', 'bool:foo', [True, False]) self.assertDictEqual(cfg.dict, {'ccc.size': '2', 'ccc.1.foo': 'true', 'ccc.2.foo': 'false'}) def test_setListValue_tuple(self): cfg = configfile.ConfigFile() cfg.setListValue('ddd', ('str:value', 'int:type', 'bool:enabled'), [('foo', 11, True), ('bar', 22, False), ('baz', 33, True)]) self.assertDictEqual(cfg.dict, {'ddd.size': '3', 'ddd.1.value': 'foo', 'ddd.1.type': '11', 'ddd.1.enabled': 'true', 'ddd.2.value': 'bar', 'ddd.2.type': '22', 'ddd.2.enabled': 'false', 'ddd.3.value': 'baz', 'ddd.3.type': '33', 'ddd.3.enabled': 'true'}) def test_setListValue_tuple_missing_values(self): cfg = configfile.ConfigFile() cfg.setListValue('ddd', ('str:value', 'int:type', 'bool:enabled'), [('foo', 11, True), ('bar', 22), ('baz',)]) self.assertDictEqual(cfg.dict, {'ddd.size': '3', 'ddd.1.value': 'foo', 'ddd.1.type': '11', 'ddd.1.enabled': 'true', 'ddd.2.value': 'bar', 'ddd.2.type': '22', 'ddd.3.value': 'baz'}) def test_setListValue_remove_leftovers(self): cfg = configfile.ConfigFile() cfg.dict = {'eee.size': '5', 'eee.1.bla': '55', 'eee.2.bla': '66', 'eee.3.bla': '77', 'eee.4.bla': '88', 'eee.5.bla': '99'} cfg.setListValue('eee', 'int:bla', [55, 66, 77]) self.assertDictEqual(cfg.dict, {'eee.size': '3', 'eee.1.bla': '55', 'eee.2.bla': '66', 'eee.3.bla': '77'}) def test_setListValue_remove_leftovers_tuple(self): cfg = configfile.ConfigFile() cfg.dict = {'fff.size': '5', 'fff.1.value': 'foo', 'fff.1.type': '11', 'fff.1.enabled': 'true', 'fff.2.value': 'bar', 'fff.2.type': '22', 'fff.2.enabled': 'false', 'fff.3.value': 'baz', 'fff.3.type': '33', 'fff.3.enabled': 'true', 'fff.4.value': 'boom', 'fff.4.type': '44', 'fff.4.enabled': 'true', 'fff.5.value': 'bam', 'fff.5.type': '55', 'fff.5.enabled': 'true'} cfg.setListValue('fff', ('str:value', 'int:type', 'bool:enabled'), [('foo', 11, True), ('bar', 22), ('baz',)]) self.assertDictEqual(cfg.dict, {'fff.size': '3', 'fff.1.value': 'foo', 'fff.1.type': '11', 'fff.1.enabled': 'true', 'fff.2.value': 'bar', 'fff.2.type': '22', 'fff.3.value': 'baz'}) def test_setListValue_invalid_type_for_type_key(self): cfg = configfile.ConfigFile() with self.assertRaises(TypeError): cfg.setListValue('aaa', 'non_existend_type:value', ['foo',]) with self.assertRaises(TypeError): cfg.setListValue('aaa', {'dict:value'}, ['foo',]) with self.assertRaises(TypeError): cfg.setListValue('aaa', 1, ['foo',]) def test_setListValue_invalid_type_for_value(self): cfg = configfile.ConfigFile() with self.assertRaises(TypeError): cfg.setListValue('bbb', 'str:value', 'foo') with self.assertRaises(TypeError): cfg.setListValue('bbb', 'str:value', {'foo': 'bar'}) ############################################################################ ### remove keys ### ############################################################################ def test_remove_key(self): cfg = configfile.ConfigFile() cfg.dict = {'foo': 'true', 'bar': '1', 'baz': 'false', 'bla': '0'} cfg.removeKey('bla') self.assertDictEqual(cfg.dict, {'foo': 'true', 'bar': '1', 'baz': 'false'}) def test_remove_keys_start_with(self): cfg = configfile.ConfigFile() cfg.dict = {'foo': 'true', 'bar': '1', 'baz': 'false', 'bla': '0'} cfg.removeKeysStartsWith('ba') self.assertDictEqual(cfg.dict, {'foo': 'true', 'bla': '0'}) def test_remove_keys_start_with_not_matching_prefix(self): cfg = configfile.ConfigFile() cfg.dict = {'foo': 'true', 'bar': '1', 'baz': 'false', 'bla': '0'} cfg.removeKeysStartsWith('not_matching') self.assertDictEqual(cfg.dict, {'foo': 'true', 'bar': '1', 'baz': 'false', 'bla': '0'}) class TestConfigFileWithProfiles(generic.TestCase): def setUp(self): super(TestConfigFileWithProfiles, self).setUp() self.cfg = configfile.ConfigFileWithProfiles('DefaultProfileName') self.cfg.addProfile('foo') self.cfg.addProfile('bar') self.cfg.addProfile('baz') def test_load(self): """ ConfigFile should be able to load its content from a previously saved ConfigFile object. """ with NamedTemporaryFile() as cfgFile: origCfg = configfile.ConfigFileWithProfiles() origCfg.setIntValue('profiles.version', 1) key = "config_key" value = "config_value" origCfg.setProfileStrValue(key, value) origCfg.setProfileStrValue(key, value, profile_id = '2') origCfg.save(cfgFile.name) self.cfg.load(cfgFile.name) self.assertEqual(len(self.cfg.keys()), len(origCfg.keys())) for k in origCfg.keys(): with self.subTest(k = k): #workaround for py.test3 2.5.1 doesn't support subTest msg = 'k = %s' %k self.assertTrue(self.cfg.hasKey(k), msg) self.assertEqual(origCfg.strValue(k), self.cfg.strValue(k)) def test_profiles(self): emptyCfg = configfile.ConfigFileWithProfiles() self.assertListEqual(emptyCfg.profiles(), ['1',]) self.assertListEqual(self.cfg.profiles(), ['1', '2', '3', '4']) self.cfg.removeProfile('3') self.assertListEqual(self.cfg.profiles(), ['1', '2', '4']) def test_profilesSortedByName(self): self.assertListEqual(self.cfg.profilesSortedByName(), ['3', '4', '1', '2']) def test_current_profile(self): self.assertEqual(self.cfg.currentProfile(), '1') self.assertTrue(self.cfg.setCurrentProfile(4)) self.assertEqual(self.cfg.currentProfile(), '4') self.assertTrue(self.cfg.setCurrentProfile('3')) self.assertEqual(self.cfg.currentProfile(), '3') self.assertFalse(self.cfg.setCurrentProfile('9')) self.assertEqual(self.cfg.currentProfile(), '3') def test_current_profile_by_name(self): self.assertEqual(self.cfg.currentProfile(), '1') self.assertTrue(self.cfg.setCurrentProfileByName('bar')) self.assertEqual(self.cfg.currentProfile(), '3') self.assertFalse(self.cfg.setCurrentProfileByName('NotExistingProfile')) self.assertEqual(self.cfg.currentProfile(), '3') def test_profileExists(self): self.assertTrue(self.cfg.profileExists('2')) self.assertTrue(self.cfg.profileExists(3)) self.assertFalse(self.cfg.profileExists('9')) self.assertFalse(self.cfg.profileExists(10)) def test_profileExistsByName(self): self.assertTrue(self.cfg.profileExistsByName('foo')) self.assertFalse(self.cfg.profileExistsByName('NotExistingProfile')) def test_profileName(self): self.assertEqual(self.cfg.profileName('1'), 'DefaultProfileName') self.assertEqual(self.cfg.profileName('2'), 'foo') self.assertEqual(self.cfg.profileName(3), 'bar') self.assertEqual(self.cfg.profileName(), 'DefaultProfileName') self.cfg.setCurrentProfile('3') self.assertEqual(self.cfg.profileName(), 'bar') self.assertEqual(self.cfg.profileName('4'), 'baz') del self.cfg.dict['profile4.name'] self.assertEqual(self.cfg.profileName('4'), 'Profile 4') def test_addProfile(self): #add already existing profile self.assertIsNone(self.cfg.addProfile('foo')) #new valid profile self.assertEqual(self.cfg.addProfile('asdf'), '5') #new valid profile fill an old profile ID self.cfg.removeProfile('3') self.assertEqual(self.cfg.addProfile('qwertz'), '3') def test_removeProfile(self): for profile in self.cfg.profiles(): self.cfg.setProfileStrValue('foo', 'bar', profile) self.assertFalse(self.cfg.removeProfile('9')) self.assertTrue(self.cfg.removeProfile('3')) self.assertNotIn('3', self.cfg.profiles()) self.assertNotIn('profile3.foo', self.cfg.dict) self.cfg.setCurrentProfile('4') self.assertTrue(self.cfg.removeProfile()) self.assertNotIn('4', self.cfg.profiles()) self.assertNotIn('profile4.foo', self.cfg.dict) self.assertEqual(self.cfg.currentProfile(), '1') self.assertTrue(self.cfg.removeProfile(2)) self.assertNotIn('2', self.cfg.profiles()) self.assertNotIn('profile2.foo', self.cfg.dict) self.assertFalse(self.cfg.removeProfile()) def test_setProfileName(self): self.assertFalse(self.cfg.setProfileName('foo', '3')) self.assertEqual(self.cfg.profileName('3'), 'bar') self.assertTrue(self.cfg.setProfileName('newName', '4')) self.assertEqual(self.cfg.profileName('4'), 'newName') self.assertTrue(self.cfg.setProfileName('otherName', 3)) self.assertEqual(self.cfg.profileName('3'), 'otherName') self.assertTrue(self.cfg.setProfileName('thirdName')) self.assertEqual(self.cfg.profileName('1'), 'thirdName') def test_get_profile_key(self): self.assertEqual(self.cfg.profileKey('foo'), 'profile1.foo') self.assertEqual(self.cfg.profileKey('foo', '2'), 'profile2.foo') self.assertEqual(self.cfg.profileKey('foo', 3), 'profile3.foo') def test_removeProfileKey(self): for profile in self.cfg.profiles(): self.cfg.setProfileStrValue('foo', 'bar', profile) self.assertIn('profile1.foo', self.cfg.dict) self.cfg.removeProfileKey('foo') self.assertNotIn('profile1.foo', self.cfg.dict) self.assertIn('profile3.foo', self.cfg.dict) self.cfg.removeProfileKey('foo', '3') self.assertNotIn('profile3.foo', self.cfg.dict) def test_removeProfileKeysStartsWith(self): for profile in self.cfg.profiles(): self.cfg.setProfileStrValue('foo', 'bar', profile) self.assertIn('profile1.foo', self.cfg.dict) self.cfg.removeProfileKeysStartsWith('f') self.assertNotIn('profile1.foo', self.cfg.dict) self.assertIn('profile3.foo', self.cfg.dict) self.cfg.removeProfileKeysStartsWith('f', '3') self.assertNotIn('profile3.foo', self.cfg.dict) def test_remapProfileKey(self): for profile in self.cfg.profiles(): self.cfg.setProfileStrValue('foo', '123', profile) self.cfg.setProfileStrValue('bar', '456', profile) # old key not in cfg self.cfg.remapProfileKey('notExistedKey', 'baz', 1) self.assertEqual(self.cfg.profileStrValue('foo', '', 1), '123') self.assertEqual(self.cfg.profileStrValue('bar', '', 1), '456') # valid remap self.cfg.remapProfileKey('foo', 'baz', 1) self.assertEqual(self.cfg.profileStrValue('foo', '', 1), '') self.assertEqual(self.cfg.profileStrValue('baz', '', 1), '123') self.assertEqual(self.cfg.profileStrValue('foo', '', 2), '123') self.assertEqual(self.cfg.profileStrValue('foo', '', 3), '123') # do not overwrite existing keys self.cfg.remapProfileKey('foo', 'bar', 1) self.assertEqual(self.cfg.profileStrValue('foo', '', 2), '123') self.assertEqual(self.cfg.profileStrValue('baz', '', 2), '') self.assertEqual(self.cfg.profileStrValue('bar', '', 2), '456') def test_hasProfileKey(self): for profile in self.cfg.profiles(): self.cfg.setProfileStrValue('foo', 'bar', profile) self.assertTrue(self.cfg.hasProfileKey('foo')) self.assertFalse(self.cfg.hasProfileKey('baz')) self.assertTrue(self.cfg.hasProfileKey('foo', '3')) self.assertFalse(self.cfg.hasProfileKey('baz', '3')) def test_set_profile_value(self): methods = {'str': ('foo', 'FOO'), 'int': ('bar', 123), 'bool': ('baz', True)} for profile in (None, '3'): for t in methods: with self.subTest(profile = profile, t = t): #workaround for py.test3 2.5.1 doesn't support subTest msg = 'profile = {}, t = {}'.format(profile, t) key, value = methods[t] setFunc = getattr(self.cfg, 'setProfile{}Value'.format(t.capitalize())) getFunc = getattr(self.cfg, 'profile{}Value'.format(t.capitalize())) setFunc(key, value, profile_id = profile) self.assertEqual(getFunc(key, profile_id = profile), value, msg) with self.subTest(profile = profile): #workaround for py.test3 2.5.1 doesn't support subTest msg = 'profile = {}'.format(profile) self.cfg.setProfileListValue('bla', 'str:value', ['ASDF', 'QWERTZ'], profile_id = profile) result = self.cfg.profileListValue('bla', 'str:value', profile_id = profile) self.assertListEqual(result, ['ASDF', 'QWERTZ'], msg) if __name__ == '__main__': unittest.main() backintime-1.5.2/common/test/test_diagnostics.py000066400000000000000000000062511465446530500220340ustar00rootroot00000000000000"""Test related to diagnostics.py""" import sys import pathlib import unittest # This workaround will become obsolet when migrating to src-layout sys.path.append(str(pathlib.Path(__file__).parent)) import diagnostics # testing target class Diagnostics(unittest.TestCase): """Test about collecting diagnostic infos.""" def test_content_minimal(self): """Minimal set of elements.""" sut = diagnostics.collect_minimal_diagnostics() # 1st level keys self.assertCountEqual(sut.keys(), ['backintime', 'host-setup']) # 2nd level "backintime" self.assertCountEqual( sut['backintime'].keys(), ['name', 'version', 'running-as-root']) # 2nd level "host-setup" self.assertCountEqual(sut['host-setup'].keys(), ['OS']) def test_some_content(self): """Some contained elements""" result = diagnostics.collect_diagnostics() # 1st level keys self.assertCountEqual( result.keys(), ['backintime', 'external-programs', 'host-setup', 'python-setup'] ) # 2nd level "backintime" minimal_keys = ['name', 'version', 'latest-config-version', 'started-from', 'running-as-root'] for key in minimal_keys: self.assertIn(key, result['backintime'], key) # 2nd level "host-setup" minimal_keys = ['platform', 'system', 'display-system', 'locale', 'PATH', 'RSYNC_OLD_ARGS', 'RSYNC_PROTECT_ARGS'] for key in minimal_keys: self.assertIn(key, result['host-setup'], key) # 2nd level "python-setup" self.assertIn('python', result['python-setup'], 'python') # 2nd level "external-programs" minimal_keys = ['rsync', 'shell'] for key in minimal_keys: self.assertIn(key, result['external-programs'], key) def test_no_ressource_warning(self): """No ResourceWarning's. Using subprocess.Popen() often cause ResourceWarning's when not used as a context manaager. """ # an AssertionError must be raised! See next block for explanation. with self.assertRaises(AssertionError): # We expect NO ResourceWarnings. But Python doesn't offer # assertNoWarns(). # This will raise an AssertionError because no ResourceWarning's # are raised. with self.assertWarns(ResourceWarning): diagnostics.collect_diagnostics() def test_no_extern_version(self): """Get version from not existing tool.""" self.assertEqual( diagnostics._get_extern_versions(['fooXbar']), '(no fooXbar)' ) def test_replace_user_path(self): """Replace users path.""" d = { 'foo': '/home/rsync', 'bar': '~/rsync' } self.assertEqual( diagnostics._replace_username_paths(d, 'rsync'), { 'foo': '/home/UsernameReplaced', 'bar': '~/UsernameReplaced' } ) self.assertEqual( diagnostics._replace_username_paths(d, 'user'), d ) backintime-1.5.2/common/test/test_encfstools.py000066400000000000000000000031071465446530500217010ustar00rootroot00000000000000# Back In Time # Copyright (C) 2016-2022 Taylor Raack, 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 sys from test import generic sys.path.append(os.path.join(os.path.dirname(__file__), '..')) class TestEncFS_mount(generic.TestCase): # encrypted filesystem verification seems quite complex to unit test at the moment, partially due to # UI elements being created and expecting input, without tests for pre-prepared unit test fixture data # TODO - perhaps pass encrypted fs class object which can be queried for passwords when necessary (so runtime # can pass UI classes which can bring up actual UI elements and return credentials or unit tests can pass # objects which can return hard coded passwords to prevent UI pop-ups in Travis) # TODO - then - code actual tests and remove this one def setUp(self): super(TestEncFS_mount, self).setUp() def test_dummy(self): self.assertTrue(True) backintime-1.5.2/common/test/test_lint.py000066400000000000000000000116371465446530500204770ustar00rootroot00000000000000import unittest import os import pathlib import subprocess import shutil from typing import Iterable ON_TRAVIS = os.environ.get('TRAVIS', '') == 'true' PYLINT_AVIALBE = not shutil.which('pylint') is None PYLINT_REASON = ('Using PyLint is mandatory on TravisCI, on other systems' 'it runs only if `pylint` is available.') 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). """ def _collect_py_files(self) -> 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. return path.rglob('**/*.py') @unittest.skipUnless(ON_TRAVIS or PYLINT_AVIALBE, PYLINT_REASON) def test_with_pylint(self): """Use Pylint to check for specific error codes. Some facts about PyLint - It is one of the slowest available linters. - It is able to catch lints other linters miss. """ # Pylint base command 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', # Deactivate all checks by default '--disable=all', # 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) '--max-line-length=79', # Whitelist variable names '--good-names=idx,fp', # '--reports=yes', ] # Explicit activate checks err_codes = [ 'C0305', # trailing-newlines 'C0325', # superfluous-parens 'C0410', # multiple-imports 'C0303', # trailing-whitespace '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 'W0311', # bad-indentation 'I0021', # useless-suppression # 'W0611', # unused-import 'W1301', # unused-format-string-key 'W1401', # anomalous-backslash-in-string (invalid escape sequence) 'W1515', # forgotten-debug-statement 'R0201', # no-self-use 'R0202', # no-classmethod-decorator 'R0203', # no-staticmethod-decorator 'W0404', # reimported 'W4902', # deprecated-method 'W4904', # deprecated-class 'W0614', # unused-wildcard-import 'W0123', # eval-used 'W0707', # raise-missing-from # Enable asap. This list is a selection of existing (not all!) # problems currently existing in the BIT code base. Quite easy to fix # because their count is low. # 'R0801', # duplicate-code # 'W0237', # arguments-renamed # 'W0221', # arguments-differ # 'W0603', # global-statement # 'W0612', # unused-variable ] cmd.append('--enable=' + ','.join(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 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, '') backintime-1.5.2/common/test/test_plugin_usercallback.py000066400000000000000000000247441465446530500235450ustar00rootroot00000000000000import sys import inspect import tempfile from pathlib import Path import stat import io from datetime import datetime import unittest import unittest.mock as mock import json from contextlib import redirect_stdout, redirect_stderr from ast import literal_eval # This workaround will become obsolet when migrating to src-layout sys.path.append(str(Path(__file__).parent)) sys.path.append(str(Path(__file__).parent / 'plugins')) import logger import pluginmanager from config import Config from snapshots import Snapshots, SID from usercallbackplugin import UserCallbackPlugin class UserCallback(unittest.TestCase): """Simple test related to to UserCallbackPlugin class. Dev note (buhtz, 2024-02-08): Test value is low because they depend on implementation and are not robust against refactoring the productive code. Some observations and suggestions: - Rename method UserCallbackPlugin.init() - Make UserCallbackPlugin.callback() private - UserCallbackPlugin.callback() : Encapsulating the Popen() part would improve the mocking. - Unit tests about logger output. But migrate "logger" to Python's inbuild "logging" module first. """ def _generic_called_with(self, the_step, reason, *args): sut = UserCallbackPlugin() sut.config = Config() sut.script = '' mock_name = 'usercallbackplugin.UserCallbackPlugin.callback' with mock.patch(mock_name) as func_callback: the_step(sut, *args) func_callback.assert_called_once() func_callback.assert_called_with(reason, *args) def test_reason_processBegin(self): self._generic_called_with(UserCallbackPlugin.processBegin, '1') def test_reason_processEnd(self): self._generic_called_with(UserCallbackPlugin.processEnd, '2') def test_reason_processnewSnapshot(self): self._generic_called_with(UserCallbackPlugin.newSnapshot, '3', 'id1', 'path') def test_reason_error(self): sut = UserCallbackPlugin() sut.config = Config() sut.script = '' mock_name = 'usercallbackplugin.UserCallbackPlugin.callback' # with error message with mock.patch(mock_name) as func_callback: sut.error('code1', 'message') func_callback.assert_called_once() func_callback.assert_called_with('4', 'code1', 'message') # no error message with mock.patch(mock_name) as func_callback: sut.error('code2', None) func_callback.assert_called_once() func_callback.assert_called_with('4', 'code2') def test_reason_appStart(self): self._generic_called_with(UserCallbackPlugin.appStart, '5') def test_reason_appExit(self): self._generic_called_with(UserCallbackPlugin.appExit, '6') def test_reason_mount(self): sut = UserCallbackPlugin() sut.config = Config() sut.script = '' mock_name = 'usercallbackplugin.UserCallbackPlugin.callback' # No profileID with mock.patch(mock_name) as func_callback: sut.mount() func_callback.assert_called_once() func_callback.assert_called_with('7', profileID=None) # With profileID with mock.patch(mock_name) as func_callback: sut.mount('123') func_callback.assert_called_once() func_callback.assert_called_with('7', profileID='123') def test_reason_unmount(self): sut = UserCallbackPlugin() sut.config = Config() sut.script = '' mock_name = 'usercallbackplugin.UserCallbackPlugin.callback' # No profileID with mock.patch(mock_name) as func_callback: sut.unmount() func_callback.assert_called_once() func_callback.assert_called_with('8', profileID=None) # With profileID with mock.patch(mock_name) as func_callback: sut.unmount('987') func_callback.assert_called_once() func_callback.assert_called_with('8', profileID='987') class SystemTest(unittest.TestCase): """Full backup run and parsing the log output for the expected user-callback returns in correct order. Create and use your own config file and take it over via `--config` option. Place your own user-callback script in the same folder as this config file. """ @classmethod def _create_user_callback_file(cls, parent_path): content = inspect.cleandoc(''' #!/usr/bin/env python3 import sys print(sys.argv[1:]) ''') callback_fp = parent_path / 'user-callback' callback_fp.write_text(content, 'utf-8') callback_fp.chmod(stat.S_IRWXU) # Name of folder with files to backup. NAME_SOURCE = 'src' # Name of folder where snapshots (backups) are stored in. NAME_DESTINATION = 'dest' @classmethod def _extract_callback_responses(cls, output): """Extract response of user-callback script out of log output. See https://github.com/bit-team/user-callback for documentation about user-callback and the response codes. Example :: # Raw output INFO: user-callback returned '['1', 'Main profile', '2']' INFO: Something else INFO: user-callback returned '['1', 'Main profile', '8']' # Result in a two entry list [ ['1', 'Main profile', '2'] ['1', 'Main profile', '8'] ] Returns: A list of response values as lists. First entry is profile, second is profile id, third is reason code. If available further entries could be contained. """ if isinstance(output, str): output = output.splitlines() # only log lines related to user-callback response_lines = filter( lambda line: 'user-callback returned' in line, output) callback_responses = [] for line in response_lines: to_eval = line[line.index("'")+1:line.rindex("'")] callback_responses.append( literal_eval(line[line.index("'")+1:line.rindex("'")]) ) # Workaround: Cast profile-id and response-code to integers for idx in range(len(callback_responses)): callback_responses[idx][0] = int(callback_responses[idx][0]) callback_responses[idx][2] = int(callback_responses[idx][2]) return callback_responses @classmethod def _create_source_and_destination_folders(cls, parent_path): # Folder to backup src_path = parent_path / cls.NAME_SOURCE src_path.mkdir() # Files and folders as backup content (src_path / 'one').write_bytes(b'0123') (src_path / 'subfolder').mkdir() (src_path / 'subfolder' / 'two').write_bytes(b'4567') # Folder to store backup dest_path = parent_path / cls.NAME_DESTINATION dest_path.mkdir() @classmethod def _create_config_file(cls, parent_path): cfg_content = inspect.cleandoc(''' config.version=6 profile1.snapshots.include.1.type=0 profile1.snapshots.include.1.value={rootpath}/{source} profile1.snapshots.include.size=1 profile1.snapshots.no_on_battery=false profile1.snapshots.notify.enabled=true profile1.snapshots.path={rootpath}/{destination} profile1.snapshots.path.host=test-host profile1.snapshots.path.profile=1 profile1.snapshots.path.user=test-user profile1.snapshots.preserve_acl=false profile1.snapshots.preserve_xattr=false profile1.snapshots.remove_old_snapshots.enabled=true profile1.snapshots.remove_old_snapshots.unit=80 profile1.snapshots.remove_old_snapshots.value=10 profile1.snapshots.rsync_options.enabled=false profile1.snapshots.rsync_options.value= profiles.version=1 ''') cfg_content = cfg_content.format( rootpath=parent_path, source=cls.NAME_SOURCE, destination=cls.NAME_DESTINATION ) # config file location config_fp = parent_path / 'config_path' / 'config' config_fp.parent.mkdir() config_fp.write_text(cfg_content, 'utf-8') return config_fp def setUp(self): """Setup a local snapshot profile including a user-callback""" # cleanup() happens automatically self._temp_dir = tempfile.TemporaryDirectory(prefix='bit.') # Workaround: tempfile and pathlib not compatible yet self.temp_path = Path(self._temp_dir.name) self._create_source_and_destination_folders(self.temp_path) self.config_fp = self._create_config_file(self.temp_path) self._create_user_callback_file(self.config_fp.parent) # Reset this instance because it is not isolated between tests. Config.PLUGIN_MANAGER = pluginmanager.PluginManager() def test_local_snapshot(self): """User-callback response while doing a local snapshot""" config = Config( config_path=str(self.config_fp), data_path=str(self.temp_path / '.local' / 'share') ) full_snapshot_path = config.snapshotsFullPath() Path(full_snapshot_path).mkdir(parents=True) snapshot = Snapshots(config) # DevNote : Because BIT don't use Python's logging module there is # no way to use assertLogs(). Current solution is to capture # stdout/stderr. stdout = io.StringIO() stderr = io.StringIO() with redirect_stdout(stdout), redirect_stderr(stderr): # Result is inverted. 'True' means there was an error. self.assertFalse(snapshot.backup()) # Empty STDOUT output self.assertFalse(stdout.getvalue()) responses = self._extract_callback_responses(stderr.getvalue()) # Number of responses self.assertEqual(5, len(responses)) # Test Name and ID self.assertEqual( {(1, 'Main profile')}, # de-duplicate (using set() )by first two elements in each entry {(entry[0], entry[1]) for entry in responses} ) # Order of response codes self.assertEqual( [ 7, # Mount 1, # Backup begins 3, # New snapshot was taken 2, # Backup ends 8, # Unmount ], [entry[2] for entry in responses] ) backintime-1.5.2/common/test/test_restore.py000066400000000000000000000171621465446530500212130ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 sys import unittest import pwd import grp import stat from tempfile import TemporaryDirectory from test import generic sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import config import snapshots import mount CURRENTUID = os.geteuid() CURRENTUSER = pwd.getpwuid(CURRENTUID).pw_name CURRENTGID = os.getegid() CURRENTGROUP = grp.getgrgid(CURRENTGID).gr_name class RestoreTestCase(generic.SnapshotsWithSidTestCase): def setUp(self): super(RestoreTestCase, self).setUp() self.include = TemporaryDirectory() generic.create_test_files(self.sid.pathBackup(self.include.name)) def tearDown(self): super(RestoreTestCase, self).tearDown() self.include.cleanup() def prepairFileInfo(self, restoreFile, mode = 33260): d = self.sid.fileInfo d[restoreFile.encode('utf-8', 'replace')] = (mode, CURRENTUSER.encode('utf-8', 'replace'), CURRENTGROUP.encode('utf-8', 'replace')) self.sid.fileInfo = d class TestRestore(RestoreTestCase): def test_restore_multiple_files(self): restoreFile1 = os.path.join(self.include.name, 'test') self.prepairFileInfo(restoreFile1) restoreFile2 = os.path.join(self.include.name, 'foo', 'bar', 'baz') self.prepairFileInfo(restoreFile2) self.sn.restore(self.sid, (restoreFile1, restoreFile2)) self.assertIsFile(restoreFile1) with open(restoreFile1, 'rt') as f: self.assertEqual(f.read(), 'bar') self.assertEqual(33260, os.stat(restoreFile1).st_mode) self.assertIsFile(restoreFile2) with open(restoreFile2, 'rt') as f: self.assertEqual(f.read(), 'foo') self.assertEqual(33260, os.stat(restoreFile2).st_mode) def test_restore_to_different_destination(self): restoreFile = os.path.join(self.include.name, 'test') self.prepairFileInfo(restoreFile) with TemporaryDirectory() as dest: destRestoreFile = os.path.join(dest, 'test') self.sn.restore(self.sid, restoreFile, restore_to = dest) self.assertIsFile(destRestoreFile) with open(destRestoreFile, 'rt') as f: self.assertEqual(f.read(), 'bar') self.assertEqual(33260, os.stat(destRestoreFile).st_mode) def test_restore_folder_to_different_destination(self): restoreFolder = self.include.name self.prepairFileInfo(restoreFolder) self.prepairFileInfo(os.path.join(restoreFolder, 'test')) self.prepairFileInfo(os.path.join(restoreFolder, 'file with spaces')) with TemporaryDirectory() as dest: destRestoreFile = os.path.join(dest, os.path.basename(restoreFolder), 'test') self.sn.restore(self.sid, restoreFolder, restore_to = dest) self.assertIsFile(destRestoreFile) with open(destRestoreFile, 'rt') as f: self.assertEqual(f.read(), 'bar') self.assertEqual(33260, os.stat(destRestoreFile).st_mode) def test_delete(self): restoreFolder = self.include.name junkFolder = os.path.join(self.include.name, 'junk') os.makedirs(junkFolder) self.assertExists(junkFolder) self.prepairFileInfo(restoreFolder) self.sn.restore(self.sid, restoreFolder, delete = True) self.assertIsFile(restoreFolder, 'test') self.assertNotExists(junkFolder) def test_backup(self): restoreFile = os.path.join(self.include.name, 'test') self.prepairFileInfo(restoreFile) with open(restoreFile, 'wt') as f: f.write('fooooooooooooooooooo') self.sn.restore(self.sid, restoreFile, backup = True) self.assertIsFile(restoreFile) with open(restoreFile, 'rt') as f: self.assertEqual(f.read(), 'bar') backupFile = restoreFile + self.sn.backupSuffix() self.assertIsFile(backupFile) with open(backupFile, 'rt') as f: self.assertEqual(f.read(), 'fooooooooooooooooooo') def test_no_backup(self): restoreFile = os.path.join(self.include.name, 'test') self.prepairFileInfo(restoreFile) with open(restoreFile, 'wt') as f: f.write('fooooooooooooooooooo') self.sn.restore(self.sid, restoreFile, backup = False) self.assertIsFile(restoreFile) with open(restoreFile, 'rt') as f: self.assertEqual(f.read(), 'bar') backupFile = restoreFile + self.sn.backupSuffix() self.assertIsNoFile(backupFile) def test_only_new(self): restoreFile = os.path.join(self.include.name, 'test') self.prepairFileInfo(restoreFile) with open(restoreFile, 'wt') as f: f.write('fooooooooooooooooooo') # change mtime to be newer than the one in snapshot st = os.stat(restoreFile) atime = st[stat.ST_ATIME] mtime = st[stat.ST_MTIME] new_mtime = mtime + 3600 os.utime(restoreFile, (atime, new_mtime)) self.sn.restore(self.sid, restoreFile, only_new = True) self.assertIsFile(restoreFile) with open(restoreFile, 'rt') as f: self.assertEqual(f.read(), 'fooooooooooooooooooo') class TestRestoreLocal(RestoreTestCase): """ Tests which should run on local and ssh profile """ def test_restore(self): restoreFile = os.path.join(self.include.name, 'test') self.prepairFileInfo(restoreFile) self.sn.restore(self.sid, restoreFile) self.assertIsFile(restoreFile) with open(restoreFile, 'rt') as f: self.assertEqual(f.read(), 'bar') self.assertEqual(33260, os.stat(restoreFile).st_mode) def test_restore_file_with_spaces(self): restoreFile = os.path.join(self.include.name, 'file with spaces') self.prepairFileInfo(restoreFile) self.sn.restore(self.sid, restoreFile) self.assertIsFile(restoreFile) with open(restoreFile, 'rt') as f: self.assertEqual(f.read(), 'asdf') self.assertEqual(33260, os.stat(restoreFile).st_mode) @unittest.skipIf(not generic.LOCAL_SSH, 'Skip as this test requires a local ssh server, public and private keys installed') class TestRestoreSSH(generic.SSHSnapshotsWithSidTestCase, TestRestoreLocal): """BUHTZ 2022-10-09: Seems to me that testing restore via SSH isn't implemented yet. """ def setUp(self): super(TestRestoreSSH, self).setUp() self.include = TemporaryDirectory() generic.create_test_files(os.path.join(self.remoteSIDBackupPath, self.include.name[1:])) #mount self.cfg.setCurrentHashId(mount.Mount(cfg = self.cfg).mount()) def tearDown(self): #unmount mount.Mount(cfg = self.cfg).umount(self.cfg.current_hash_id) super(TestRestoreSSH, self).tearDown() self.include.cleanup() backintime-1.5.2/common/test/test_schedule.py000066400000000000000000000051311465446530500213150ustar00rootroot00000000000000# SPDX-FileCopyrightText: © 2024 Christian BUHTZ # # SPDX-License-Identifier: GPL-2.0 # # This file is part of the program "Back In time" which is released under GNU # General Public License v2 (GPLv2). # See file LICENSE or go to . import unittest import schedule class Schedule(unittest.TestCase): """Manipulation of crontab content""" def test_remove_bit_entries(self): """Remove BIT entries from crontab. Three BIT entries in the crontab. The 1st and 3rd are auto generated by BIT schedule feature and will be removed. But the second is user defined and need to stay. """ content = [ '# -------------- min (0 - 59)', '# | --------------- hour (0 - 23)', '# | | ---------------- day of month (1 - 31),' '# | | | ----------------- month (1 - 12)', '# | | | | ------------------ day of week (0 - 6)', '# Saturday, or use names; 7 is Sunday, the same as 0)', '# | | | | |', '# | | | | |', '# * * * * *', '', '30/* * * * * dohyperorg > /def/null', '', '51 */3 * * * cronjobblock ' '/home/user/.my.scripts/thunderbird_backup_duplicity.sh', '', '#Back In Time system entry, this will be edited by the gui:', '0 8,12,18,23 * * * /usr/bin/nice -n19 /usr/bin/ionice -c2 -n7 ' '/usr/bin/backintime backup-job >/dev/null', '0 2 3 4 5 /usr/bin/nice -n19 /usr/bin/ionice -c2 -n7 ' '/usr/bin/backintime --profile-id 7 backup-job >/dev/null', '#Back In Time system entry, this will be edited by the gui:', '0 0 1 1 * /usr/bin/nice -n19 /usr/bin/ionice -c2 -n7 ' '/usr/bin/backintime --profile-id 3 backup-job >/dev/null', ] expect = content[:] # Remove the last BIT entry incl. comment del expect[-1] del expect[-1] # Remove the one before the one before the last del expect[-2] del expect[-2] result = schedule.remove_bit_from_crontab(content) self.assertEqual(result, expect) self.assertIn('--profile-id 7', result[-1]) def test_bit_to_crontab(self): result = schedule.append_bit_to_crontab( [], ['foo', 'bar'] ) self.assertEqual(len(result), 4) self.assertTrue(result[0][0], '#') self.assertTrue(result[1], 'foo') self.assertTrue(result[2][0], '#') self.assertTrue(result[3], 'bar') backintime-1.5.2/common/test/test_sid.py000066400000000000000000000534641465446530500203140ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 sys import unittest import stat import re from datetime import date, datetime from test import generic from unittest.mock import patch sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import configfile import snapshots import logger from snapshotlog import LogFilter, SnapshotLog class TestSID(generic.SnapshotsTestCase): def test_new_object_with_valid_date(self): sid1 = snapshots.SID('20151219-010324-123', self.cfg) sid2 = snapshots.SID('20151219-010324', self.cfg) sid3 = snapshots.SID(datetime(2015, 12, 19, 1, 3, 24), self.cfg) sid4 = snapshots.SID(date(2015, 12, 19), self.cfg) self.assertEqual(sid1.sid, '20151219-010324-123') self.assertEqual(sid2.sid, '20151219-010324') self.assertRegex(sid3.sid, r'20151219-010324-\d{3}') self.assertRegex(sid4.sid, r'20151219-000000-\d{3}') def test_new_object_with_invalid_value(self): with self.assertRaises(ValueError): snapshots.SID('20151219-010324-1234', self.cfg) with self.assertRaises(ValueError): snapshots.SID('20151219-01032', self.cfg) with self.assertRaises(ValueError): snapshots.SID('2015121a-010324-1234', self.cfg) def test_new_object_with_invalid_type(self): with self.assertRaises(TypeError): snapshots.SID(123, self.cfg) def test_equal_sid(self): sid1a = snapshots.SID('20151219-010324-123', self.cfg) sid1b = snapshots.SID('20151219-010324-123', self.cfg) sid2 = snapshots.SID('20151219-020324-123', self.cfg) self.assertIsNot(sid1a, sid1b) self.assertTrue(sid1a == sid1b) self.assertTrue(sid1a == '20151219-010324-123') self.assertTrue(sid1a != sid2) self.assertTrue(sid1a != '20151219-020324-123') def test_sort_sids(self): root = snapshots.RootSnapshot(self.cfg) new = snapshots.NewSnapshot(self.cfg) sid1 = snapshots.SID('20151219-010324-123', self.cfg) sid2 = snapshots.SID('20151219-020324-123', self.cfg) sid3 = snapshots.SID('20151219-030324-123', self.cfg) sid4 = snapshots.SID('20151219-040324-123', self.cfg) sids1 = [sid3, sid1, sid4, sid2] sids1.sort() self.assertEqual(sids1, [sid1, sid2, sid3, sid4]) #RootSnapshot 'Now' should always stay on top sids2 = [sid3, sid1, root, sid4, sid2] sids2.sort() self.assertEqual(sids2, [sid1, sid2, sid3, sid4, root]) sids2.sort(reverse = True) self.assertEqual(sids2, [root, sid4, sid3, sid2, sid1]) #new_snapshot should always be the last sids3 = [sid3, sid1, new, sid4, sid2] sids3.sort() self.assertEqual(sids3, [sid1, sid2, sid3, sid4, new]) sids3.sort(reverse = True) self.assertEqual(sids3, [new, sid4, sid3, sid2, sid1]) def test_hash(self): sid1a = snapshots.SID('20151219-010324-123', self.cfg) sid1b = snapshots.SID('20151219-010324-123', self.cfg) sid2 = snapshots.SID('20151219-020324-123', self.cfg) self.assertEqual(sid1a.__hash__(), sid1b.__hash__()) self.assertNotEqual(sid1a.__hash__(), sid2.__hash__()) s = set() s.add(sid1a) self.assertEqual(len(s), 1) s.add(sid2) self.assertEqual(len(s), 2) s.add(sid1b) self.assertEqual(len(s), 2) def test_split(self): sid = snapshots.SID('20151219-010324-123', self.cfg) self.assertTupleEqual(sid.split(), (2015, 12, 19, 1, 3, 24)) def test_displayID(self): sid = snapshots.SID('20151219-010324-123', self.cfg) self.assertEqual(sid.displayID, '2015-12-19 01:03:24') def test_displayName(self): sid = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) with open(sid.path('name'), 'wt') as f: f.write('foo') self.assertEqual(sid.displayName, '2015-12-19 01:03:24 - foo') with open(sid.path('failed'), 'wt') as f: pass self.assertRegex(sid.displayName, r'2015-12-19 01:03:24 - foo (.+?)') def test_withoutTag(self): sid = snapshots.SID('20151219-010324-123', self.cfg) self.assertEqual(sid.withoutTag, '20151219-010324') def test_tag(self): sid = snapshots.SID('20151219-010324-123', self.cfg) self.assertEqual(sid.tag, '123') def test_path(self): sid = snapshots.SID('20151219-010324-123', self.cfg) self.assertEqual(sid.path(), os.path.join(self.snapshotPath, '20151219-010324-123')) self.assertEqual(sid.path('foo', 'bar', 'baz'), os.path.join(self.snapshotPath, '20151219-010324-123', 'foo', 'bar', 'baz')) self.assertEqual(sid.pathBackup(), os.path.join(self.snapshotPath, '20151219-010324-123', 'backup')) self.assertEqual(sid.pathBackup('/foo'), os.path.join(self.snapshotPath, '20151219-010324-123', 'backup', 'foo')) def test_makeDirs(self): sid = snapshots.SID('20151219-010324-123', self.cfg) self.assertTrue(sid.makeDirs()) self.assertTrue(os.path.isdir(os.path.join(self.snapshotPath, '20151219-010324-123', 'backup'))) self.assertTrue(sid.makeDirs('foo', 'bar', 'baz')) self.assertTrue(os.path.isdir(os.path.join(self.snapshotPath, '20151219-010324-123', 'backup', 'foo', 'bar', 'baz'))) def test_exists(self): sid = snapshots.SID('20151219-010324-123', self.cfg) self.assertFalse(sid.exists()) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) self.assertFalse(sid.exists()) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123', 'backup')) self.assertTrue(sid.exists()) def test_isExistingPathInsideSnapshotFolder(self): sid = snapshots.SID('20151219-010324-123', self.cfg) backupPath = os.path.join(self.snapshotPath, '20151219-010324-123', 'backup') os.makedirs(os.path.join(backupPath, 'foo')) #test existing file and non existing file self.assertTrue(sid.isExistingPathInsideSnapshotFolder('/foo')) self.assertFalse(sid.isExistingPathInsideSnapshotFolder('/tmp')) #test valid absolute symlink inside snapshot os.symlink(os.path.join(backupPath, 'foo'), os.path.join(backupPath, 'bar')) self.assertIsLink(backupPath, 'bar') self.assertTrue(sid.isExistingPathInsideSnapshotFolder('/bar')) #test valid relative symlink inside snapshot os.symlink('./foo', os.path.join(backupPath, 'baz')) self.assertIsLink(backupPath, 'baz') self.assertTrue(sid.isExistingPathInsideSnapshotFolder('/baz')) #test invalid symlink os.symlink(os.path.join(backupPath, 'asdf'), os.path.join(backupPath, 'qwer')) self.assertIsLink(backupPath, 'qwer') self.assertFalse(sid.isExistingPathInsideSnapshotFolder('/qwer')) #test valid symlink outside snapshot os.symlink('/tmp', os.path.join(backupPath, 'bla')) self.assertIsLink(backupPath, 'bla') self.assertFalse(sid.isExistingPathInsideSnapshotFolder('/bla')) def test_name(self): sid = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) self.assertEqual(sid.name, '') sid.name = 'foo' with open(sid.path('name'), 'rt') as f: self.assertEqual(f.read(), 'foo') self.assertEqual(sid.name, 'foo') def test_lastChecked(self): sid = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) infoFile = os.path.join(self.snapshotPath, '20151219-010324-123', 'info') #no info file self.assertEqual(sid.lastChecked, '2015-12-19 01:03:24') #set time manually to 2015-12-19 02:03:24 with open(infoFile, 'wt'): pass d = datetime(2015, 12, 19, 2, 3, 24) os.utime(infoFile, (d.timestamp(), d.timestamp())) self.assertEqual(sid.lastChecked, '2015-12-19 02:03:24') #setLastChecked and check if it matches current date sid.setLastChecked() now = datetime.now() self.assertRegex(sid.lastChecked, now.strftime(r'%Y-%m-%d %H:%M:\d{2}')) def test_failed(self): sid = snapshots.SID('20151219-010324-123', self.cfg) snapshotPath = os.path.join(self.snapshotPath, '20151219-010324-123') failedPath = os.path.join(snapshotPath, sid.FAILED) os.makedirs(snapshotPath) self.assertNotExists(failedPath) self.assertFalse(sid.failed) sid.failed = True self.assertExists(failedPath) self.assertTrue(sid.failed) sid.failed = False self.assertNotExists(failedPath) self.assertFalse(sid.failed) def test_info(self): sid1 = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) infoFile = os.path.join(self.snapshotPath, '20151219-010324-123', 'info') i1 = configfile.ConfigFile() i1.setStrValue('foo', 'bar') sid1.info = i1 #test if file exist and has correct content self.assertIsFile(infoFile) with open(infoFile, 'rt') as f: self.assertEqual(f.read(), 'foo=bar\n') #new sid instance and test if correct value is returned sid2 = snapshots.SID('20151219-010324-123', self.cfg) i2 = sid2.info self.assertEqual(i2.strValue('foo', 'default'), 'bar') def test_fileInfo(self): sid1 = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) infoFile = os.path.join(self.snapshotPath, '20151219-010324-123', 'fileinfo.bz2') d = snapshots.FileInfoDict() d[b'/tmp'] = (123, b'foo', b'bar') d[b'/tmp/foo'] = (456, b'asdf', b'qwer') sid1.fileInfo = d self.assertIsFile(infoFile) #load fileInfo in a new snapshot sid2 = snapshots.SID('20151219-010324-123', self.cfg) self.assertDictEqual(sid2.fileInfo, d) @patch('logger.error') def test_fileInfoErrorRead(self, mock_logger): sid = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) infoFile = sid.path(sid.FILEINFO) # remove all permissions from file with open(infoFile, 'wt') as f: pass with generic.mockPermissions(infoFile): self.assertEqual(sid.fileInfo, snapshots.FileInfoDict()) self.assertTrue(mock_logger.called) @patch('logger.error') def test_fileInfoErrorWrite(self, mock_logger): sid = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) infoFile = sid.path(sid.FILEINFO) # remove all permissions from file with open(infoFile, 'wt') as f: pass with generic.mockPermissions(infoFile): d = snapshots.FileInfoDict() d[b'/tmp'] = (123, b'foo', b'bar') d[b'/tmp/foo'] = (456, b'asdf', b'qwer') sid.fileInfo = d self.assertTrue(mock_logger.called) def test_log(self): sid = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) logFile = os.path.join(self.snapshotPath, '20151219-010324-123', 'takesnapshot.log.bz2') #no log available self.assertRegex('\n'.join(sid.log()), r'Failed to get snapshot log from.*') sid.setLog('foo bar\nbaz') self.assertIsFile(logFile) self.assertEqual('\n'.join(sid.log()), 'foo bar\nbaz') def test_log_filter(self): sid = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) logFile = os.path.join(self.snapshotPath, '20151219-010324-123', 'takesnapshot.log.bz2') sid.setLog('foo bar\n[I] 123\n[C] baz\n[E] bla') self.assertIsFile(logFile) self.assertEqual('\n'.join(sid.log(mode = LogFilter.CHANGES)), 'foo bar\n[C] baz') def test_setLog_binary(self): sid = snapshots.SID('20151219-010324-123', self.cfg) os.makedirs(os.path.join(self.snapshotPath, '20151219-010324-123')) logFile = os.path.join(self.snapshotPath, '20151219-010324-123', 'takesnapshot.log.bz2') sid.setLog(b'foo bar\nbaz') self.assertIsFile(logFile) self.assertEqual('\n'.join(sid.log()), 'foo bar\nbaz') def test_makeWritable(self): sid = snapshots.SID('20151219-010324-123', self.cfg) sidPath = os.path.join(self.snapshotPath, '20151219-010324-123') os.makedirs(sidPath) testFile = os.path.join(sidPath, 'test') #make only read and explorable os.chmod(sidPath, stat.S_IRUSR | stat.S_IXUSR) with self.assertRaises(PermissionError): with open(testFile, 'wt') as f: f.write('foo') sid.makeWritable() self.assertEqual(os.stat(sidPath).st_mode & stat.S_IWUSR, stat.S_IWUSR) try: with open(testFile, 'wt') as f: f.write('foo') except PermissionError: msg = 'writing to {} raised PermissionError unexpectedly!' self.fail(msg.format(testFile)) class TestNewSnapshot(generic.SnapshotsTestCase): def test_create_new(self): new = snapshots.NewSnapshot(self.cfg) self.assertFalse(new.exists()) self.assertTrue(new.makeDirs()) self.assertTrue(new.exists()) self.assertTrue(os.path.isdir(os.path.join(self.snapshotPath, new.NEWSNAPSHOT, 'backup'))) def test_saveToContinue(self): new = snapshots.NewSnapshot(self.cfg) snapshotPath = os.path.join(self.snapshotPath, new.NEWSNAPSHOT) saveToContinuePath = os.path.join(snapshotPath, new.SAVETOCONTINUE) os.makedirs(snapshotPath) self.assertNotExists(saveToContinuePath) self.assertFalse(new.saveToContinue) new.saveToContinue = True self.assertExists(saveToContinuePath) self.assertTrue(new.saveToContinue) new.saveToContinue = False self.assertNotExists(saveToContinuePath) self.assertFalse(new.saveToContinue) def test_hasChanges(self): now = datetime(2016, 7, 10, 16, 24, 17) new = snapshots.NewSnapshot(self.cfg) new.makeDirs() log = SnapshotLog(self.cfg) log.new(now) self.assertFalse(new.hasChanges) log.append('[I] foo', log.ALL) log.append('[E] bar', log.ALL) log.flush() self.assertFalse(new.hasChanges) log.append('[C] baz', log.ALL) log.flush() self.assertTrue(new.hasChanges) class TestRootSnapshot(generic.SnapshotsTestCase): #TODO: add test with 'sid.path(use_mode=['ssh_encfs'])' def test_create(self): sid = snapshots.RootSnapshot(self.cfg) self.assertTrue(sid.isRoot) self.assertEqual(sid.sid, '/') self.assertEqual(sid.name, 'Now') def test_path(self): sid = snapshots.RootSnapshot(self.cfg) self.assertEqual(sid.path(), '/') self.assertEqual(sid.path('foo', 'bar'), '/foo/bar') class TestIterSnapshots(generic.SnapshotsTestCase): def setUp(self): super(TestIterSnapshots, self).setUp() for i in ('20151219-010324-123', '20151219-020324-123', '20151219-030324-123', '20151219-040324-123'): os.makedirs(os.path.join(self.snapshotPath, i, 'backup')) def test_list_valid(self): l1 = snapshots.listSnapshots(self.cfg) self.assertListEqual(l1, ['20151219-040324-123', '20151219-030324-123', '20151219-020324-123', '20151219-010324-123']) self.assertIsInstance(l1[0], snapshots.SID) def test_list_new_snapshot(self): os.makedirs(os.path.join(self.snapshotPath, 'new_snapshot', 'backup')) l2 = snapshots.listSnapshots(self.cfg, includeNewSnapshot = True) self.assertListEqual(l2, ['new_snapshot', '20151219-040324-123', '20151219-030324-123', '20151219-020324-123', '20151219-010324-123']) self.assertIsInstance(l2[0], snapshots.NewSnapshot) self.assertIsInstance(l2[-1], snapshots.SID) def test_list_snapshot_without_backup(self): #new snapshot without backup folder shouldn't be added os.makedirs(os.path.join(self.snapshotPath, '20151219-050324-123')) l3 = snapshots.listSnapshots(self.cfg) self.assertListEqual(l3, ['20151219-040324-123', '20151219-030324-123', '20151219-020324-123', '20151219-010324-123']) def test_list_invalid_snapshot(self): #invalid snapshot shouldn't be added os.makedirs(os.path.join(self.snapshotPath, '20151219-000324-abc', 'backup')) l4 = snapshots.listSnapshots(self.cfg) self.assertListEqual(l4, ['20151219-040324-123', '20151219-030324-123', '20151219-020324-123', '20151219-010324-123']) def test_list_without_new_snapshot(self): os.makedirs(os.path.join(self.snapshotPath, 'new_snapshot', 'backup')) l5 = snapshots.listSnapshots(self.cfg, includeNewSnapshot = False) self.assertListEqual(l5, ['20151219-040324-123', '20151219-030324-123', '20151219-020324-123', '20151219-010324-123']) def test_list_symlink_last_snapshot(self): os.symlink('./20151219-040324-123', os.path.join(self.snapshotPath, 'last_snapshot')) l6 = snapshots.listSnapshots(self.cfg) self.assertListEqual(l6, ['20151219-040324-123', '20151219-030324-123', '20151219-020324-123', '20151219-010324-123']) def test_list_not_reverse(self): os.makedirs(os.path.join(self.snapshotPath, 'new_snapshot', 'backup')) l7 = snapshots.listSnapshots(self.cfg, includeNewSnapshot = True, reverse = False) self.assertListEqual(l7, ['20151219-010324-123', '20151219-020324-123', '20151219-030324-123', '20151219-040324-123', 'new_snapshot']) self.assertIsInstance(l7[0], snapshots.SID) self.assertIsInstance(l7[-1], snapshots.NewSnapshot) def test_iter_snapshots(self): for i, sid in enumerate(snapshots.iterSnapshots(self.cfg)): self.assertIn(sid, ['20151219-040324-123', '20151219-030324-123', '20151219-020324-123', '20151219-010324-123']) self.assertIsInstance(sid, snapshots.SID) self.assertEqual(i, 3) def test_lastSnapshot(self): self.assertEqual(snapshots.lastSnapshot(self.cfg), '20151219-040324-123') class TestIterSnapshotsNonexistingSnapshotPath(generic.TestCaseSnapshotPath): def test_iterSnapshots(self): for item in snapshots.iterSnapshots(self.cfg): self.fail('got unexpected snapshot') def test_listSnapshots(self): self.assertEqual(snapshots.listSnapshots(self.cfg), []) def test_lastSnapshots(self): self.assertIsNone(snapshots.lastSnapshot(self.cfg)) if __name__ == '__main__': unittest.main() backintime-1.5.2/common/test/test_snapshotlog.py000066400000000000000000000166301465446530500220700ustar00rootroot00000000000000# Back In Time # Copyright (C) 2016-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. # # 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 sys import unittest import re from test import generic from tempfile import TemporaryDirectory from datetime import datetime sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import snapshotlog import config import snapshots class TestLogFilter(generic.TestCase): #TODO: add decode test def __init__(self, *args, **kwargs): super(TestLogFilter, self).__init__(*args, **kwargs) self.e = '[E] foo bar' self.c = '[C] foo bar' self.i = '[I] foo bar' self.n = '' self.h = '========== header ===========' def test_filter(self): #No filter logFilter = snapshotlog.LogFilter() for line in (self.e, self.c, self.i, self.n, self.h): self.assertEqual(line, logFilter.filter(line)) #Error filter logFilter = snapshotlog.LogFilter(mode = snapshotlog.LogFilter.ERROR) for line in (self.e, self.n, self.h): self.assertEqual(line, logFilter.filter(line)) for line in (self.c, self.i): self.assertIsNone(logFilter.filter(line)) #Changes filter logFilter = snapshotlog.LogFilter(mode = snapshotlog.LogFilter.CHANGES) for line in (self.c, self.n, self.h): self.assertEqual(line, logFilter.filter(line)) for line in (self.e, self.i): self.assertIsNone(logFilter.filter(line)) #Information filter logFilter = snapshotlog.LogFilter(mode = snapshotlog.LogFilter.INFORMATION) for line in (self.i, self.n, self.h): self.assertEqual(line, logFilter.filter(line)) for line in (self.c, self.e): self.assertIsNone(logFilter.filter(line)) #Error + Changes filter logFilter = snapshotlog.LogFilter(mode = snapshotlog.LogFilter.ERROR_AND_CHANGES) for line in (self.e, self.c, self.n, self.h): self.assertEqual(line, logFilter.filter(line)) for line in (self.i,): self.assertIsNone(logFilter.filter(line)) # New filter (#1587): rsync transfer failures (experimental) logFilter = snapshotlog.LogFilter(mode=snapshotlog.LogFilter.RSYNC_TRANSFER_FAILURES) log_lines = ( '[I] Take snapshot (rsync: symlink has no referent: "/home/user/Documents/dead-link")', '[E] Error: rsync: [sender] send_files failed to open "/home/user/Documents/root_only_file.txt": Permission denied (13)', '[I] Schnappschuss erstellen (rsync: IO error encountered -- skipping file deletion)', '[I] Schnappschuss erstellen (rsync: rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1333) [sender=3.2.3])', '[I] Take snapshot (rsync: rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1333) [sender=3.2.3])', ) for line in log_lines: self.assertEqual(line, logFilter.filter(line)) for line in (self.e, self.c, self.i, self.h): self.assertIsNone(logFilter.filter(line)) for line in self.n: self.assertEqual(line, logFilter.filter(line)) # empty line stays empty line class TestSnapshotLog(generic.SnapshotsTestCase): def setUp(self): super(TestSnapshotLog, self).setUp() self.logFile = os.path.join(self.cfg._LOCAL_DATA_FOLDER, 'takesnapshot_.log') def test_new(self): log = snapshotlog.SnapshotLog(self.cfg) now = datetime.today() with open(self.logFile, 'wt') as f: f.write('foo\nbar\n') log.new(now) log.flush() self.assertExists(self.logFile) with open(self.logFile, 'rt') as f: self.assertRegex(f.read(), re.compile(r'''========== Take snapshot \(profile .*\): .* ========== ''', re.MULTILINE)) def test_new_continue(self): log = snapshotlog.SnapshotLog(self.cfg) now = datetime.today() with open(self.logFile, 'wt') as f: f.write('foo\nbar\n') new = snapshots.NewSnapshot(self.cfg) new.makeDirs() new.saveToContinue = True log.new(now) log.flush() self.assertExists(self.logFile) with open(self.logFile, 'rt') as f: self.assertRegex(f.read(), re.compile(r'''foo bar Last snapshot didn't finish but can be continued. ======== continue snapshot \(profile .*\): .* ======== ''', re.MULTILINE)) def test_append(self): log = snapshotlog.SnapshotLog(self.cfg) log.append('foo', 1) log.flush() self.assertExists(self.logFile) with open(self.logFile, 'rt') as f: self.assertEqual(f.read(), 'foo\n') def test_append_log_level(self): self.cfg.setLogLevel(2) log = snapshotlog.SnapshotLog(self.cfg) log.append('foo', 3) log.flush() self.assertNotExists(self.logFile) log.append('bar', 1) log.flush() self.assertExists(self.logFile) with open(self.logFile, 'rt') as f: self.assertEqual(f.read(), 'bar\n') def test_get(self): log = snapshotlog.SnapshotLog(self.cfg) log.append('foo bar', 1) log.flush() self.assertExists(self.logFile) self.assertEqual('\n'.join(log.get()), 'foo bar') def test_get_filter(self): log = snapshotlog.SnapshotLog(self.cfg) log.append('foo bar', 1) log.append('[I] 123', 1) log.append('[C] baz', 1) log.append('[E] bla', 1) log.flush() self.assertExists(self.logFile) self.assertEqual('\n'.join(log.get(mode = snapshotlog.LogFilter.CHANGES)), 'foo bar\n[C] baz') def test_skipLines_show_all(self): log = snapshotlog.SnapshotLog(self.cfg) for i in range(10): log.append(str(i), 1) log.flush() self.assertEqual('\n'.join(log.get(skipLines = 0)), '\n'.join([str(i) for i in range(10)])) def test_skipLines(self): log = snapshotlog.SnapshotLog(self.cfg) for i in range(10): log.append(str(i), 1) log.flush() self.assertEqual('\n'.join(log.get(skipLines = 4)), '\n'.join([str(i) for i in range(4, 10)])) def test_skipLines_filtered(self): log = snapshotlog.SnapshotLog(self.cfg) log.append('foo bar', 1) log.append('[I] 123', 1) log.append('[C] baz', 1) log.append('[E] bla', 1) log.append('[C] 456', 1) log.append('[C] 789', 1) log.append('[E] qwe', 1) log.append('[C] asd', 1) log.flush() self.assertEqual('\n'.join(log.get(mode = snapshotlog.LogFilter.CHANGES, skipLines = 2)), '[C] 456\n[C] 789\n[C] asd') backintime-1.5.2/common/test/test_snapshots.py000066400000000000000000001346571465446530500215630ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 sys import pathlib import shutil import stat import pwd import grp import re import random import string import unittest from unittest.mock import patch from datetime import date, datetime from threading import Thread from tempfile import TemporaryDirectory from test import generic sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import logger import config import snapshots import tools import mount CURRENTUID = os.geteuid() CURRENTUSER = pwd.getpwuid(CURRENTUID).pw_name CURRENTGID = os.getegid() CURRENTGROUP = grp.getgrgid(CURRENTGID).gr_name # all groups the current user is member in GROUPS = [i.gr_name for i in grp.getgrall() if CURRENTUSER in i.gr_mem] NO_GROUPS = not GROUPS IS_ROOT = os.geteuid() == 0 class TestSnapshots(generic.SnapshotsTestCase): ############################################################################ ### takeSnapshotMessage ### ############################################################################ def test_setTakeSnapshotMessage_info(self): self.sn.setTakeSnapshotMessage(0, 'first message') self.sn.snapshotLog.flush() # test NotifyPlugin self.mockNotifyPlugin.assert_called_once_with(self.sn.config.currentProfile(), self.sn.config.profileName(), 0, 'first message', -1) self.assertExists(self.sn.config.takeSnapshotMessageFile()) # test message file with open(self.sn.config.takeSnapshotMessageFile(), 'rt') as f: message = f.read() self.assertEqual(message, '0\nfirst message') # test snapshot log self.assertEqual('\n'.join(self.sn.snapshotLog.get()), '[I] first message') def test_setTakeSnapshotMessage_error(self): self.sn.setTakeSnapshotMessage(1, 'second message') self.sn.snapshotLog.flush() # test NotifyPlugin self.mockNotifyPlugin.assert_called_once_with(self.sn.config.currentProfile(), self.sn.config.profileName(), 1, 'second message', -1) # test message file self.assertExists(self.sn.config.takeSnapshotMessageFile()) with open(self.sn.config.takeSnapshotMessageFile(), 'rt') as f: message = f.read() self.assertEqual(message, '1\nsecond message') # test snapshot log self.assertEqual('\n'.join(self.sn.snapshotLog.get()), '[E] second message') ############################################################################ ### uid ### ############################################################################ def test_uid_valid(self): self.assertEqual(self.sn.uid('root'), 0) self.assertEqual(self.sn.uid(b'root'), 0) self.assertEqual(self.sn.uid(CURRENTUSER), CURRENTUID) self.assertEqual(self.sn.uid(CURRENTUSER.encode()), CURRENTUID) def test_uid_invalid(self): self.assertEqual(self.sn.uid('nonExistingUser'), -1) self.assertEqual(self.sn.uid(b'nonExistingUser'), -1) def test_uid_backup(self): self.assertEqual(self.sn.uid('root', backup = 99999), 0) self.assertEqual(self.sn.uid(b'root', backup = 99999), 0) self.assertEqual(self.sn.uid('nonExistingUser', backup = 99999), 99999) self.assertEqual(self.sn.uid(b'nonExistingUser', backup = 99999), 99999) self.assertEqual(self.sn.uid(CURRENTUSER, backup = 99999), CURRENTUID) self.assertEqual(self.sn.uid(CURRENTUSER.encode(), backup = 99999), CURRENTUID) ############################################################################ ### gid ### ############################################################################ def test_gid_valid(self): self.assertEqual(self.sn.gid('root'), 0) self.assertEqual(self.sn.gid(b'root'), 0) self.assertEqual(self.sn.gid(CURRENTGROUP), CURRENTGID) self.assertEqual(self.sn.gid(CURRENTGROUP.encode()), CURRENTGID) def test_gid_invalid(self): self.assertEqual(self.sn.gid('nonExistingGroup'), -1) self.assertEqual(self.sn.gid(b'nonExistingGroup'), -1) def test_gid_backup(self): self.assertEqual(self.sn.gid('root', backup = 99999), 0) self.assertEqual(self.sn.gid(b'root', backup = 99999), 0) self.assertEqual(self.sn.gid('nonExistingGroup', backup = 99999), 99999) self.assertEqual(self.sn.gid(b'nonExistingGroup', backup = 99999), 99999) self.assertEqual(self.sn.gid(CURRENTGROUP, backup = 99999), CURRENTGID) self.assertEqual(self.sn.gid(CURRENTGROUP.encode(), backup = 99999), CURRENTGID) ############################################################################ ### userName ### ############################################################################ def test_userName_valid(self): self.assertEqual(self.sn.userName(0), 'root') self.assertEqual(self.sn.userName(CURRENTUID), CURRENTUSER) def test_userName_invalid(self): self.assertEqual(self.sn.userName(99999), '-') ############################################################################ ### groupName ### ############################################################################ def test_groupName_valid(self): self.assertEqual(self.sn.groupName(0), 'root') self.assertEqual(self.sn.groupName(CURRENTGID), CURRENTGROUP) def test_groupName_invalid(self): self.assertEqual(self.sn.groupName(99999), '-') ############################################################################ ### takeSnapshot helper scripts ### ############################################################################ def test_rsyncRemotePath(self): self.assertEqual(self.sn.rsyncRemotePath('/foo'), '/foo') # "quote" is ignored because the "mode" isn't ssh or ssh_encfs self.assertEqual(self.sn.rsyncRemotePath('/foo', quote = '\\\"'), '/foo') self.assertEqual(self.sn.rsyncRemotePath('/foo', use_mode = ['local']), '/foo') # The same as above. self.assertEqual(self.sn.rsyncRemotePath('/foo', use_mode = ['local'], quote = '\\\"'), '/foo') #set up SSH profile self.cfg.setSnapshotsMode('ssh') self.cfg.setSshHost('localhost') self.cfg.setSshUser('foo') self.assertEqual(self.sn.rsyncRemotePath('/bar'), 'foo@localhost:"/bar"') self.assertEqual(self.sn.rsyncRemotePath('/bar', quote = '\\\"'), 'foo@localhost:\\\"/bar\\\"') self.assertEqual(self.sn.rsyncRemotePath('/bar', use_mode = []), '/bar') def test_createLastSnapshotSymlink(self): sid1 = snapshots.SID('20151219-010324-123', self.cfg) sid1.makeDirs() symlink = self.cfg.lastSnapshotSymlink() self.assertNotExists(symlink) self.assertTrue(self.sn.createLastSnapshotSymlink(sid1)) self.assertIsLink(symlink) self.assertEqual(os.path.realpath(symlink), sid1.path()) sid2 = snapshots.SID('20151219-020324-123', self.cfg) sid2.makeDirs() self.assertTrue(self.sn.createLastSnapshotSymlink(sid2)) self.assertIsLink(symlink) self.assertEqual(os.path.realpath(symlink), sid2.path()) # def flockSecondInstance(self): # cfgFile = os.path.abspath(os.path.join(__file__, os.pardir, 'config')) # cfg = config.Config(cfgFile) # sn = snapshots.Snapshots(cfg) # sn.GLOBAL_FLOCK = self.sn.GLOBAL_FLOCK # cfg.setGlobalFlock(True) # sn.flockExclusive() # sn.flockRelease() # def test_flockExclusive(self): # RWUGO = 33206 #-rw-rw-rw # self.cfg.setGlobalFlock(True) # thread = Thread(target = self.flockSecondInstance, args = ()) # self.sn.flockExclusive() # self.assertExists(self.sn.GLOBAL_FLOCK) # mode = os.stat(self.sn.GLOBAL_FLOCK).st_mode # self.assertEqual(mode, RWUGO) # thread.start() # thread.join(0.01) # self.assertTrue(thread.is_alive()) # self.sn.flockRelease() # thread.join() # self.assertFalse(thread.is_alive()) def test_statFreeSpaceLocal(self): self.assertIsInstance(self.sn.statFreeSpaceLocal('/'), int) @patch('time.sleep') # speed up unittest def test_makeDirs(self, sleep): self.assertFalse(self.sn.makeDirs('/')) self.assertTrue(self.sn.makeDirs(os.getcwd())) with TemporaryDirectory() as d: path = os.path.join(d, 'foo', 'bar') self.assertTrue(self.sn.makeDirs(path)) ############################################################################ ### rsync Ex-/Include and suffix ### ############################################################################ def test_rsyncExclude_unique_items(self): exclude = self.sn.rsyncExclude(['/foo', '*bar', '/baz/1']) self.assertListEqual(list(exclude), ['--exclude=/foo', '--exclude=*bar', '--exclude=/baz/1']) def test_rsyncExclude_duplicate_items(self): exclude = self.sn.rsyncExclude(['/foo', '*bar', '/baz/1', '/foo', '/baz/1']) self.assertListEqual(list(exclude), ['--exclude=/foo', '--exclude=*bar', '--exclude=/baz/1']) def test_rsyncInclude_unique_items(self): i1, i2 = self.sn.rsyncInclude([('/foo', 0), ('/bar', 1), ('/baz/1/2', 1)]) self.assertListEqual(list(i1), ['--include=/foo/', '--include=/baz/1/', '--include=/baz/']) self.assertListEqual(list(i2), ['--include=/foo/**', '--include=/bar', '--include=/baz/1/2']) def test_rsyncInclude_duplicate_items(self): i1, i2 = self.sn.rsyncInclude([('/foo', 0), ('/bar', 1), ('/foo', 0), ('/baz/1/2', 1), ('/baz/1/2', 1)]) self.assertListEqual(list(i1), ['--include=/foo/', '--include=/baz/1/', '--include=/baz/']) self.assertListEqual(list(i2), ['--include=/foo/**', '--include=/bar', '--include=/baz/1/2']) def test_rsyncInclude_root(self): i1, i2 = self.sn.rsyncInclude([('/', 0), ]) self.assertListEqual(list(i1), []) self.assertListEqual(list(i2), ['--include=/', '--include=/**']) def test_rsyncSuffix(self): suffix = self.sn.rsyncSuffix(includeFolders = [('/foo', 0), ('/bar', 1), ('/baz/1/2', 1)], excludeFolders = ['/foo/bar', '*blub', '/bar/2']) self.assertIsInstance(suffix, list) self.assertRegex(' '.join(suffix), r'^--chmod=Du\+wx ' + r'--exclude=/tmp/.*? ' + r'--exclude=.*?\.local/share/backintime ' + r'--exclude=\.local/share/backintime/mnt ' + r'--include=/foo/ ' + r'--include=/baz/1/ ' + r'--include=/baz/ ' + r'--exclude=/foo/bar ' + r'--exclude=\*blub ' + r'--exclude=/bar/2 ' + r'--include=/foo/\*\* ' + r'--include=/bar ' + r'--include=/baz/1/2 ' + r'--exclude=\* /$') ############################################################################ ### callback ### ############################################################################ def test_restoreCallback(self): msg = 'foo' callback = lambda x: self.callback(self.assertEqual, x, msg) self.sn.restoreCallback(callback, True, msg) self.assertTrue(self.run) self.assertFalse(self.sn.restorePermissionFailed) self.run = False callback = lambda x: self.callback(self.assertRegex, x, r'{} : \w+'.format(msg)) self.sn.restoreCallback(callback, False, msg) self.assertTrue(self.run) self.assertTrue(self.sn.restorePermissionFailed) def test_rsyncCallback(self): params = [False, False] self.sn.rsyncCallback('foo', params) self.assertListEqual([False, False], params) with open(self.cfg.takeSnapshotMessageFile(), 'rt') as f: self.assertEqual('0\nTake snapshot (rsync: foo)', f.read()) self.sn.snapshotLog.flush() with open(self.cfg.takeSnapshotLogFile(), 'rt') as f: self.assertEqual('[I] Take snapshot (rsync: foo)\n', f.read()) def test_rsyncCallback_keep_params(self): params = [True, True] self.sn.rsyncCallback('foo', params) self.assertListEqual([True, True], params) def test_rsyncCallback_transfer(self): params = [False, False] self.sn.rsyncCallback('BACKINTIME: $TRAP; ' %self.remotePath) ssh = sshtools.SSH(cfg = self.cfg) with self.assertRaisesRegex(MountException, r"Remote host .+ doesn't support hardlinks"): ssh.checkRemoteCommands() def test_check_remote_command_with_spaces(self): self.cfg.setSmartRemoveRunRemoteInBackground(tools.checkCommand('screen') and tools.checkCommand('flock')) self.remotePath = os.path.join(self.tmpDir.name, 'foo bar') self.cfg.setSshSnapshotsPath(self.remotePath) os.mkdir(self.remotePath) ssh = sshtools.SSH(cfg = self.cfg) ssh.checkRemoteCommands() def test_randomId(self): ssh = sshtools.SSH(cfg = self.cfg) self.assertRegex(ssh.randomId(size = 6), r'[A-Z0-9]{6}') class TestSshKey(generic.TestCaseCfg): def test_sshKeyGen(self): with TemporaryDirectory() as tmp: secKey = os.path.join(tmp, 'key') pubKey = secKey + '.pub' # create new key self.assertTrue(sshtools.sshKeyGen(secKey)) self.assertIsFile(secKey) self.assertIsFile(pubKey) # do not overwrite existing keys self.assertFalse(sshtools.sshKeyGen(secKey)) @unittest.skipIf(not tools.checkCommand('ssh-keygen'), "'ssh-keygen' not found.") def test_sshKeyFingerprint(self): self.assertIsNone( sshtools.sshKeyFingerprint(os.path.abspath(__file__))) with TemporaryDirectory() as d: key = os.path.join(d, 'key') cmd = ['ssh-keygen', '-q', '-N', '', '-f', key] proc = subprocess.Popen(cmd) proc.communicate() fingerprint = sshtools.sshKeyFingerprint(key) self.assertIsInstance(fingerprint, str) if fingerprint.startswith('SHA256'): self.assertEqual(len(fingerprint), 50) self.assertRegex(fingerprint, r'^SHA256:[a-zA-Z0-9/+]+$') else: self.assertEqual(len(fingerprint), 47) self.assertRegex(fingerprint, r'^[a-fA-F0-9:]+$') @unittest.skipIf(not generic.LOCAL_SSH, 'Skip as this test requires a local ssh server, public and private keys installed') def test_sshHostKey(self): fingerprint, keyHash, keyType = sshtools.sshHostKey('localhost') self.assertIsInstance(fingerprint, str) self.assertIsInstance(keyHash, str) self.assertIsInstance(keyType, str) if fingerprint.startswith('SHA256'): self.assertEqual(len(fingerprint), 50) self.assertRegex(fingerprint, r'^SHA256:[a-zA-Z0-9/+]+$') else: self.assertEqual(len(fingerprint), 47) self.assertRegex(fingerprint, r'^[a-fA-F0-9:]+$') self.assertIn(keyType, ('ECDSA', 'RSA')) hostKey = '/etc/ssh/ssh_host_{}_key.pub'.format(keyType.lower()) self.assertExists(hostKey) self.assertEqual(3, len(keyHash.split())) try: with open(hostKey, 'rt') as f: pubKey = f.read().split()[1] self.assertEqual(pubKey, keyHash.split()[2]) except (IOError, IndexError): pass def test_writeKnownHostFile(self): KEY = '|1|abcdefghijklmnopqrstuvwxyz= ecdsa-sha2-nistp256 AAAAABCDEFGHIJKLMNOPQRSTUVWXYZ=' with TemporaryDirectory() as tmp: knownHosts = os.path.expanduser('~/.ssh/known_hosts') knownHostsSic = os.path.join(tmp, 'known_hosts') if os.path.exists(knownHosts): shutil.copyfile(knownHosts, knownHostsSic) try: sshtools.writeKnownHostsFile(KEY) self.assertExists(knownHosts) with open(knownHosts, 'rt') as f: self.assertIn(KEY, [x.strip() for x in f.readlines()]) finally: # restore original known_hosts file if os.path.exists(knownHostsSic): shutil.copyfile(knownHostsSic, knownHosts) @unittest.skipIf(not generic.LOCAL_SSH, SKIP_MESSAGE_SSH) class TestStartSshAgent(generic.SSHTestCase): # running this test requires that user has public / private key pair created and ssh server running SOCK = 'SSH_AUTH_SOCK' PID = 'SSH_AGENT_PID' def setUp(self): super(TestStartSshAgent, self).setUp() self.ssh = sshtools.SSH(cfg = self.cfg) self.currentSock = os.environ.pop(self.SOCK, '') self.currentPid = os.environ.pop(self.PID, '') def tearDown(self): os.environ[self.SOCK] = self.currentSock os.environ[self.PID] = self.currentPid def test_startSshAgent(self): self.ssh.startSshAgent() self.assertTrue(self.SOCK in os.environ) self.assertTrue(self.PID in os.environ) @patch('tools.which') @patch('os.kill') def test_startSshAgentEqualSign(self, mockKill, mockWhich): mockWhich.return_value = ['echo', 'setenv SSH_AUTH_SOCK=/tmp/ssh-zWg8uTdgh1QJ/agent.9070;\n', 'setenv SSH_AGENT_PID=9071;\n' 'echo Agent pid 9071;'] self.ssh.startSshAgent() self.assertTrue(self.SOCK in os.environ) self.assertTrue(self.PID in os.environ) @patch('tools.which') @patch('os.kill') def test_startSshAgentSpace(self, mockKill, mockWhich): mockWhich.return_value = ['echo', 'setenv SSH_AUTH_SOCK /tmp/ssh-zWg8uTdgh1QJ/agent.9070;\n', 'setenv SSH_AGENT_PID 9071;\n' 'echo Agent pid 9071;'] self.ssh.startSshAgent() self.assertTrue(self.SOCK in os.environ) self.assertTrue(self.PID in os.environ) @patch('tools.which') @patch('os.kill') def test_startSshAgentExport(self, mockKill, mockWhich): mockWhich.return_value = ['echo', 'SSH_AUTH_SOCK=/tmp/ssh-zWg8uTdgh1QJ/agent.9070; export SSH_AUTH_SOCK;\n', 'SSH_AGENT_PID=9071; export SSH_AGENT_PID;\n' 'echo Agent pid 9071;'] self.ssh.startSshAgent() self.assertTrue(self.SOCK in os.environ) self.assertTrue(self.PID in os.environ) @patch('tools.which') def test_startSshAgentError(self, mockWhich): mockWhich.return_value = '/bin/false' with self.assertRaises(MountException): self.ssh.startSshAgent() @patch('tools.which') def test_startSshAgentMissing(self, mockWhich): mockWhich.return_value = '' with self.assertRaises(MountException): self.ssh.startSshAgent() class SSHCopyID(unittest.TestCase): def test_complete_command(self): """Complete generated command""" command = sshtools.sshCopyIdCommand( generic.PRIV_KEY_FILE, 'user', 'non_existing_host', ) self.assertEqual( command, [ 'ssh-copy-id', '-i', generic.PRIV_KEY_FILE, '-p', '22', 'user@non_existing_host' ] ) def test_default_port(self): """Default port""" sut = sshtools.sshCopyIdCommand( generic.PRIV_KEY_FILE, 'user', 'non_existing_host', ) self.assertEqual(len(sut), 6, sut) # no port explicit specified self.assertEqual(sut[4], '22') def test_custom_port(self): """Custom (random) port""" custom_port = str(random.randint(23, 128)) sut = sshtools.sshCopyIdCommand( generic.PRIV_KEY_FILE, 'user', 'non_existing_host', port=custom_port ) self.assertEqual(sut[3], '-p') self.assertEqual(sut[4], custom_port) def test_proxy_with_default_port(self): """Used proxy and default port""" proxy_user = 'non_existing_proxy_user' proxy_host = 'non_existing_proxy_host' sut = sshtools.sshCopyIdCommand( generic.PRIV_KEY_FILE, 'user', 'non_existing_host', proxy_user=proxy_user, proxy_host=proxy_host ) self.assertIn( 'ProxyJump=non_existing_proxy_user@non_existing_proxy_host:22', sut) def test_proxy_with_custom_port(self): """Used proxy and custom port""" proxy_user = 'non_existing_proxy_user' proxy_host = 'non_existing_proxy_host' proxy_port = str(random.randint(23, 128)) sut = sshtools.sshCopyIdCommand( generic.PRIV_KEY_FILE, 'user', 'non_existing_host', proxy_user=proxy_user, proxy_host=proxy_host, proxy_port=proxy_port ) self.assertIn( 'ProxyJump=non_existing_proxy_user@non_existing_proxy_host' f':{proxy_port}', sut) backintime-1.5.2/common/test/test_takeSnapshot.py000066400000000000000000000264641465446530500222010ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 sys import stat import unittest from unittest.mock import patch from datetime import date, datetime, timedelta from tempfile import TemporaryDirectory from test import generic sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import snapshots import mount class TestTakeSnapshot(generic.SnapshotsTestCase): def setUp(self): super(TestTakeSnapshot, self).setUp() self.include = TemporaryDirectory() generic.create_test_files(self.include.name) def tearDown(self): super(TestTakeSnapshot, self).tearDown() self.include.cleanup() def remount(self): #dummy method only used in TestTakeSnapshotSSH pass def getInode(self, sid): return os.stat(sid.pathBackup(os.path.join(self.include.name, 'test'))).st_ino @patch('time.sleep') # speed up unittest def test_takeSnapshot(self, sleep): now = datetime.today() - timedelta(minutes = 6) sid1 = snapshots.SID(now, self.cfg) # Note: 'self.sn' is of type 'Snapshots' # First boolean: Snapshot succeeded # Second boolean: Error occurred self.assertListEqual( [True, False], # Snapshot without error self.sn.takeSnapshot( sid=sid1, now=now, include_folders=[ (self.include.name, 0), # '0' means it is a file ] ) ) self.assertTrue(sid1.exists()) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'file with spaces'))) self.assertExists(self.cfg.anacronSpoolFile()) for f in ('config', 'fileinfo.bz2', 'info', 'takesnapshot.log.bz2'): self.assertExists(sid1.path(f)) for f in ('failed', 'save_to_continue'): self.assertNotExists(sid1.path(f)) # second takeSnapshot which should not create a new snapshot as nothing # has changed os.remove(self.cfg.anacronSpoolFile()) now = datetime.today() - timedelta(minutes = 4) sid2 = snapshots.SID(now, self.cfg) self.assertListEqual([False, False], self.sn.takeSnapshot(sid2, now, [(self.include.name, 0),])) self.assertFalse(sid2.exists()) self.assertExists(self.cfg.anacronSpoolFile()) # third takeSnapshot self.remount() with open(os.path.join(self.include.name, 'lalala'), 'wt') as f: f.write('asdf') now = datetime.today() - timedelta(minutes = 2) sid3 = snapshots.SID(now, self.cfg) self.assertListEqual([True, False], self.sn.takeSnapshot(sid3, now, [(self.include.name, 0),])) self.assertTrue(sid3.exists()) self.assertTrue(sid3.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'lalala'))) inode1 = self.getInode(sid1) inode3 = self.getInode(sid3) self.assertEqual(inode1, inode3) # fourth takeSnapshot with force create new snapshot even if nothing # has changed self.cfg.setTakeSnapshotRegardlessOfChanges(True) now = datetime.today() sid4 = snapshots.SID(now, self.cfg) self.assertListEqual([True, False], self.sn.takeSnapshot(sid4, now, [(self.include.name, 0),])) self.assertTrue(sid4.exists()) self.assertTrue(sid4.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertTrue(sid4.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) @patch('time.sleep') # speed up unittest def test_takeSnapshot_with_spaces_in_include(self, sleep): now = datetime.today() sid1 = snapshots.SID(now, self.cfg) include = os.path.join(self.include.name, 'test path with spaces') generic.create_test_files(include) self.assertListEqual([True, False], self.sn.takeSnapshot(sid1, now, [(include, 0),])) self.assertTrue(sid1.exists()) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(include, 'foo', 'bar', 'baz'))) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(include, 'test'))) for f in ('config', 'fileinfo.bz2', 'info', 'takesnapshot.log.bz2'): self.assertExists(sid1.path(f)) for f in ('failed', 'save_to_continue'): self.assertNotExists(sid1.path(f)) @patch('time.sleep') # speed up unittest def test_takeSnapshot_exclude(self, sleep): now = datetime.today() sid1 = snapshots.SID(now, self.cfg) self.cfg.setExclude(['bar/baz']) self.assertListEqual([True, False], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) self.assertTrue(sid1.exists()) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar'))) self.assertFalse(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) for f in ('config', 'fileinfo.bz2', 'info', 'takesnapshot.log.bz2'): self.assertExists(sid1.path(f)) for f in ('failed', 'save_to_continue'): self.assertNotExists(sid1.path(f)) @patch('time.sleep') # speed up unittest def test_takeSnapshot_with_spaces_in_exclude(self, sleep): now = datetime.today() sid1 = snapshots.SID(now, self.cfg) exclude = os.path.join(self.include.name, 'test path with spaces') generic.create_test_files(exclude) self.cfg.setExclude([exclude]) self.assertListEqual([True, False], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) self.assertTrue(sid1.exists()) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) self.assertFalse(sid1.isExistingPathInsideSnapshotFolder(exclude)) for f in ('config', 'fileinfo.bz2', 'info', 'takesnapshot.log.bz2'): self.assertExists(sid1.path(f)) for f in ('failed', 'save_to_continue'): self.assertNotExists(sid1.path(f)) @patch('time.sleep') # speed up unittest def test_takeSnapshot_error(self, sleep): with generic.mockPermissions(os.path.join(self.include.name, 'test')): now = datetime.today() sid1 = snapshots.SID(now, self.cfg) self.assertListEqual([True, True], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) self.assertTrue(sid1.exists()) self.assertTrue(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertFalse(sid1.isExistingPathInsideSnapshotFolder(os.path.join(self.include.name, 'test'))) for f in ('config', 'fileinfo.bz2', 'info', 'takesnapshot.log.bz2', 'failed'): self.assertExists(sid1.path(f)) self.assertNotExists(self.cfg.anacronSpoolFile()) @patch('time.sleep') # speed up unittest def test_takeSnapshot_error_without_continue(self, sleep): with generic.mockPermissions(os.path.join(self.include.name, 'test')): self.cfg.setContinueOnErrors(False) now = datetime.today() sid1 = snapshots.SID(now, self.cfg) self.assertListEqual([False, True], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) self.assertFalse(sid1.exists()) @patch('time.sleep') # speed up unittest def test_takeSnapshot_new_exists(self, sleep): new_snapshot = snapshots.NewSnapshot(self.cfg) new_snapshot.makeDirs() with open(new_snapshot.path('leftover'), 'wt') as f: f.write('foo') now = datetime.today() - timedelta(minutes = 6) sid1 = snapshots.SID(now, self.cfg) self.assertListEqual([True, False], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) self.assertTrue(sid1.exists()) self.assertNotExists(sid1.path('leftover')) @patch('time.sleep') # speed up unittest def test_takeSnapshot_new_exists_continue(self, sleep): new_snapshot = snapshots.NewSnapshot(self.cfg) new_snapshot.makeDirs() with open(new_snapshot.path('leftover'), 'wt') as f: f.write('foo') new_snapshot.saveToContinue = True now = datetime.today() - timedelta(minutes = 6) sid1 = snapshots.SID(now, self.cfg) self.assertListEqual([True, False], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) self.assertTrue(sid1.exists()) self.assertExists(sid1.path('leftover')) @patch('time.sleep') # speed up unittest def test_takeSnapshot_fail_create_new_snapshot(self, sleep): with generic.mockPermissions(self.snapshotPath, 0o500): now = datetime.today() sid1 = snapshots.SID(now, self.cfg) self.assertListEqual([False, True], self.sn.takeSnapshot(sid1, now, [(self.include.name, 0),])) @unittest.skipIf(not generic.LOCAL_SSH, 'Skip as this test requires a local ssh server, public and private keys installed') class TestTakeSnapshotSSH(generic.SSHSnapshotTestCase, TestTakeSnapshot): def setUp(self): super(TestTakeSnapshotSSH, self).setUp() self.include = TemporaryDirectory() generic.create_test_files(self.include.name) #mount self.cfg.setCurrentHashId(mount.Mount(cfg = self.cfg).mount()) def tearDown(self): #unmount mount.Mount(cfg = self.cfg).umount(self.cfg.current_hash_id) super(TestTakeSnapshotSSH, self).tearDown() self.include.cleanup() def remount(self): mount.Mount(cfg = self.cfg).umount(self.cfg.current_hash_id) hash_id = mount.Mount(cfg = self.cfg).mount() def getInode(self, sid): return os.stat(os.path.join(self.snapshotPath, sid.sid, 'backup', self.include.name[1:], 'test')).st_ino backintime-1.5.2/common/test/test_tools.py000066400000000000000000000767751465446530500207070ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 sys import subprocess import random import pathlib import gzip import stat import signal import unittest from unittest.mock import patch from copy import deepcopy from tempfile import NamedTemporaryFile, TemporaryDirectory from datetime import datetime from test import generic from time import sleep import pyfakefs.fake_filesystem_unittest as pyfakefs_ut sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import tools import configfile # chroot jails used for building may have no UUID devices (because of tmpfs) # we need to skip tests that require UUIDs DISK_BY_UUID_AVAILABLE = os.path.exists(tools.DISK_BY_UUID) UDEVADM_HAS_UUID = subprocess.Popen( ['udevadm', 'info', '-e'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).communicate()[0].find(b'ID_FS_UUID=') > 0 RSYNC_INSTALLED = tools.checkCommand('rsync') RSYNC_307_VERSION = """rsync version 3.0.7 protocol version 30 Copyright (C) 1996-2009 by Andrew Tridgell, Wayne Davison, and others. Web site: http://rsync.samba.org/ Capabilities: 64-bit files, 64-bit inums, 32-bit timestamps, 64-bit long ints, socketpairs, hardlinks, symlinks, IPv6, batchfiles, inplace, append, ACLs, xattrs, iconv, symtimes rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the GNU General Public License for details. """ RSYNC_310_VERSION = """rsync version 3.1.0 protocol version 31 Copyright (C) 1996-2013 by Andrew Tridgell, Wayne Davison, and others. Web site: http://rsync.samba.org/ Capabilities: 64-bit files, 64-bit inums, 64-bit timestamps, 64-bit long ints, socketpairs, hardlinks, symlinks, IPv6, batchfiles, inplace, append, ACLs, xattrs, iconv, symtimes, prealloc rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the GNU General Public License for details. """ class TestTools(generic.TestCase): """ All functions test here come from tools.py """ def setUp(self): super(TestTools, self).setUp() self.subproc = None def tearDown(self): super(TestTools, self).tearDown() self._kill_process() def _create_process(self, *args): dummyPath = os.path.join(os.path.dirname(__file__), generic.DUMMY) cmd = [dummyPath] cmd.extend(args) self.subproc = subprocess.Popen(cmd) sleep(0.1) return self.subproc.pid def _kill_process(self): if self.subproc: self.subproc.kill() self.subproc.wait() self.subproc = None def test_sharePath(self): share = tools.sharePath() self.assertTrue(share.endswith('share'), 'share = {}'.format(share)) def test_backintimePath(self): path = tools.backintimePath('common') self.assertIn(path, __file__) def test_registerBackintimePath(self): path = tools.backintimePath('foo') tools.registerBackintimePath('foo') self.assertIn(path, sys.path) sys.path.remove(path) def test_runningFromSource(self): self.assertTrue(tools.runningFromSource()) def test_addSourceToPathEnviron(self): source = tools.backintimePath('common') path = [x for x in os.getenv('PATH').split(':') if x != source] os.environ['PATH'] = ':'.join(path) tools.addSourceToPathEnviron() self.assertIn(source, os.environ['PATH']) def test_readFile(self): """ Test the function readFile """ test_tools_file = os.path.abspath(__file__) test_directory = os.path.dirname(test_tools_file) non_existing_file = os.path.join(test_directory, "nonExistingFile") self.assertIsInstance(tools.readFile(test_tools_file), str) self.assertIsNone(tools.readFile(non_existing_file)) with NamedTemporaryFile('wt') as tmp: tmp.write('foo\nbar') tmp.flush() self.assertIsInstance(tools.readFile(tmp.name), str) self.assertEqual(tools.readFile(tmp.name), 'foo\nbar') tmp_gz = NamedTemporaryFile().name with gzip.open(tmp_gz + '.gz', 'wt') as f: f.write('foo\nbar') f.flush() self.assertIsInstance(tools.readFile(tmp_gz), str) self.assertEqual(tools.readFile(tmp_gz), 'foo\nbar') os.remove(tmp_gz + '.gz') def test_readFileLines(self): """ Test the function readFileLines """ test_tools_file = os.path.abspath(__file__) test_directory = os.path.dirname(test_tools_file) non_existing_file = os.path.join(test_directory, "nonExistingFile") output = tools.readFileLines(test_tools_file) self.assertIsInstance(output, list) self.assertGreaterEqual(len(output), 1) self.assertIsInstance(output[0], str) self.assertIsNone(tools.readFileLines(non_existing_file)) with NamedTemporaryFile('wt') as tmp: tmp.write('foo\nbar') tmp.flush() self.assertIsInstance(tools.readFileLines(tmp.name), list) self.assertListEqual(tools.readFileLines(tmp.name), ['foo', 'bar']) tmp_gz = NamedTemporaryFile().name with gzip.open(tmp_gz + '.gz', 'wt') as f: f.write('foo\nbar') f.flush() self.assertIsInstance(tools.readFileLines(tmp_gz), list) self.assertEqual(tools.readFileLines(tmp_gz), ['foo', 'bar']) os.remove(tmp_gz + '.gz') def test_checkCommand(self): """ Test the function checkCommand """ self.assertFalse(tools.checkCommand('')) self.assertFalse(tools.checkCommand("notExistedCommand")) self.assertTrue(tools.checkCommand("ls")) self.assertTrue(tools.checkCommand('backintime')) def test_which(self): """ Test the function which """ self.assertRegex(tools.which("ls"), r'/.*/ls') self.assertEqual(tools.which('backintime'), os.path.join(os.getcwd(), 'backintime')) self.assertIsNone(tools.which("notExistedCommand")) def test_makeDirs(self): self.assertFalse(tools.makeDirs('/')) self.assertTrue(tools.makeDirs(os.getcwd())) with TemporaryDirectory() as d: path = os.path.join(d, 'foo', 'bar') self.assertTrue(tools.makeDirs(path)) def test_makeDirs_not_writable(self): with TemporaryDirectory() as d: os.chmod(d, stat.S_IRUSR) path = os.path.join( d, 'foobar{}'.format(random.randrange(100, 999))) self.assertFalse(tools.makeDirs(path)) def test_mkdir(self): self.assertFalse(tools.mkdir('/')) with TemporaryDirectory() as d: path = os.path.join(d, 'foo') self.assertTrue(tools.mkdir(path)) for mode in (0o700, 0o644, 0o777): msg = 'new path should have octal permissions {0:#o}' \ .format(mode) path = os.path.join(d, '{0:#o}'.format(mode)) self.assertTrue(tools.mkdir(path, mode), msg) self.assertEqual( '{0:o}'.format(os.stat(path).st_mode & 0o777), '{0:o}'.format(mode), msg) def test_pids(self): pids = tools.pids() self.assertGreater(len(pids), 0) self.assertIn(os.getpid(), pids) def test_processStat(self): pid = self._create_process() stat = tools.processStat(pid) self.assertRegex( stat, r'{} \({}\) \w .*'.format(pid, generic.DUMMY[:15])) @patch('builtins.open') def test_processStat_exception(self, mock_open): mock_open.side_effect = OSError() pid = self._create_process() self.assertEqual(tools.processStat(pid), '') def test_processPaused(self): pid = self._create_process() self.assertFalse(tools.processPaused(pid)) self.subproc.send_signal(signal.SIGSTOP) sleep(0.01) self.assertTrue(tools.processPaused(pid)) self.subproc.send_signal(signal.SIGCONT) sleep(0.01) self.assertFalse(tools.processPaused(pid)) def test_processName(self): pid = self._create_process() self.assertEqual(tools.processName(pid), generic.DUMMY[:15]) def test_processCmdline(self): pid = self._create_process() self.assertRegex(tools.processCmdline(pid), r'.*/sh.*/common/test/dummy_test_process\.sh') self._kill_process() pid = self._create_process('foo', 'bar') self.assertRegex(tools.processCmdline(pid), r'.*/sh.*/common/test/dummy_test_process\.sh.foo.bar') @patch('builtins.open') def test_processCmdline_exception(self, mock_open): mock_open.side_effect = OSError() pid = self._create_process() self.assertEqual(tools.processCmdline(pid), '') def test_pidsWithName(self): self.assertEqual(len(tools.pidsWithName('nonExistingProcess')), 0) pid = self._create_process() pids = tools.pidsWithName(generic.DUMMY) self.assertGreaterEqual(len(pids), 1) self.assertIn(pid, pids) def test_processExists(self): self.assertFalse(tools.processExists('nonExistingProcess')) self._create_process() self.assertTrue(tools.processExists(generic.DUMMY)) def test_processAlive(self): """ Test the function processAlive """ # TODO: add test (in chroot) running proc as root and kill as non-root self.assertTrue(tools.processAlive(os.getpid())) pid = self._create_process() self.assertTrue(tools.processAlive(pid)) self._kill_process() self.assertFalse(tools.processAlive(pid)) self.assertFalse(tools.processAlive(999999)) with self.assertRaises(ValueError): tools.processAlive(0) self.assertFalse(tools.processAlive(-1)) def test_checkXServer(self): try: tools.checkXServer() except Exception as e: self.fail( 'tools.ckeck_x_server() raised exception {}'.format(str(e))) def test_preparePath(self): path_with_slash_at_begin = "/test/path" path_without_slash_at_begin = "test/path" path_with_slash_at_end = "/test/path/" path_without_slash_at_end = "/test/path" self.assertEqual( tools.preparePath(path_with_slash_at_begin), path_with_slash_at_begin) self.assertEqual( tools.preparePath(path_without_slash_at_begin), path_with_slash_at_begin) self.assertEqual( tools.preparePath(path_without_slash_at_end), path_without_slash_at_end) self.assertEqual( tools.preparePath(path_with_slash_at_end), path_without_slash_at_end) def test_powerStatusAvailable(self): if tools.processExists('upowerd') and not generic.ON_TRAVIS: self.assertTrue(tools.powerStatusAvailable()) else: self.assertFalse(tools.powerStatusAvailable()) self.assertIsInstance(tools.onBattery(), bool) def test_rsyncCaps(self): if RSYNC_INSTALLED: caps = tools.rsyncCaps() self.assertIsInstance(caps, list) self.assertGreaterEqual(len(caps), 1) self.assertListEqual(tools.rsyncCaps(data=RSYNC_307_VERSION), ['64-bit files', '64-bit inums', '32-bit timestamps', '64-bit long ints', 'socketpairs', 'hardlinks', 'symlinks', 'IPv6', 'batchfiles', 'inplace', 'append', 'ACLs', 'xattrs', 'iconv', 'symtimes']) self.assertListEqual(tools.rsyncCaps(data=RSYNC_310_VERSION), ['progress2', '64-bit files', '64-bit inums', '64-bit timestamps', '64-bit long ints', 'socketpairs', 'hardlinks', 'symlinks', 'IPv6', 'batchfiles', 'inplace', 'append', 'ACLs', 'xattrs', 'iconv', 'symtimes', 'prealloc']) def test_md5sum(self): with NamedTemporaryFile() as f: f.write(b'foo') f.flush() self.assertEqual(tools.md5sum(f.name), 'acbd18db4cc2f85cedef654fccc4a4d8') def test_checkCronPattern(self): self.assertTrue(tools.checkCronPattern('0')) self.assertTrue(tools.checkCronPattern('0,10,13,15,17,20,23')) self.assertTrue(tools.checkCronPattern('*/6')) self.assertFalse(tools.checkCronPattern('a')) self.assertFalse(tools.checkCronPattern(' 1')) self.assertFalse(tools.checkCronPattern('0,10,13,1a,17,20,23')) self.assertFalse(tools.checkCronPattern('0,10,13, 15,17,20,23')) self.assertFalse(tools.checkCronPattern('*/6,8')) self.assertFalse(tools.checkCronPattern('*/6 a')) def test_mountpoint(self): self.assertEqual(tools.mountpoint('/nonExistingFolder/foo/bar'), '/') proc = os.path.join('/proc', str(os.getpid()), 'fd') self.assertEqual(tools.mountpoint(proc), '/proc') def test_decodeOctalEscape(self): self.assertEqual(tools.decodeOctalEscape('/mnt/normalPath'), '/mnt/normalPath') self.assertEqual( tools.decodeOctalEscape('/mnt/path\\040with\\040space'), '/mnt/path with space') def test_mountArgs(self): rootArgs = tools.mountArgs('/') self.assertIsInstance(rootArgs, list) self.assertGreaterEqual(len(rootArgs), 3) self.assertEqual(rootArgs[1], '/') procArgs = tools.mountArgs('/proc') self.assertGreaterEqual(len(procArgs), 3) self.assertEqual(procArgs[0], 'proc') self.assertEqual(procArgs[1], '/proc') self.assertEqual(procArgs[2], 'proc') def test_isRoot(self): self.assertIsInstance(tools.isRoot(), bool) def test_usingSudo(self): self.assertIsInstance(tools.usingSudo(), bool) def test_patternHasNotEncryptableWildcard(self): self.assertFalse(tools.patternHasNotEncryptableWildcard('foo')) self.assertFalse(tools.patternHasNotEncryptableWildcard('/foo')) self.assertFalse(tools.patternHasNotEncryptableWildcard('foo/*/bar')) self.assertFalse(tools.patternHasNotEncryptableWildcard('foo/**/bar')) self.assertFalse(tools.patternHasNotEncryptableWildcard('*/foo')) self.assertFalse(tools.patternHasNotEncryptableWildcard('**/foo')) self.assertFalse(tools.patternHasNotEncryptableWildcard('foo/*')) self.assertFalse(tools.patternHasNotEncryptableWildcard('foo/**')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo?')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo[1-2]')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo*')) self.assertTrue(tools.patternHasNotEncryptableWildcard('*foo')) self.assertTrue(tools.patternHasNotEncryptableWildcard('**foo')) self.assertTrue(tools.patternHasNotEncryptableWildcard('*.foo')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo*bar')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo**bar')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo*/bar')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo**/bar')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo/*bar')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo/**bar')) self.assertTrue(tools.patternHasNotEncryptableWildcard('foo/*/bar*')) self.assertTrue(tools.patternHasNotEncryptableWildcard('*foo/*/bar')) def test_readTimeStamp(self): with NamedTemporaryFile('wt') as f: f.write('20160127 0124') f.flush() self.assertEqual(tools.readTimeStamp(f.name), datetime(2016, 1, 27, 1, 24)) with NamedTemporaryFile('wt') as f: f.write('20160127') f.flush() self.assertEqual(tools.readTimeStamp(f.name), datetime(2016, 1, 27, 0, 0)) def test_writeTimeStamp(self): with NamedTemporaryFile('rt') as f: tools.writeTimeStamp(f.name) s = f.read().strip('\n') self.assertTrue(s.replace(' ', '').isdigit()) self.assertEqual(len(s), 13) def test_splitCommands(self): ret = list(tools.splitCommands(['echo foo;'], head='echo start;', tail='echo end', maxLength=40)) self.assertEqual(len(ret), 1) self.assertEqual(ret[0], 'echo start;echo foo;echo end') ret = list(tools.splitCommands(['echo foo;']*3, head='echo start;', tail='echo end', maxLength=40)) self.assertEqual(len(ret), 2) self.assertEqual(ret[0], 'echo start;echo foo;echo foo;echo end') self.assertEqual(ret[1], 'echo start;echo foo;echo end') ret = list(tools.splitCommands(['echo foo;']*3, head='echo start;', tail='echo end', maxLength=0)) self.assertEqual(len(ret), 1) self.assertEqual(ret[0], 'echo start;echo foo;echo foo;echo foo;echo end') ret = list(tools.splitCommands(['echo foo;'] * 3, head='echo start;', tail='echo end', maxLength=-10)) self.assertEqual(len(ret), 1) self.assertEqual( ret[0], 'echo start;echo foo;echo foo;echo foo;echo end') class TestEscapeIPv6(generic.TestCase): def test_escaped(self): values_and_expected = ( ('fd00:0::5', '[fd00:0::5]'), ( '2001:db8:0:8d3:0:8a2e:70:7344', '[2001:db8:0:8d3:0:8a2e:70:7344]' ), ('::', '[::]'), ('::1', '[::1]'), ('::ffff:192.0.2.128', '[::ffff:192.0.2.128]'), ('fe80::1', '[fe80::1]'), ) for val, exp in values_and_expected: with self.subTest(val=val, exp=exp): self.assertEqual(tools.escapeIPv6Address(val), exp) def test_passed(self): test_values = ( '192.168.1.1', '172.17.1.133', '255.255.255.255', '169.254.0.1', ) for val in test_values: with self.subTest(val=val): # IPv4 addresses are not escaped self.assertEqual(tools.escapeIPv6Address(val), val) def test_invalid(self): """Invalid IP addresses and hostnames""" test_values = ( 'foo.bar', 'fd00', '2001:0db8:::0000:0000:8a2e:0370:7334', ':2001:0db8:85a3:0000:0000:8a2e:0370:7334', '2001:0gb8:85a3:0000:0000:8a2e:0370:7334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334:abcd', 'localhost', ) for val in test_values: with self.subTest(val=val): self.assertEqual(tools.escapeIPv6Address(val), val) class TestToolsEnviron(generic.TestCase): """??? """ def __init__(self, *args, **kwargs): super(TestToolsEnviron, self).__init__(*args, **kwargs) self.env = deepcopy(os.environ) def setUp(self): super(TestToolsEnviron, self).setUp() self.temp_file = '/tmp/temp.txt' os.environ = deepcopy(self.env) def tearDown(self): super(TestToolsEnviron, self).tearDown() if os.path.exists(self.temp_file): os.remove(self.temp_file) os.environ = deepcopy(self.env) def test_envLoad_without_previous_values(self): test_env = configfile.ConfigFile() test_env.setStrValue('FOO', 'bar') test_env.setStrValue('ASDF', 'qwertz') test_env.save(self.temp_file) # make sure environ is clean self.assertNotIn('FOO', os.environ) self.assertNotIn('ASDF', os.environ) tools.envLoad(self.temp_file) self.assertIn('FOO', os.environ) self.assertIn('ASDF', os.environ) self.assertEqual(os.environ['FOO'], 'bar') self.assertEqual(os.environ['ASDF'], 'qwertz') def test_envLoad_do_not_overwrite_previous_values(self): test_env = configfile.ConfigFile() test_env.setStrValue('FOO', 'bar') test_env.setStrValue('ASDF', 'qwertz') test_env.save(self.temp_file) # add some environ vars that should not get overwritten os.environ['FOO'] = 'defaultFOO' os.environ['ASDF'] = 'defaultASDF' tools.envLoad(self.temp_file) self.assertIn('FOO', os.environ) self.assertIn('ASDF', os.environ) self.assertEqual(os.environ['FOO'], 'defaultFOO') self.assertEqual(os.environ['ASDF'], 'defaultASDF') def test_envSave(self): keys = ( 'GNOME_KEYRING_CONTROL', 'DBUS_SESSION_BUS_ADDRESS', 'DBUS_SESSION_BUS_PID', 'DBUS_SESSION_BUS_WINDOWID', 'DISPLAY', 'XAUTHORITY', 'GNOME_DESKTOP_SESSION_ID', 'KDE_FULL_SESSION') for i, k in enumerate(keys): os.environ[k] = str(i) tools.envSave(self.temp_file) self.assertIsFile(self.temp_file) test_env = configfile.ConfigFile() test_env.load(self.temp_file) for i, k in enumerate(keys): with self.subTest(i=i, k=k): # workaround for py.test3 2.5.1 doesn't support subTest msg = 'i = %s, k = %s' % (i, k) self.assertEqual(test_env.strValue(k), str(i), msg) class TestToolsUniquenessSet(generic.TestCase): # TODO: add test for follow_symlink def test_checkUnique(self): with TemporaryDirectory() as d: for i in range(1, 5): os.mkdir(os.path.join(d, str(i))) t1 = os.path.join(d, '1', 'foo') t2 = os.path.join(d, '2', 'foo') t3 = os.path.join(d, '3', 'foo') t4 = os.path.join(d, '4', 'foo') for i in (t1, t2): with open(i, 'wt') as f: f.write('bar') for i in (t3, t4): with open(i, 'wt') as f: f.write('42') # fix timestamps because otherwise test will fail on slow machines obj = os.stat(t1) os.utime(t2, times=(obj.st_atime, obj.st_mtime)) obj = os.stat(t3) os.utime(t4, times=(obj.st_atime, obj.st_mtime)) # same size and mtime uniqueness = tools.UniquenessSet(dc=False, follow_symlink=False, list_equal_to='') self.assertTrue(uniqueness.check(t1)) self.assertFalse(uniqueness.check(t2)) self.assertTrue(uniqueness.check(t3)) self.assertFalse(uniqueness.check(t4)) os.utime(t1, times=(0, 0)) os.utime(t3, times=(0, 0)) # same size different mtime uniqueness = tools.UniquenessSet(dc=False, follow_symlink=False, list_equal_to='') self.assertTrue(uniqueness.check(t1)) self.assertTrue(uniqueness.check(t2)) self.assertTrue(uniqueness.check(t3)) self.assertTrue(uniqueness.check(t4)) # same size different mtime use deep_check uniqueness = tools.UniquenessSet(dc=True, follow_symlink=False, list_equal_to='') self.assertTrue(uniqueness.check(t1)) self.assertFalse(uniqueness.check(t2)) self.assertTrue(uniqueness.check(t3)) self.assertFalse(uniqueness.check(t4)) def test_checkUnique_hardlinks(self): with TemporaryDirectory() as d: for i in range(1, 5): os.mkdir(os.path.join(d, str(i))) t1 = os.path.join(d, '1', 'foo') t2 = os.path.join(d, '2', 'foo') t3 = os.path.join(d, '3', 'foo') t4 = os.path.join(d, '4', 'foo') with open(t1, 'wt') as f: f.write('bar') os.link(t1, t2) self.assertEqual(os.stat(t1).st_ino, os.stat(t2).st_ino) with open(t3, 'wt') as f: f.write('42') os.link(t3, t4) self.assertEqual(os.stat(t3).st_ino, os.stat(t4).st_ino) uniqueness = tools.UniquenessSet(dc=True, follow_symlink=False, list_equal_to='') self.assertTrue(uniqueness.check(t1)) self.assertFalse(uniqueness.check(t2)) self.assertTrue(uniqueness.check(t3)) self.assertFalse(uniqueness.check(t4)) def test_checkEqual(self): with TemporaryDirectory() as d: for i in range(1, 5): os.mkdir(os.path.join(d, str(i))) t1 = os.path.join(d, '1', 'foo') t2 = os.path.join(d, '2', 'foo') t3 = os.path.join(d, '3', 'foo') t4 = os.path.join(d, '4', 'foo') for i in (t1, t2): with open(i, 'wt') as f: f.write('bar') for i in (t3, t4): with open(i, 'wt') as f: f.write('42') # fix timestamps because otherwise test will fail on slow machines obj = os.stat(t1) os.utime(t2, times=(obj.st_atime, obj.st_mtime)) obj = os.stat(t3) os.utime(t4, times=(obj.st_atime, obj.st_mtime)) # same size and mtime uniqueness = tools.UniquenessSet(dc=False, follow_symlink=False, list_equal_to=t1) self.assertTrue(uniqueness.check(t1)) self.assertTrue(uniqueness.check(t2)) self.assertFalse(uniqueness.check(t3)) os.utime(t1, times=(0, 0)) # same size different mtime uniqueness = tools.UniquenessSet(dc=False, follow_symlink=False, list_equal_to=t1) self.assertTrue(uniqueness.check(t1)) self.assertFalse(uniqueness.check(t2)) self.assertFalse(uniqueness.check(t3)) # same size different mtime use deep_check uniqueness = tools.UniquenessSet(dc=True, follow_symlink=False, list_equal_to=t1) self.assertTrue(uniqueness.check(t1)) self.assertTrue(uniqueness.check(t2)) self.assertFalse(uniqueness.check(t3)) class TestToolsExecuteSubprocess(generic.TestCase): # new method with subprocess def test_returncode(self): self.assertEqual(tools.Execute(['true']).run(), 0) self.assertEqual(tools.Execute(['false']).run(), 1) def test_callback(self): c = lambda x, y: self.callback(self.assertEqual, x, 'foo') tools.Execute(['echo', 'foo'], callback=c).run() self.assertTrue(self.run) self.run = False # give extra user_data for callback c = lambda x, y: self.callback(self.assertEqual, x, y) tools.Execute(['echo', 'foo'], callback=c, user_data='foo').run() self.assertTrue(self.run) self.run = False # no output c = lambda x, y: self.callback(self.fail, 'callback was called unexpectedly') tools.Execute(['true'], callback=c).run() self.assertFalse(self.run) self.run = False def test_pausable(self): proc = tools.Execute(['true']) self.assertTrue(proc.pausable) class TestToolsExecuteOsSystem(generic.TestCase): # old method with os.system def test_returncode(self): self.assertEqual(tools.Execute('true').run(), 0) self.assertEqual(tools.Execute('false').run(), 256) def test_callback(self): c = lambda x, y: self.callback(self.assertEqual, x, 'foo') tools.Execute('echo foo', callback=c).run() self.assertTrue(self.run) self.run = False # give extra user_data for callback c = lambda x, y: self.callback(self.assertEqual, x, y) tools.Execute('echo foo', callback=c, user_data='foo').run() self.assertTrue(self.run) self.run = False # no output c = lambda x, y: self.callback(self.fail, 'callback was called unexpectedly') tools.Execute('true', callback=c).run() self.assertFalse(self.run) self.run = False def test_pausable(self): proc = tools.Execute('true') self.assertFalse(proc.pausable) class Tools_FakeFS(pyfakefs_ut.TestCase): """Tests using a fake filesystem.""" def setUp(self): self.setUpPyfakefs(allow_root_user=False) def test_git_repo_info_none(self): """Actually not a git repo""" self.assertEqual(tools.get_git_repository_info(), None) def test_git_repo_info(self): """Simulate a git repo""" path = pathlib.Path('.git') path.mkdir() # Branch folders and hash containing file foobar = path / 'refs' / 'heads' / 'fix' / 'foobar' foobar.parent.mkdir(parents=True) with foobar.open('w') as handle: handle.write('01234') # HEAD file head = path / 'HEAD' with head.open('w') as handle: handle.write('ref: refs/heads/fix/foobar') # Test self.assertEqual( tools.get_git_repository_info(), { 'hash': '01234', 'branch': 'fix/foobar' } ) backintime-1.5.2/common/tools.py000066400000000000000000002662431465446530500166600ustar00rootroot00000000000000"""Collection of helper functions not fitting to other modules. """ # Back In Time # Copyright (C) 2008-2022 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 sys import pathlib import subprocess import shlex import signal import re import errno import gzip import tempfile import locale import gettext from collections.abc import MutableSet import hashlib import ipaddress import atexit from datetime import datetime from packaging.version import Version from time import sleep import logger # Try to import keyring is_keyring_available = False try: # Jan 4, 2024 aryoda: The env var BIT_USE_KEYRING is neither documented # anywhere nor used at all in the code. # Via "git blame" I have found a commit message saying: # "block subsequent 'import keyring' if it failed once" # So I assume it is an internal temporary env var only. # Note: os.geteuid() is used instead of tools.isRoot() here # because the latter is still not available here in the global # module code. if os.getenv('BIT_USE_KEYRING', 'true') == 'true' and os.geteuid() != 0: import keyring from keyring import backend import keyring.util.platform_ is_keyring_available = True except Exception as e: is_keyring_available = False # block subsequent 'import keyring' if it failed once before os.putenv('BIT_USE_KEYRING', 'false') logger.warning(f"'import keyring' failed with: {repr(e)}") # getting dbus imports to work in Travis CI is a huge pain # use conditional dbus import ON_TRAVIS = os.environ.get('TRAVIS', 'None').lower() == 'true' ON_RTD = os.environ.get('READTHEDOCS', 'None').lower() == 'true' try: import dbus except ImportError: if ON_TRAVIS or ON_RTD: # python-dbus doesn't work on Travis yet. dbus = None else: raise import configfile import bcolors from applicationinstance import ApplicationInstance from exceptions import Timeout, InvalidChar, InvalidCmd, LimitExceeded, PermissionDeniedByPolicy import languages DISK_BY_UUID = '/dev/disk/by-uuid' # |-----------------| # | Handling paths | # |-----------------| def sharePath(): """Get path where Back In Time is installed. This is similar to ``XDG_DATA_DIRS``. If running from source return default ``/usr/share``. Share path like: :: /usr/share /usr/local/share /opt/usr/share Returns: str: Share path. """ share = os.path.abspath( os.path.join(__file__, os.pardir, os.pardir, os.pardir) ) if os.path.basename(share) == 'share': return share return '/usr/share' def backintimePath(*path): """ 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``. """ return os.path.abspath(os.path.join(__file__, os.pardir, os.pardir, *path)) def docPath(): """Not sure what this path is about. """ path = pathlib.Path(sharePath()) / 'doc' / 'backintime-common' # Dev note (buhtz, aryoda, 2024-02): # This piece of code originally resisted in Config.__init__() and was # introduced by Dan in 2008. The reason for the existence of this "if" # is unclear. # Makefile (in common) does only install into share/doc/backintime-common # but never into the the backintime "binary" path so I guess the if is # a) either a distro-specific exception for a distro package that # (manually?) installs the LICENSE into another path # b) or a left-over from old code where the LICENSE was installed # differently... license_file = pathlib.Path(backintimePath()) / 'LICENSE' if license_file.exists(): path = backintimePath() return str(path) # |---------------------------------------------------| # | Internationalization (i18n) & localization (L10n) | # |---------------------------------------------------| _GETTEXT_DOMAIN = 'backintime' _GETTEXT_LOCALE_DIR = pathlib.Path(sharePath()) / 'locale' def _determine_current_used_language_code(translation, language_code): """Return the language code used by GNU gettext for real. Args: translation(gettext.NullTranslations): The translation installed. language_code(str): Configured language code. The used language code can differ from the one in Back In Times config file and from the current systems locale. It is necessary because of situations where the language is not explicit setup in Back In Time config file and GNU gettext do try to find and use a language file for the current systems locale. But even this can fail and the fallback (source language "en") is used or an alternative locale. """ try: # The field "language" is rooted in header of the po-file. current_used_language_code = translation.info()['language'] except KeyError: # Workaround: # BIT versions 1.3.3 or older don't have the "language" field in the # header of their po-files. # The approach is to extract the language code from the full filepath # of the currently used mo-file. # Get the filepath of the used mo-file mo_file_path = gettext.find( domain=_GETTEXT_DOMAIN, localedir=_GETTEXT_LOCALE_DIR, languages=[language_code, ] if language_code else None, ) # Extract the language code form that path if mo_file_path: mo_file_path = pathlib.Path(mo_file_path) # e.g /usr/share/locale/de/LC_MESSAGES/backintime.mo # ^^ current_used_language_code = mo_file_path.relative_to( _GETTEXT_LOCALE_DIR).parts[0] else: # Workaround: Happens when LC_ALL=C, which in BIT context mean # its source language in English. current_used_language_code = 'en' return current_used_language_code def initiate_translation(language_code): """Initiate Class-based API of GNU gettext. Args: language_code(str): Language code to use (based on ISO-639). It installs the ``_()`` (and ``ngettext()`` for plural forms) in the ``builtins`` namespace and eliminates the need to ``import gettext`` and declare ``_()`` in each module. The systems current local is used if the language code is None. """ if language_code: logger.debug(f'Language code "{language_code}".') else: logger.debug('No language code. Use systems current locale.') translation = gettext.translation( domain=_GETTEXT_DOMAIN, localedir=_GETTEXT_LOCALE_DIR, languages=[language_code, ] if language_code else None, fallback=True ) translation.install(names=['ngettext']) used_code = _determine_current_used_language_code( translation, language_code) set_lc_time_by_language_code(used_code) return used_code def set_lc_time_by_language_code(language_code: str): """Set ``LC_TIME`` based on a specific language code. Args: language_code(str): A language code consisting of two letters. The reason is to display correctly translated weekday and months names. Python's :mod:`datetime` module, as well ``PyQt6.QtCore.QDate``, use :mod:`locale` to determine the correct translation. The module :mod:`gettext` and ``PyQt6.QtCore.QTranslator`` is not involved so their setup does not take effect. Be aware that a language code (e.g. ``de``) is not the same as a locale code (e.g. ``de_DE.UTF-8``). This function attempts to determine the latter based on the language code. A warning is logged if it is not possible. """ # Determine the normalized locale code (e.g. "de_DE.UTF-8") by # language code (e.g. "de"). # "de" -> "de_DE.ISO8859-1" -> "de_DE" code = locale.normalize(language_code).split('.')[0] try: # "de_DE" -> "de_DE.UTF-8" code = code + '.' + locale.getencoding() except AttributeError: # Python 3.10 or older code = code + '.' + locale.getpreferredencoding() try: logger.debug(f'Try to set locale.LC_TIME to "{code}" based on ' f'language code "{language_code}".') locale.setlocale(locale.LC_TIME, code) except locale.Error: logger.warning( f'Determined normalized locale code "{code}" (from language code ' f'"{code}" not available or invalid. The code will be ignored. ' 'This might lead to unusual display of dates and timestamps, but ' 'it does not affect the functionality of the application.') def get_available_language_codes(): """Return language codes available in the current installation. The filesystem is searched for ``backintime.mo`` files and the language code is extracted from the full path of that files. Return: List of language codes. """ # full path of one mo-file # e.g. /usr/share/locale/de/LC_MESSAGES/backintime.mo mo = gettext.find(domain=_GETTEXT_DOMAIN, localedir=_GETTEXT_LOCALE_DIR) if mo: mo = pathlib.Path(mo) else: # Workaround. This happens if LC_ALL=C and BIT don't use an explicit # language. Should be re-design. mo = _GETTEXT_LOCALE_DIR / 'xy' / 'LC_MESSAGES' / 'backintime.mo' # e.g. de/LC_MESSAGES/backintime.mo mo = mo.relative_to(_GETTEXT_LOCALE_DIR) # e.g. */LC_MESSAGES/backintime.mo mo = pathlib.Path('*') / pathlib.Path(*mo.parts[1:]) mofiles = _GETTEXT_LOCALE_DIR.rglob(str(mo)) return [p.relative_to(_GETTEXT_LOCALE_DIR).parts[0] for p in mofiles] def get_language_names(language_code): """Return a list with language names in three different flavors. Language codes from `get_available_language_codes()` are combined with `languages.language_names` to prepare the list. Args: language_code (str): Usually the current language used by Back In Time. Returns: A dictionary indexed by language codes with 3-item tuples as values. Each tuple contain three representations of the same language: ``language_code`` (usually the current locales language), the language itself (native) and in English (the source language); e.g. ``ja`` (Japanese) for ``de`` (German) locale is ``('Japanisch', '日本語', 'Japanese')``. """ result = {} codes = ['en'] + get_available_language_codes() for c in codes: try: # A dict with one specific language and how its name is # represented in all other languages. # e.g. "Japanese" in "de" is "Japanisch" # e.g. "Deutsch" in "es" is "alemán" lang = languages.names[c] except KeyError: names = None else: names = ( # in currents locale language lang[language_code], # native lang['_native'], # in English (source language) lang['en'] ) result[c] = names return result def get_native_language_and_completeness(language_code): """Return the language name in its native flavor and the completeness of its translation in percent. Args: language_code(str): The language code. Returns: A two-entry tuple with language name as string and a percent as integer. """ name = languages.names[language_code][language_code] completeness = languages.completeness[language_code] return (name, completeness) # |------------------------------------| # | Miscellaneous, not categorized yet | # |------------------------------------| def registerBackintimePath(*path): """ Add BackInTime path ``path`` to :py:data:`sys.path` so subsequent imports can discover them. Args: *path (str): paths that should be joined to 'backintime' Note: Duplicate in :py:func:`qt/qttools.py` because modules in qt folder would need this to actually import :py:mod:`tools`. """ path = backintimePath(*path) if path not in sys.path: sys.path.insert(0, path) def runningFromSource(): """Check if BackInTime is running from source (without installing). Dev notes by buhtz (2024-04): This function is dangerous and will give a false-negative in fake filesystems (e.g. PyFakeFS). The function should not exist. Beside unit tests it is used only two times. Remove it until migration to pyproject.toml based project packaging (#1575). Returns: bool: ``True`` if BackInTime is running from source. """ return os.path.isfile(backintimePath('common', 'backintime')) def addSourceToPathEnviron(): """ Add 'backintime/common' path to 'PATH' environ variable. """ source = backintimePath('common') path = os.getenv('PATH') if path and source not in path.split(':'): os.environ['PATH'] = '%s:%s' % (source, path) def get_git_repository_info(path=None, hash_length=None): """Return the current branch and last commit hash. About the length of a commit hash. There is no strict rule but it is common sense that 8 to 10 characters are enough to be unique. Credits: https://stackoverflow.com/a/51224861/4865723 Args: path (Path): Path with '.git' folder in (default is current working directory). cut_hash (int): Restrict length of commit hash. Returns: (dict): Dict with keys "branch" and "hash" if it is a git repo, otherwise an `None`. """ if not path: # Default is current working dir path = pathlib.Path.cwd() elif isinstance(path, str): # WORKAROUND until cmoplete migration to pathlib path = pathlib.Path(path) git_folder = path / '.git' if not git_folder.exists(): return None result = {} # branch name with (git_folder / 'HEAD').open('r') as handle: val = handle.read() if not val.startswith('ref: '): result['branch'] = '(detached HEAD)' result['hash'] = val else: result['branch'] = '/'.join(val.split('/')[2:]).strip() # commit hash with (git_folder / 'refs' / 'heads' / result['branch']) \ .open('r') as handle: result['hash'] = handle.read().strip() if hash_length: result['hash'] = result['hash'][:hash_length] return result def readFile(path, default=None): """ Read the file in ``path`` or its '.gz' compressed variant and return its content or ``default`` if ``path`` does not exist. Args: path (str): full path to file that should be read. '.gz' will be added automatically if the file is compressed default (str): default if ``path`` does not exist Returns: str: content of file in ``path`` """ ret_val = default try: if os.path.exists(path): with open(path) as f: ret_val = f.read() elif os.path.exists(path + '.gz'): with gzip.open(path + '.gz', 'rt') as f: ret_val = f.read() except: pass return ret_val def readFileLines(path, default = None): """ Read the file in ``path`` or its '.gz' compressed variant and return its content as a list of lines or ``default`` if ``path`` does not exist. Args: path (str): full path to file that should be read. '.gz' will be added automatically if the file is compressed default (list): default if ``path`` does not exist Returns: list: content of file in ``path`` split by lines. """ ret_val = default try: if os.path.exists(path): with open(path) as f: ret_val = [x.rstrip('\n') for x in f.readlines()] elif os.path.exists(path + '.gz'): with gzip.open(path + '.gz', 'rt') as f: ret_val = [x.rstrip('\n') for x in f.readlines()] except: pass return ret_val def checkCommand(cmd): """Check if command ``cmd`` is a file in 'PATH' environment. Args: cmd (str): The command. Returns: bool: ``True`` if ``cmd`` is in 'PATH' environment otherwise ``False``. """ cmd = cmd.strip() if not cmd: return False if os.path.isfile(cmd): return True return which(cmd) is not None def which(cmd): """Get the fullpath of executable command ``cmd``. Works like command-line 'which' command. Dev note by buhtz (2024-04): Give false-negative results in fake filesystems. Quit often use in the whole code base. But not sure why can we replace it with "which" from shell? Args: cmd (str): The command. Returns: str: Fullpath of command ``cmd`` or ``None`` if command is not available. """ pathenv = os.getenv('PATH', '') path = pathenv.split(':') common = backintimePath('common') if runningFromSource() and common not in path: path.insert(0, common) for directory in path: fullpath = os.path.join(directory, cmd) if os.path.isfile(fullpath) and os.access(fullpath, os.X_OK): return fullpath return None def makeDirs(path): """ Create directories ``path`` recursive and return success. Args: path (str): fullpath to directories that should be created Returns: bool: ``True`` if successful """ path = path.rstrip(os.sep) if not path: return False if os.path.isdir(path): return True else: try: os.makedirs(path) except Exception as e: logger.error("Failed to make dirs '%s': %s" % (path, str(e)), traceDepth=1) return os.path.isdir(path) def mkdir(path, mode=0o755, enforce_permissions=True): """ Create directory ``path``. Args: path (str): full path to directory that should be created mode (int): numeric permission mode Returns: bool: ``True`` if successful """ if os.path.isdir(path): try: if enforce_permissions: os.chmod(path, mode) except: return False return True else: os.mkdir(path, mode) if mode & 0o002 == 0o002: # make file world (other) writable was requested # debian and ubuntu won't set o+w with os.mkdir # this will fix it os.chmod(path, mode) return os.path.isdir(path) def pids(): """ List all PIDs currently running on the system. Returns: list: PIDs as int """ return [int(x) for x in os.listdir('/proc') if x.isdigit()] def processStat(pid): """ Get the stat's of the process with ``pid``. Args: pid (int): Process Indicator Returns: str: stat from /proc/PID/stat """ try: with open('/proc/{}/stat'.format(pid), 'rt') as f: return f.read() except OSError as e: logger.warning('Failed to read process stat from {}: [{}] {}' .format(e.filename, e.errno, e.strerror)) return '' def processPaused(pid): """ Check if process ``pid`` is paused (got signal SIGSTOP). Args: pid (int): Process Indicator Returns: bool: True if process is paused """ m = re.match(r'\d+ \(.+\) T', processStat(pid)) return bool(m) def processName(pid): """ Get the name of the process with ``pid``. Args: pid (int): Process Indicator Returns: str: name of the process """ m = re.match(r'.*\((.+)\).*', processStat(pid)) if m: return m.group(1) def processCmdline(pid): """ Get the cmdline (command that spawnd this process) of the process with ``pid``. Args: pid (int): Process Indicator Returns: str: cmdline of the process """ try: with open('/proc/{}/cmdline'.format(pid), 'rt') as f: return f.read().strip('\n') except OSError as e: logger.warning('Failed to read process cmdline from {}: [{}] {}'.format(e.filename, e.errno, e.strerror)) return '' def pidsWithName(name): """ Get all processes currently running with name ``name``. Args: name (str): name of a process like 'python3' or 'backintime' Returns: list: PIDs as int """ # /proc/###/stat stores just the first 16 chars of the process name return [x for x in pids() if processName(x) == name[:15]] def processExists(name): """ Check if process ``name`` is currently running. Args: name (str): name of a process like 'python3' or 'backintime' Returns: bool: ``True`` if there is a process running with ``name`` """ return len(pidsWithName(name)) > 0 def processAlive(pid): """ Check if the process with PID ``pid`` is alive. Args: pid (int): Process Indicator Returns: bool: ``True`` if the process with PID ``pid`` is alive Raises: ValueError: If ``pid`` is 0 because 'kill(0, SIG)' would send SIG to all processes """ if pid < 0: return False elif pid == 0: raise ValueError('invalid PID 0') else: try: os.kill(pid, 0) #this will raise an exception if the pid is not valid except OSError as err: if err.errno == errno.ESRCH: # ESRCH == No such process return False elif err.errno == errno.EPERM: # EPERM clearly means there's a process to deny access to return True else: raise else: return True def checkXServer(): """ Check if there is a X11 server running on this system. Use ``is_Qt_working`` instead if you want to be sure that Qt is working. Returns: bool: ``True`` if X11 server is running """ # Note: Return values of xdpyinfo <> 0 are not clearly documented. # xdpyinfo does indeed return 1 if it prints # xdypinfo: unable to open display "..." # This seems to be undocumented (at least not in the man pages) # and the source is not obvious here: # https://cgit.freedesktop.org/xorg/app/xdpyinfo/tree/xdpyinfo.c if checkCommand('xdpyinfo'): proc = subprocess.Popen(['xdpyinfo'], stdout = subprocess.DEVNULL, stderr = subprocess.DEVNULL) proc.communicate() return proc.returncode == 0 else: return False def is_Qt_working(systray_required=False): """ Check if the Qt GUI library is working (installed and configured) This function is contained in BiT CLI (not BiT Qt) to allow Qt diagnostics output even if the BiT Qt GUI is not installed. This function does NOT add a hard Qt dependency (just "probing") so it is OK to be in BiT CLI. Args: systray_required: Set to ``True`` if the systray of the desktop environment must be available too to consider Qt as "working" Returns: bool: ``True`` Qt can create a GUI ``False`` Qt fails (or the systray is not available if ``systray_required`` is ``True``) """ # Spawns a new process since it may crash with a SIGABRT and we # don't want to crash BiT if this happens... try: path = os.path.join(backintimePath("common"), "qt_probing.py") cmd = [sys.executable, path] if logger.DEBUG: cmd.append('--debug') with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) as proc: std_output, error_output = proc.communicate(timeout=30) # to get the exit code # "timeout" fixes #1592 (qt_probing.py may hang as root): Kill after timeout logger.debug(f"Qt probing result: exit code {proc.returncode}") if proc.returncode != 2 or logger.DEBUG: # if some Qt parts are missing: Show details logger.debug(f"Qt probing stdout:\n{std_output}") logger.debug(f"Qt probing errout:\n{error_output}") return proc.returncode == 2 or (proc.returncode == 1 and systray_required is False) except FileNotFoundError: logger.error(f"Qt probing script not found: {cmd[0]}") raise # Fix for #1592 (qt_probing.py may hang as root): Kill after timeout except subprocess.TimeoutExpired: proc.kill() outs, errs = proc.communicate() logger.info("Qt probing sub process killed after timeout without response") logger.debug(f"Qt probing stdout:\n{outs}") logger.debug(f"Qt probing errout:\n{errs}") except Exception as e: logger.error(f"Error: {repr(e)}") raise def preparePath(path): """ Removes trailing slash '/' from ``path``. Args: path (str): absolute path Returns: str: path ``path`` without trailing but with leading slash """ path = path.strip("/") path = os.sep + path return path def powerStatusAvailable(): """ Check if org.freedesktop.UPower is available so that :py:func:`tools.onBattery` would return the correct power status. Returns: bool: ``True`` if :py:func:`tools.onBattery` can report power status """ if dbus: try: bus = dbus.SystemBus() proxy = bus.get_object('org.freedesktop.UPower', '/org/freedesktop/UPower') return 'OnBattery' in proxy.GetAll('org.freedesktop.UPower', dbus_interface = 'org.freedesktop.DBus.Properties') except dbus.exceptions.DBusException: pass return False def onBattery(): """ Checks if the system is on battery power. Returns: bool: ``True`` if system is running on battery """ if dbus: try: bus = dbus.SystemBus() proxy = bus.get_object('org.freedesktop.UPower', '/org/freedesktop/UPower') return bool(proxy.Get('org.freedesktop.UPower', 'OnBattery', dbus_interface = 'org.freedesktop.DBus.Properties')) except dbus.exceptions.DBusException: pass return False def rsyncCaps(data = None): """ Get capabilities of the installed rsync binary. This can be different from version to version and also on build arguments used when building rsync. Args: data (str): 'rsync --version' output. This is just for unittests. Returns: list: List of str with rsyncs capabilities """ if not data: proc = subprocess.Popen(['rsync', '--version'], stdout = subprocess.PIPE, universal_newlines = True) data = proc.communicate()[0] caps = [] #rsync >= 3.1 does provide --info=progress2 matchers = [r'rsync\s*version\s*(\d\.\d)', r'rsync\s*version\s*v(\d\.\d.\d)'] for matcher in matchers: m = re.match(matcher, data) if m and Version(m.group(1)) >= Version('3.1'): caps.append('progress2') break #all other capabilities are separated by ',' between #'Capabilities:' and '\n\n' m = re.match(r'.*Capabilities:(.+)\n\n.*', data, re.DOTALL) if not m: return caps for line in m.group(1).split('\n'): caps.extend([i.strip(' \n') for i in line.split(',') if i.strip(' \n')]) return caps def rsyncPrefix(config, no_perms=True, use_mode=['ssh', 'ssh_encfs'], progress=True): """ Get rsync command and all args for creating a new snapshot. Args are based on current profile in ``config``. Args: config (config.Config): current config no_perms (bool): don't sync permissions (--no-p --no-g --no-o) if ``True``. :py:func:`config.Config.preserveAcl` == ``True`` or :py:func:`config.Config.preserveXattr` == ``True`` will overwrite this to ``False`` use_mode (list): if current mode is in this list add additional args for that mode progress (bool): add '--info=progress2' to show progress Returns: list: rsync command with all args but without --include, --exclude, source and destination """ caps = rsyncCaps() cmd = [] if config.nocacheOnLocal(): cmd.append('nocache') cmd.append('rsync') cmd.extend(( # recurse into directories '--recursive', # preserve modification times '--times', # preserve device files (super-user only) '--devices', # preserve special files '--specials', # preserve hard links '--hard-links', # numbers in a human-readable format '--human-readable', # use "new" argument protection '-s' )) if config.useChecksum() or config.forceUseChecksum: cmd.append('--checksum') if config.copyUnsafeLinks(): cmd.append('--copy-unsafe-links') if config.copyLinks(): cmd.append('--copy-links') else: cmd.append('--links') if config.oneFileSystem(): cmd.append('--one-file-system') if config.preserveAcl() and "ACLs" in caps: cmd.append('--acls') # preserve ACLs (implies --perms) no_perms = False if config.preserveXattr() and "xattrs" in caps: cmd.append('--xattrs') # preserve extended attributes no_perms = False if no_perms: cmd.extend(('--no-perms', '--no-group', '--no-owner')) else: cmd.extend(('--perms', # preserve permissions '--executability', # preserve executability '--group', # preserve group '--owner')) # preserve owner (super-user only) if progress and 'progress2' in caps: cmd.extend(('--info=progress2', '--no-inc-recursive')) if config.bwlimitEnabled(): cmd.append('--bwlimit=%d' % config.bwlimit()) if config.rsyncOptionsEnabled(): cmd.extend(shlex.split(config.rsyncOptions())) cmd.extend(rsyncSshArgs(config, use_mode)) return cmd def rsyncSshArgs(config, use_mode=['ssh', 'ssh_encfs']): """ Get SSH args for rsync based on current profile in ``config``. Args: config (config.Config): Current config instance. use_mode (list): If the profiles current mode is in this list add additional args. Returns: list: List of rsync args related to SSH. """ cmd = [] mode = config.snapshotsMode() if mode in ['ssh', 'ssh_encfs'] and mode in use_mode: ssh = config.sshCommand(user_host=False, ionice=False, nice=False) cmd.append('--rsh=' + ' '.join(ssh)) if config.niceOnRemote() \ or config.ioniceOnRemote() \ or config.nocacheOnRemote(): rsync_path = '--rsync-path=' if config.niceOnRemote(): rsync_path += 'nice -n 19 ' if config.ioniceOnRemote(): rsync_path += 'ionice -c2 -n7 ' if config.nocacheOnRemote(): rsync_path += 'nocache ' rsync_path += 'rsync' cmd.append(rsync_path) return cmd def rsyncRemove(config, run_local = True): """ Get rsync command and all args for removing snapshots with rsync. Args: config (config.Config): current config run_local (bool): if True and current mode is ``ssh`` or ``ssh_encfs`` this will add SSH options Returns: list: rsync command with all args """ cmd = ['rsync', '-a', '--delete', '-s'] if run_local: cmd.extend(rsyncSshArgs(config)) return cmd #TODO: check if we really need this def tempFailureRetry(func, *args, **kwargs): while True: try: return func(*args, **kwargs) except (os.error, IOError) as ex: if ex.errno == errno.EINTR: continue else: raise def md5sum(path): """ Calculate md5sum for file in ``path``. Args: path (str): full path to file Returns: str: md5sum of file """ md5 = hashlib.md5() with open(path, 'rb') as f: while True: data = f.read(4096) if not data: break md5.update(data) return md5.hexdigest() def checkCronPattern(s): """ Check if ``s`` is a valid cron pattern. Examples:: 0,10,13,15,17,20,23 */6 Args: s (str): pattern to check Returns: bool: ``True`` if ``s`` is a valid cron pattern Dev note: Schedule for removal. See comment in `config.Config.saveProfile()`. """ if s.find(' ') >= 0: return False try: if s.startswith('*/'): if s[2:].isdigit() and int(s[2:]) <= 24: return True else: return False for i in s.split(','): if i.isdigit() and int(i) <= 24: continue else: return False return True except ValueError: return False #TODO: check if this is still necessary def checkHomeEncrypt(): """ Return ``True`` if users home is encrypted """ home = os.path.expanduser('~') if not os.path.ismount(home): return False if checkCommand('ecryptfs-verify'): try: subprocess.check_call(['ecryptfs-verify', '--home'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except subprocess.CalledProcessError: pass else: return True if checkCommand('encfs'): proc = subprocess.Popen(['mount'], stdout=subprocess.PIPE, universal_newlines = True) mount = proc.communicate()[0] r = re.compile('^encfs on %s type fuse' % home) for line in mount.split('\n'): if r.match(line): return True return False def envLoad(f): """ Load environ variables from file ``f`` into current environ. Do not overwrite existing environ variables. Args: f (str): full path to file with environ variables """ env = os.environ.copy() env_file = configfile.ConfigFile() env_file.load(f, maxsplit = 1) for key in env_file.keys(): value = env_file.strValue(key) if not value: continue if not key in list(env.keys()): os.environ[key] = value del env_file def envSave(f): """ Save environ variables to file that are needed by cron to connect to keyring. This will only work if the user is logged in. Args: f (str): full path to file for environ variables """ env = os.environ.copy() env_file = configfile.ConfigFile() for key in ('GNOME_KEYRING_CONTROL', 'DBUS_SESSION_BUS_ADDRESS', \ 'DBUS_SESSION_BUS_PID', 'DBUS_SESSION_BUS_WINDOWID', \ 'DISPLAY', 'XAUTHORITY', 'GNOME_DESKTOP_SESSION_ID', \ 'KDE_FULL_SESSION'): if key in env: env_file.setStrValue(key, env[key]) env_file.save(f) def keyringSupported(): """ Checks if a keyring (supported by BiT) is available Returns: bool: ``True`` if a supported keyring could be loaded """ if not is_keyring_available: logger.debug('No keyring due to import error.') return False keyring_config_file_folder = "Unknown" try: keyring_config_file_folder = keyring.util.platform_.config_root() except: pass logger.debug(f"Keyring config file folder: {keyring_config_file_folder}") # Determine the currently active backend try: # get_keyring() internally calls keyring.core.init_backend() # which fixes non-available backends for the first call. # See related issue #1321: # https://github.com/bit-team/backintime/issues/1321 # The module name is used instead of the class name # to show only the keyring name (not the technical name) displayName = keyring.get_keyring().__module__ except: displayName = str(keyring.get_keyring()) # technical class name! logger.debug("Available keyring backends:") try: for b in backend.get_all_keyring(): logger.debug(b) except Exception as e: logger.debug("Available backends cannot be listed: " + repr(e)) available_backends = [] # Create a list of installed backends that BiT supports (white-listed). # This is done by trying to put the meta classes ("class definitions", # NOT instances of the class itself!) of the supported backends # into the "backends" list backends_to_check = [ (keyring.backends, ['SecretService', 'Keyring']), (keyring.backends, ['Gnome', 'Keyring']), (keyring.backends, ['kwallet', 'Keyring']), (keyring.backends, ['kwallet', 'DBusKeyring']), (keyring.backend, ['SecretServiceKeyring']), (keyring.backend, ['GnomeKeyring']), (keyring.backend, ['KDEWallet']), # See issue #1410: ChainerBackend is now supported to solve the # problem of configuring the used backend since it iterates over all # of them and is to be the default backend now. Please read the issue # details to understand the unwanted side-effects the chainer could # bring with it. # See also: # https://github.com/jaraco/keyring/blob/977ed03677bb0602b91f005461ef3dddf01a49f6/keyring/backends/chainer.py#L11 # noqa (keyring.backends, ('chainer', 'ChainerBackend')), ] for backend_package, backends in backends_to_check: result = backend_package # e.g. keyring.backends try: # Load the backend step-by-step. # e.g. When the target is "keyring.backends.Gnome.Keyring" then in # a first step "Gnome" part is loaded first and if successful the # "keyring" part. for b in backends: result = getattr(result, b) except AttributeError as err: # Debug message if backend is not available. logger.debug('Metaclass {}.{} not found: {}' .format(backend_package.__name__, '.'.join(backends), repr(err))) else: # Remember the backend class (not an instance) as available. available_backends.append(result) logger.debug("Available supported backends: " + repr(available_backends)) if available_backends and isinstance(keyring.get_keyring(), tuple(available_backends)): logger.debug("Found appropriate keyring '{}'".format(displayName)) return True logger.debug(f"No appropriate keyring found. '{displayName}' can't be " "used with BackInTime.") logger.debug("See https://github.com/bit-team/backintime on how to fix " "this by creating a keyring config file.") return False def password(*args): if is_keyring_available: return keyring.get_password(*args) return None def setPassword(*args): if is_keyring_available: return keyring.set_password(*args) return False def mountpoint(path): """ Get the mountpoint of ``path``. If your HOME is on a separate partition mountpoint('/home/user/foo') would return '/home'. Args: path (str): full path Returns: str: mountpoint of the filesystem """ path = os.path.realpath(os.path.abspath(path)) while path != os.path.sep: if os.path.ismount(path): return path path = os.path.abspath(os.path.join(path, os.pardir)) return path def decodeOctalEscape(s): """ Decode octal-escaped characters with its ASCII dependence. For example '\040' will be a space ' ' Args: s (str): string with or without octal-escaped characters Returns: str: human readable string """ def repl(m): return chr(int(m.group(1), 8)) return re.sub(r'\\(\d{3})', repl, s) def mountArgs(path): """ Get all /etc/mtab args for the filesystem of ``path`` as a list. Example:: [DEVICE, MOUNTPOINT, FILESYSTEM_TYPE, OPTIONS, DUMP, PASS] ['/dev/sda3', '/', 'ext4', 'defaults', '0', '0'] ['/dev/sda1', '/boot', 'ext4', 'defaults', '0', '0'] Args: path (str): full path Returns: list: mount args """ mp = mountpoint(path) with open('/etc/mtab', 'r') as mounts: for line in mounts: args = line.strip('\n').split(' ') if len(args) >= 2: args[1] = decodeOctalEscape(args[1]) if args[1] == mp: return args return None def device(path): """ Get the device for the filesystem of ``path``. Example:: /dev/sda1 /dev/mapper/vglinux proc Args: path (str): full path Returns: str: device """ args = mountArgs(path) if args: return args[0] return None def filesystem(path): """ Get the filesystem type for the filesystem of ``path``. Args: path (str): full path Returns: str: filesystem """ args = mountArgs(path) if args and len(args) >= 3: return args[2] return None def _uuidFromDev_via_filesystem(dev): """Get the UUID for the block device ``dev`` from ``/dev/disk/by-uuid`` in the filesystem. Args: dev (pathlib.Path): The block device path (e.g. ``/dev/sda1``). Returns: str: The UUID or ``None`` if nothing found. """ # /dev/disk/by-uuid path_DISK_BY_UUID = pathlib.Path(DISK_BY_UUID) if not path_DISK_BY_UUID.exists(): return None # Each known uuid for uuid_symlink in path_DISK_BY_UUID.glob('*'): # Resolve the symlink (get it's target) to get the real device name # and compare it with the device we are looking for if dev == uuid_symlink.resolve(): # e.g. 'c7aca0a7-89ed-43f0-a4f9-c744dfe673e0' return uuid_symlink.name # Nothing found return None def _uuidFromDev_via_blkid_command(dev): """Get the UUID for the block device ``dev`` via the extern command ``blkid``. Hint: On most systems the ``blkid`` command is available only for the super-user (e.g. via ``sudo``). Args: dev (pathlib.Path): The block device path (e.g. ``/dev/sda1``). Returns: str: The UUID or ``None`` if nothing found. """ # Call "blkid" command try: # If device does not exist, blkid will exit with a non-zero code output = subprocess.check_output(['blkid', dev], stderr = subprocess.DEVNULL, universal_newlines=True) except (subprocess.CalledProcessError, FileNotFoundError): return None # Parse the commands output for a UUID try: return re.findall(r'.*\sUUID=\"([^\"]*)\".*', output)[0] except IndexError: # nothing found via the regex pattern pass return None def _uuidFromDev_via_udevadm_command(dev): """Get the UUID for the block device ``dev`` via the extern command ``udevadm``. Args: dev (pathlib.Path): The block device path (e.g. ``/dev/sda1``). Returns: str: The UUID or ``None`` if nothing found. """ # Call "udevadm" command try: output = subprocess.check_output(['udevadm', 'info', f'--name={dev}'], stderr = subprocess.DEVNULL, universal_newlines=True) except (subprocess.CalledProcessError, FileNotFoundError): return None # Parse the commands output for a UUID try: return re.findall(r'.*?ID_FS_UUID=(\S+)', output)[0] except IndexError: # nothing found via the regex pattern pass return None def uuidFromDev(dev): """ Get the UUID for the block device ``dev``. Args: dev (str, pathlib.Path): block device path Returns: str: UUID """ # handle Path objects only if not isinstance(dev, pathlib.Path): dev = pathlib.Path(dev) if dev.exists(): dev = dev.resolve() # when /dev/sda1 is a symlink # Look at /dev/disk/by-uuid/ uuid = _uuidFromDev_via_filesystem(dev) if uuid: return uuid # Try extern command "blkid" uuid = _uuidFromDev_via_blkid_command(dev) if uuid: return uuid # "dev" doesn't exist in the filesystem # Try "udevadm" command at the end return _uuidFromDev_via_udevadm_command(dev) def uuidFromPath(path): """ Get the UUID for the for the filesystem of ``path``. Args: path (str): full path Returns: str: UUID """ return uuidFromDev(device(path)) def isRoot(): """ Check if we are root. Returns: bool: ``True`` if we are root """ # The EUID (Effective UID) may be different from the UID (user ID) # in case of SetUID or using "sudo" (where EUID is "root" and UID # is the original user who executed "sudo"). return os.geteuid() == 0 def usingSudo(): """ Check if 'sudo' was used to start this process. Returns: bool: ``True`` if the process was started with sudo """ return isRoot() and os.getenv('HOME', '/root') != '/root' re_wildcard = re.compile(r'(?:\[|\]|\?)') re_asterisk = re.compile(r'\*') re_separate_asterisk = re.compile(r'(?:^\*+[^/\*]|[^/\*]\*+[^/\*]|[^/\*]\*+|\*+[^/\*]|[^/\*]\*+$)') def patternHasNotEncryptableWildcard(pattern): """ Check if ``pattern`` has wildcards ``[ ] ? *``. but return ``False`` for ``foo/*``, ``foo/*/bar``, ``*/bar`` or ``**/bar`` Args: pattern (str): path or pattern to check Returns: bool: ``True`` if ``pattern`` has wildcards ``[ ] ? *`` but ``False`` if wildcard look like ``foo/*``, ``foo/*/bar``, ``*/bar`` or ``**/bar`` """ if not re_wildcard.search(pattern) is None: return True if not re_asterisk is None and not re_separate_asterisk.search(pattern) is None: return True return False def readTimeStamp(fname): """ Read date string from file ``fname`` and try to return datetime. Args: fname (str): Full path to timestamp file. Returns: datetime.datetime: Timestamp object. """ if not os.path.exists(fname): logger.debug(f"No timestamp file '{fname}'") return with open(fname, 'r') as f: s = f.read().strip('\n') time_formats = ( '%Y%m%d %H%M', # BIT like '%Y%m%d', # Anacron like ) for form in time_formats: try: stamp = datetime.strptime(s, form) except ValueError: # invalid format # next iteration pass else: # valid time stamp logger.debug(f"Read timestamp '{stamp}' from file '{fname}'") return stamp def writeTimeStamp(fname): """Write current date and time into file ``fname``. Args: fname (str): Full path to timestamp file. """ now = datetime.now().strftime('%Y%m%d %H%M') logger.debug(f"Write timestamp '{now}' into file '{fname}'") makeDirs(os.path.dirname(fname)) with open(fname, 'w') as f: f.write(now) INHIBIT_LOGGING_OUT = 1 INHIBIT_USER_SWITCHING = 2 INHIBIT_SUSPENDING = 4 INHIBIT_IDLE = 8 INHIBIT_DBUS = ( {'service': 'org.gnome.SessionManager', 'objectPath': '/org/gnome/SessionManager', 'methodSet': 'Inhibit', 'methodUnSet': 'Uninhibit', 'interface': 'org.gnome.SessionManager', 'arguments': (0, 1, 2, 3) }, {'service': 'org.mate.SessionManager', 'objectPath': '/org/mate/SessionManager', 'methodSet': 'Inhibit', 'methodUnSet': 'Uninhibit', 'interface': 'org.mate.SessionManager', 'arguments': (0, 1, 2, 3) }, {'service': 'org.freedesktop.PowerManagement', 'objectPath': '/org/freedesktop/PowerManagement/Inhibit', 'methodSet': 'Inhibit', 'methodUnSet': 'UnInhibit', 'interface': 'org.freedesktop.PowerManagement.Inhibit', 'arguments': (0, 2) }) def inhibitSuspend(app_id = sys.argv[0], toplevel_xid = None, reason = 'take snapshot', flags = INHIBIT_SUSPENDING | INHIBIT_IDLE): """ Prevent machine to go to suspend or hibernate. Returns the inhibit cookie which is used to end the inhibitor. """ if ON_TRAVIS or dbus is None: # no suspend on travis (no dbus either) return # 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 isRoot(): logger.debug("Inhibit Suspend failed because BIT was started as root.") return if not app_id: app_id = 'backintime' try: if not toplevel_xid: toplevel_xid = 0 except IndexError: toplevel_xid = 0 for dbus_props in INHIBIT_DBUS: try: #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 if 'DBUS_SESSION_BUS_ADDRESS' in os.environ: bus = dbus.bus.BusConnection(os.environ['DBUS_SESSION_BUS_ADDRESS']) else: bus = dbus.SessionBus() # This code may hang forever (if BiT is run as root via cron job and no user is logged in). See #1592 interface = bus.get_object(dbus_props['service'], dbus_props['objectPath']) proxy = interface.get_dbus_method(dbus_props['methodSet'], dbus_props['interface']) cookie = proxy(*[(app_id, dbus.UInt32(toplevel_xid), reason, dbus.UInt32(flags))[i] for i in dbus_props['arguments']]) logger.debug('Inhibit Suspend started. Reason: {}'.format(reason)) return (cookie, bus, dbus_props) except dbus.exceptions.DBusException: pass logger.warning('Inhibit Suspend failed.') def unInhibitSuspend(cookie, bus, dbus_props): """ Release inhibit. """ assert isinstance(cookie, int), 'cookie is not int type: %s' % cookie assert isinstance(bus, dbus.bus.BusConnection), 'bus is not dbus.bus.BusConnection type: %s' % bus assert isinstance(dbus_props, dict), 'dbus_props is not dict type: %s' % dbus_props try: interface = bus.get_object(dbus_props['service'], dbus_props['objectPath']) proxy = interface.get_dbus_method(dbus_props['methodUnSet'], dbus_props['interface']) proxy(cookie) logger.debug('Release inhibit Suspend') return None except dbus.exceptions.DBusException: logger.warning('Release inhibit Suspend failed.') return (cookie, bus, dbus_props) def splitCommands(cmds, head = '', tail = '', maxLength = 0): """ Split a list of commands ``cmds`` into multiple commands with each length lower than ``maxLength``. Args: cmds (list): commands head (str): command that need to run first on every iteration of ``cmds`` tail (str): command that need to run after every iteration of ``cmds`` maxLength (int): maximum length a command could be. Don't split if <= 0 Yields: str: new command with length < ``maxLength`` Example:: head cmds[0] cmds[n] tail """ while cmds: s = head while cmds and ((len(s + cmds[0] + tail) <= maxLength) or maxLength <= 0): s += cmds.pop(0) s += tail yield s def escapeIPv6Address(address): """Escape IP addresses with square brackets ``[]`` if they are IPv6. If it is an IPv4 address or a hostname (lettersonly) nothing is changed. Args: address (str): IP-Address to escape if needed. Returns: str: The address, escaped if it is IPv6. """ try: ip = ipaddress.ip_address(address) except ValueError: # invalid IP, e.g. a hostname return address if ip.version == 6: return f'[{address}]' return address def camelCase(s): """ Remove underlines and make every first char uppercase. Args: s (str): string separated by underlines (foo_bar) Returns: str: string without underlines but uppercase chars (FooBar) """ return ''.join([x.capitalize() for x in s.split('_')]) def fdDup(old, new_fd, mode = 'w'): """ Duplicate file descriptor `old` to `new_fd` and closing the latter first. Used to redirect stdin, stdout and stderr from daemonized threads. Args: old (str): 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: fd = open(old, mode) os.dup2(fd.fileno(), new_fd.fileno()) except OSError as e: logger.debug('Failed to redirect {}: {}'.format(old, str(e))) class UniquenessSet: """ Check for uniqueness or equality of files. Args: dc (bool): if ``True`` use deep check which will compare files md5sums if they are of same size but no hardlinks (don't have the same inode). If ``False`` use files size and mtime follow_symlink (bool): if ``True`` check symlinks target instead of the link list_equal_to (str): full path to file. If not empty only return equal files to the given path instead of unique files. """ def __init__(self, dc = False, follow_symlink = False, list_equal_to = ''): self.deep_check = dc self.follow_sym = follow_symlink self._uniq_dict = {} # if not self._uniq_dict[size] -> size already checked with md5sum self._size_inode = set() # if (size,inode) in self._size_inode -> path is a hlink self.list_equal_to = list_equal_to if list_equal_to: st = os.stat(list_equal_to) if self.deep_check: self.reference = (st.st_size, md5sum(list_equal_to)) else: self.reference = (st.st_size, int(st.st_mtime)) def check(self, input_path): """ Check file ``input_path`` for either uniqueness or equality (depending on ``list_equal_to`` from constructor). Args: input_path (str): full path to file Returns: bool: ``True`` if file is unique and ``list_equal_to`` is empty. Or ``True`` if file is equal to file in ``list_equal_to`` """ # follow symlinks ? path = input_path if self.follow_sym and os.path.islink(input_path): path = os.readlink(input_path) if self.list_equal_to: return self.checkEqual(path) else: return self.checkUnique(path) def checkUnique(self, path): """ Check file ``path`` for uniqueness and store a unique key for ``path``. Args: path (str): full path to file Returns: bool: ``True`` if file is unique """ # check if self.deep_check: dum = os.stat(path) size,inode = dum.st_size, dum.st_ino # is it a hlink ? if (size, inode) in self._size_inode: logger.debug("[deep test]: skip, it's a duplicate (size, inode)", self) return False self._size_inode.add((size,inode)) if size not in self._uniq_dict: # first item of that size unique_key = size logger.debug("[deep test]: store current size?", self) else: prev = self._uniq_dict[size] if prev: # store md5sum instead of previously stored size md5sum_prev = md5sum(prev) self._uniq_dict[size] = None self._uniq_dict[md5sum_prev] = prev logger.debug("[deep test]: size duplicate, remove the size, store prev md5sum", self) unique_key = md5sum(path) logger.debug("[deep test]: store current md5sum?", self) else: # store a tuple of (size, modification time) obj = os.stat(path) unique_key = (obj.st_size, int(obj.st_mtime)) # store if not already present, then return True if unique_key not in self._uniq_dict: logger.debug(" >> ok, store!", self) self._uniq_dict[unique_key] = path return True logger.debug(" >> skip (it's a duplicate)", self) return False def checkEqual(self, path): """ Check if ``path`` is equal to the file in ``list_equal_to`` from constructor. Args: path (str): full path to file Returns: bool: ``True`` if file is equal """ st = os.stat(path) if self.deep_check: if self.reference[0] == st.st_size: return self.reference[1] == md5sum(path) return False else: return self.reference == (st.st_size, int(st.st_mtime)) class Alarm(object): """ Establish a callback function that is called after a timeout. The implementation uses a SIGALRM signal so do not call code in the callback that does not support multi-threading (reentrance) or you may cause non-deterministic "random" RTEs. """ def __init__(self, callback = None, overwrite = True): """Create a new alarm instance Args: callback: Function to call when the timer ran down (ensure calling only reentrant code). Use ``None`` to throw a ``Timeout`` exception instead. overwrite: Is it allowed to (re)start the timer even though the current timer is still running ("ticking"). ``True`` cancels the current timer (if active) and restarts with the new timeout. ``False`` silently ignores the start request if the current timer is still "ticking" """ self.callback = callback self.ticking = False self.overwrite = overwrite def start(self, timeout): """ Start the timer (which calls the handler function when the timer ran down). The start is silently ignored if the current timer is still ticking and the the attribute ``overwrite`` is ``False``. Args: timeout: timer count down in seconds """ if self.ticking and not self.overwrite: return try: # Warning: This code may cause non-deterministic RTEs # if the handler function calls code that does # not support reentrance (see e.g. issue #1003). signal.signal(signal.SIGALRM, self.handler) signal.alarm(timeout) except ValueError: pass self.ticking = True def stop(self): """ Stop timer before it comes to an end """ try: signal.alarm(0) self.ticking = False except: pass def handler(self, signum, frame): """ This method is called after the timer ran down to zero and calls the callback function of the alarm instance. Raises: Timeout: If no callback function was set for the alarm instance """ self.ticking = False if self.callback is None: raise Timeout() else: self.callback() class ShutDown(object): """ Shutdown the system after the current snapshot has finished. This should work for KDE, Gnome, Unity, Cinnamon, XFCE, Mate and E17. """ DBUS_SHUTDOWN ={'gnome': {'bus': 'sessionbus', 'service': 'org.gnome.SessionManager', 'objectPath': '/org/gnome/SessionManager', 'method': 'Shutdown', #methods Shutdown # Reboot # Logout 'interface': 'org.gnome.SessionManager', 'arguments': () #arg (only with Logout) # 0 normal # 1 no confirm # 2 force }, 'kde': {'bus': 'sessionbus', 'service': 'org.kde.ksmserver', 'objectPath': '/KSMServer', 'method': 'logout', 'interface': 'org.kde.KSMServerInterface', 'arguments': (-1, 2, -1) #1st arg -1 confirm # 0 no confirm #2nd arg -1 full dialog with default logout # 0 logout # 1 restart # 2 shutdown #3rd arg -1 wait 30sec # 2 immediately }, 'xfce': {'bus': 'sessionbus', 'service': 'org.xfce.SessionManager', 'objectPath': '/org/xfce/SessionManager', 'method': 'Shutdown', #methods Shutdown # Restart # Suspend (no args) # Hibernate (no args) # Logout (two args) 'interface': 'org.xfce.Session.Manager', 'arguments': (True,) #arg True allow saving # False don't allow saving #1st arg (only with Logout) # True show dialog # False don't show dialog #2nd arg (only with Logout) # True allow saving # False don't allow saving }, 'mate': {'bus': 'sessionbus', 'service': 'org.mate.SessionManager', 'objectPath': '/org/mate/SessionManager', 'method': 'Shutdown', #methods Shutdown # Logout 'interface': 'org.mate.SessionManager', 'arguments': () #arg (only with Logout) # 0 normal # 1 no confirm # 2 force }, 'e17': {'bus': 'sessionbus', 'service': 'org.enlightenment.Remote.service', 'objectPath': '/org/enlightenment/Remote/RemoteObject', 'method': 'Halt', #methods Halt -> Shutdown # Reboot # Logout # Suspend # Hibernate 'interface': 'org.enlightenment.Remote.Core', 'arguments': () }, 'e19': {'bus': 'sessionbus', 'service': 'org.enlightenment.wm.service', 'objectPath': '/org/enlightenment/wm/RemoteObject', 'method': 'Shutdown', #methods Shutdown # Restart 'interface': 'org.enlightenment.wm.Core', 'arguments': () }, 'z_freed': {'bus': 'systembus', 'service': 'org.freedesktop.login1', 'objectPath': '/org/freedesktop/login1', 'method': 'PowerOff', 'interface': 'org.freedesktop.login1.Manager', 'arguments': (True,) } } def __init__(self): self.is_root = isRoot() if self.is_root: self.proxy, self.args = None, None else: self.proxy, self.args = self._prepair() self.activate_shutdown = False self.started = False def _prepair(self): """ Try to connect to the given dbus services. If successful it will return a callable dbus proxy and those arguments. """ try: if 'DBUS_SESSION_BUS_ADDRESS' in os.environ: sessionbus = dbus.bus.BusConnection(os.environ['DBUS_SESSION_BUS_ADDRESS']) else: sessionbus = dbus.SessionBus() systembus = dbus.SystemBus() except: return (None, None) des = list(self.DBUS_SHUTDOWN.keys()) des.sort() for de in des: if de == 'gnome' and self.unity7(): continue dbus_props = self.DBUS_SHUTDOWN[de] try: if dbus_props['bus'] == 'sessionbus': bus = sessionbus else: bus = systembus interface = bus.get_object(dbus_props['service'], dbus_props['objectPath']) proxy = interface.get_dbus_method(dbus_props['method'], dbus_props['interface']) return (proxy, dbus_props['arguments']) except dbus.exceptions.DBusException: continue return (None, None) def canShutdown(self): """ Indicate if a valid dbus service is available to shutdown system. """ return not self.proxy is None or self.is_root def askBeforeQuit(self): """ Indicate if ShutDown is ready to fire and so the application shouldn't be closed. """ return self.activate_shutdown and not self.started def shutdown(self): """ Run 'shutdown -h now' if we are root or call the dbus proxy to start the shutdown. """ if not self.activate_shutdown: return False if self.is_root: self.started = True proc = subprocess.Popen(['shutdown', '-h', 'now']) proc.communicate() return proc.returncode if self.proxy is None: return False else: self.started = True return self.proxy(*self.args) def unity7(self): """ Unity >= 7.0 doesn't shutdown automatically. It will only show shutdown dialog and wait for user input. """ if not checkCommand('unity'): return False proc = subprocess.Popen(['unity', '--version'], stdout = subprocess.PIPE, universal_newlines = True) unity_version = proc.communicate()[0] m = re.match(r'unity ([\d\.]+)', unity_version) return m and Version(m.group(1)) >= Version('7.0') and processExists('unity-panel-service') class SetupUdev(object): """ Setup Udev rules for starting BackInTime when a drive get connected. This is done by serviceHelper.py script (included in backintime-qt) running as root though DBus. """ CONNECTION = 'net.launchpad.backintime.serviceHelper' OBJECT = '/UdevRules' INTERFACE = 'net.launchpad.backintime.serviceHelper.UdevRules' MEMBERS = ('addRule', 'save', 'delete') def __init__(self): if dbus is None: self.isReady = False return try: bus = dbus.SystemBus() conn = bus.get_object(SetupUdev.CONNECTION, SetupUdev.OBJECT) self.iface = dbus.Interface(conn, SetupUdev.INTERFACE) except dbus.exceptions.DBusException as e: # Only DBusExceptions are handled to do a "graceful recovery" # by working without a serviceHelper D-Bus connection... # All other exceptions are still raised causing BiT # to stop during startup. # if e._dbus_error_name in ('org.freedesktop.DBus.Error.NameHasNoOwner', # 'org.freedesktop.DBus.Error.ServiceUnknown', # 'org.freedesktop.DBus.Error.FileNotFound'): logger.warning('Failed to connect to Udev serviceHelper daemon ' 'via D-Bus: ' + e.get_dbus_name()) logger.warning('D-Bus message: ' + e.get_dbus_message()) logger.warning('Udev-based profiles cannot be changed or checked ' 'due to Udev serviceHelper connection failure') conn = None # else: # raise self.isReady = bool(conn) def addRule(self, cmd, uuid): """Prepare rules in serviceHelper.py """ if not self.isReady: return try: return self.iface.addRule(cmd, uuid) except dbus.exceptions.DBusException as exc: if exc._dbus_error_name == 'net.launchpad.backintime.InvalidChar': raise InvalidChar(str(exc)) from exc elif exc._dbus_error_name == 'net.launchpad.backintime.InvalidCmd': raise InvalidCmd(str(exc)) from exc elif exc._dbus_error_name == 'net.launchpad.backintime.LimitExceeded': raise LimitExceeded(str(exc)) from exc else: raise def save(self): """Save rules with serviceHelper.py after authentication. If no rules where added before this will delete current rule. """ if not self.isReady: return try: return self.iface.save() except dbus.exceptions.DBusException as err: if err._dbus_error_name == 'com.ubuntu.DeviceDriver.PermissionDeniedByPolicy': raise PermissionDeniedByPolicy(str(err)) from err else: raise err def clean(self): """Clean up remote cache. """ if not self.isReady: return self.iface.clean() class PathHistory(object): def __init__(self, path): self.history = [path,] self.index = 0 def append(self, path): #append path after the current index self.history = self.history[:self.index + 1] + [path,] self.index = len(self.history) - 1 def previous(self): if self.index == 0: return self.history[0] try: path = self.history[self.index - 1] except IndexError: return self.history[self.index] self.index -= 1 return path def next(self): if self.index == len(self.history) - 1: return self.history[-1] try: path = self.history[self.index + 1] except IndexError: return self.history[self.index] self.index += 1 return path def reset(self, path): self.history = [path,] self.index = 0 class OrderedSet(MutableSet): """ OrderedSet from Python recipe http://code.activestate.com/recipes/576694/ """ def __init__(self, iterable=None): self.end = end = [] end += [None, end, end] # sentinel node for doubly linked list self.map = {} # key --> [key, prev, next] if iterable is not None: self |= iterable def __len__(self): return len(self.map) def __contains__(self, key): return key in self.map def add(self, key): if key not in self.map: end = self.end curr = end[1] curr[2] = end[1] = self.map[key] = [key, curr, end] def discard(self, key): if key in self.map: key, prev, next = self.map.pop(key) prev[2] = next next[1] = prev def __iter__(self): end = self.end curr = end[2] while curr is not end: yield curr[0] curr = curr[2] def __reversed__(self): end = self.end curr = end[1] while curr is not end: yield curr[0] curr = curr[1] def pop(self, last=True): if not self: raise KeyError('set is empty') key = self.end[1][0] if last else self.end[2][0] self.discard(key) return key def __repr__(self): if not self: return '%s()' % (self.__class__.__name__,) return '%s(%r)' % (self.__class__.__name__, list(self)) def __eq__(self, other): if isinstance(other, OrderedSet): return len(self) == len(other) and list(self) == list(other) return set(self) == set(other) class Execute(object): """ Execute external commands and handle its output. Args: cmd (:py:class:`str` or :py:class:`list`): command with arguments that should be called. Depending on if this is :py:class:`str` or :py:class:`list` instance the command will be called by either :py:func:`os.system` (deprecated) or :py:class:`subprocess.Popen` callback (method): function which will handle output returned by command (e.g. to extract errors) user_data: extra arguments which will be forwarded to ``callback`` function (e.g. a tuple - which is passed by reference in Python - to "return" results of the callback function as side effect). filters (tuple): Tuple of functions used to filter messages before sending them to the ``callback`` function parent (instance): instance of the calling method used only to proper format log messages conv_str (bool): convert output to :py:class:`str` if True or keep it as :py:class:`bytes` if False join_stderr (bool): join stderr to stdout Note: Signals SIGTSTP ("keyboard stop") and SIGCONT send to Python main process will be forwarded to the command. SIGHUP will kill the process. """ def __init__(self, cmd, callback = None, user_data = None, filters = (), parent = None, conv_str = True, join_stderr = True): self.cmd = cmd self.callback = callback self.user_data = user_data self.filters = filters self.currentProc = None self.conv_str = conv_str self.join_stderr = join_stderr # we need to forward parent to have the correct class name in debug log if parent: self.parent = parent else: self.parent = self if isinstance(self.cmd, list): self.pausable = True self.printable_cmd = ' '.join(self.cmd) logger.debug('Call command "%s"' %self.printable_cmd, self.parent, 2) else: self.pausable = False self.printable_cmd = self.cmd logger.warning('Call command with old os.system method "%s"' %self.printable_cmd, self.parent, 2) def run(self): """ Start the command. Returns: int: return code from the command """ ret_val = 0 out = '' # backwards compatibility with old os.system and os.popen calls # TODO Is this still required as the minimal Python version is 3.10++ now? # TODO Which Python versions are considered as "old" here? if isinstance(self.cmd, str): logger.deprecated(self) if self.callback is None: ret_val = os.system(self.cmd) else: pipe = os.popen(self.cmd, 'r') while True: line = tempFailureRetry(pipe.readline) if not line: break line = line.strip() for f in self.filters: line = f(line) if not line: continue self.callback(line, self.user_data) ret_val = pipe.close() if ret_val is None: ret_val = 0 # new and preferred method using subprocess.Popen # TODO Which minimal Python version is required to be considered as "new"? elif isinstance(self.cmd, (list, tuple)): try: # register signals for pause, resume and kill # Forward these signals (sent to the "backintime" process # normally) to the child process ("rsync" normally). # Note: SIGSTOP (unblockable stop) cannot be forwarded because # it cannot be caught in a signal handler! signal.signal(signal.SIGTSTP, self.pause) signal.signal(signal.SIGCONT, self.resume) signal.signal(signal.SIGHUP, self.kill) except ValueError: # signal only work in qt main thread # TODO What does this imply? pass if self.join_stderr: stderr = subprocess.STDOUT else: stderr = subprocess.DEVNULL logger.debug(f"Starting command '{self.printable_cmd[:min(16, len(self.printable_cmd))]}...'") self.currentProc = subprocess.Popen(self.cmd, stdout = subprocess.PIPE, stderr = stderr) # # TEST code for developers to simulate a killed rsync process # if self.printable_cmd.startswith("rsync --recursive"): # self.currentProc.terminate() # signal 15 (SIGTERM) like "killall" and "kill" do by default # # self.currentProc.send_signal(signal.SIGHUP) # signal 1 # # self.currentProc.kill() # signal 9 # logger.error("rsync killed for testing purposes during development") if self.callback: for line in self.currentProc.stdout: if self.conv_str: line = line.decode().rstrip('\n') else: line = line.rstrip(b'\n') for f in self.filters: line = f(line) if not line: continue self.callback(line, self.user_data) # We use communicate() instead of wait() to avoid a deadlock # when stdout=PIPE and/or stderr=PIPE and the child process # generates enough output to pipe that it blocks waiting for # free buffer. See also: # https://docs.python.org/3.10/library/subprocess.html#subprocess.Popen.wait out = self.currentProc.communicate()[0] # TODO Why is "out" empty instead of containing all stdout? # Most probably because Popen was called with a PIPE as stdout # to directly process each stdout line by calling the callback... ret_val = self.currentProc.returncode # TODO ret_val is sometimes 0 instead of e.g. 23 for rsync. Why? try: # reset signal handler to their default signal.signal(signal.SIGTSTP, signal.SIG_DFL) signal.signal(signal.SIGCONT, signal.SIG_DFL) signal.signal(signal.SIGHUP, signal.SIG_DFL) except ValueError: # signal only work in qt main thread # TODO What does this imply? pass if ret_val != 0: msg = 'Command "%s" returns %s%s%s' %(self.printable_cmd, bcolors.WARNING, ret_val, bcolors.ENDC) if out: msg += ' | %s' %out.decode().strip('\n') logger.warning(msg, self.parent, 2) else: msg = 'Command "%s..." returns %s' %(self.printable_cmd[:min(16, len(self.printable_cmd))], ret_val) if out: msg += ': %s' %out.decode().strip('\n') logger.debug(msg, self.parent, 2) return ret_val def pause(self, signum, frame): """ Slot which will send ``SIGSTOP`` to the command. Is connected to signal ``SIGTSTP``. """ if self.pausable and self.currentProc: logger.info('Pause process "%s"' %self.printable_cmd, self.parent, 2) return self.currentProc.send_signal(signal.SIGSTOP) def resume(self, signum, frame): """ Slot which will send ``SIGCONT`` to the command. Is connected to signal ``SIGCONT``. """ if self.pausable and self.currentProc: logger.info('Resume process "%s"' %self.printable_cmd, self.parent, 2) return self.currentProc.send_signal(signal.SIGCONT) def kill(self, signum, frame): """ Slot which will kill the command. Is connected to signal ``SIGHUP``. """ if self.pausable and self.currentProc: logger.info('Kill process "%s"' %self.printable_cmd, self.parent, 2) return self.currentProc.kill() class Daemon: """ A generic daemon class. Usage: subclass the Daemon class and override the run() method Daemon Copyright by Sander Marechal License CC BY-SA 3.0 http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ """ def __init__(self, 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.appInstance = ApplicationInstance(pidfile, autoExit = False, flock = False) def daemonize(self): """ "Converts" the current process into a daemon (= process running in the background) and sends 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('first fork pid: {}'.format(pid), self) if pid > 0: # exit first parent sys.exit(0) except OSError as e: logger.error("fork #1 failed: %d (%s)" % (e.errno, str(e)), 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('second fork pid: {}'.format(pid), self) if pid > 0: # exit from second parent sys.exit(0) except OSError as e: logger.error("fork #2 failed: %d (%s)" % (e.errno, str(e)), self) sys.exit(1) # redirect standard file descriptors logger.debug('redirect standard file descriptors', self) sys.stdout.flush() sys.stderr.flush() fdDup(self.stdin, sys.stdin, 'r') fdDup(self.stdout, sys.stdout, 'w') fdDup(self.stderr, sys.stderr, 'w') signal.signal(signal.SIGTERM, self.cleanupHandler) if self.pidfile: atexit.register(self.appInstance.exitApplication) # write pidfile logger.debug('write pidfile', self) self.appInstance.startApplication() def cleanupHandler(self, signum, frame): if self.pidfile: self.appInstance.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.appInstance.check(): message = "pidfile %s already exists. Daemon already running?\n" logger.error(message % self.pidfile, self) sys.exit(1) # Start the daemon self.daemonize() self.run() def stop(self): """ Stop the daemon """ if not self.pidfile: logger.debug("Unattended daemon can't be stopped. No PID file", self) return # Get the pid from the pidfile pid, procname = self.appInstance.readPidFile() if not pid: message = "pidfile %s does not exist. Daemon not running?\n" logger.error(message % self.pidfile, self) return # not an error in a restart # Try killing the daemon process try: while True: os.kill(pid, signal.SIGTERM) sleep(0.1) except OSError as err: if err.errno == errno.ESRCH: #no such process self.appInstance.exitApplication() else: logger.error(str(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.debug("Unattended daemon can't be reloaded. No PID file", self) return # Get the pid from the pidfile pid, procname = self.appInstance.readPidFile() if not pid: message = "pidfile %s does not exist. Daemon not running?\n" logger.error(message % self.pidfile, 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.appInstance.exitApplication() else: sys.stderr.write(str(err)) sys.exit(1) def status(self): """ return status """ if not self.pidfile: logger.debug("Unattended daemon can't be checked. No PID file", self) return return not self.appInstance.check() def run(self): """ You should override this method when you subclass Daemon. It will be called after the process has been daemonized by start() or restart(). """ pass # def __logKeyringWarning(): # # from time import sleep # sleep(0.1) # # TODO This function may not be thread-safe # logger.warning('import keyring failed') # # # # if is_keyring_available: # # # delay warning to give logger some time to import # # # Jan 4, 2024 aryoda: # # This is an assumed work-around for #820 (unhandled exception: NoneType) # # but does not seem to fix the problem. # # So I have refactored the possible name shadowing of "keyring" # # as described in # # https://github.com/bit-team/backintime/issues/820#issuecomment-1472971734 # # and left this code unchanged to wait for user feed-back if it works now. # # If the error still occurs I would move the log output call # # to the client of this module so that it is certain to assume it is # # correctly initialized. # # Maybe use backintime.py and app.py for logging... # # (don't call tools.keyringSupported() for that because # # it produces too much debug logging output whenever it is called # # but just query tools.is_keyring_available. # from threading import Thread # thread = Thread(target=__logKeyringWarning, args=()) # thread.start() backintime-1.5.2/common/version.py000066400000000000000000000004251465446530500171710ustar00rootroot00000000000000"""Centralize management about the version. That file is a workaround until the project migrated to a Python build-system. See Issue #1575 for details about that migration. """ # Version string regularyly used by the application and presented to users. __version__ = '1.5.2' backintime-1.5.2/create-manpage-backintime-config.py000077500000000000000000000272741465446530500224520ustar00rootroot00000000000000#!/usr/bin/env python3 # Back In Time # Copyright (C) 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. # # 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 script is a helper to create a manpage about Back In Times's config file. The file `common/config.py` is parsed for variable names, default values and other information. The founder of that script @Germar gave a detailed description about that script in #1354. The script reads every line and tries to analyze it: - It searches for `DEFAULT` and puts those into a `dict` for later replacing the variable with the value. - If that didn't match it will look for lines starting with `#?` which is basically my own description for the manpage-entry. Multiple lines will get merged and stored in `commentline` until the processing of the current config option is done. That will reset `commentline`. - If a line starts with `#` it will be skipped. - Next the script searches for lines which ``return`` the config value (like `snapshots.ssh.port`. There it will extract the key/name (`snapshots.ssh.port`), the default value (`22`), the instance (`Int`) and if it is a profile or general value. - If the line contains a `List` value like `snapshots.include` it will process all values for the list like `snapshots.include..value` and `snapshots.include..type`. Also it will add the size like `snapshots.include.size`. In `process_line` it will replace some information with those I wrote manually in the `#?` description, separated by `;` there is the comment, value, force_default and force_var. If there is no forced value it will chose the value based on the instance with `select_values` """ import re import os import sys from time import strftime, gmtime PATH = os.path.join(os.getcwd(), 'common') 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 ?\( ?[\'"](.*?)[\'"] ?(%?[^,]*?), ?[\'"]?([^\'",\)]*)[\'"]?') HEADER = r'''.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 usable 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 = r'''.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 return { 'bool': 'true|false', 'str': 'text', 'int': '0-99999' }[instance.lower()] 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 key not 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(): d = { 'profiles.version': { INSTANCE: 'int', NAME: 'profiles.version', VALUES: '1', DEFAULT: '1', COMMENT: 'Internal version of profiles config.', REFERENCE: 'configfile.py', LINE: 419 }, '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 }, 'profile.name': { INSTANCE: 'str', NAME: 'profile.name', VALUES: 'text', DEFAULT: 'Main profile', COMMENT: 'Name of this profile.', REFERENCE: 'configfile.py', LINE: 704 } } # Default variables and there values collected from config.py replace_default = {} # Variables named "CONFIG_VERSION" or with names starting with "DEFAULT" regex_default = re.compile(r'(^DEFAULT[\w]*|CONFIG_VERSION)[\s]*= (.*)') with open(CONFIG, 'r') as f: print(f'Read "{CONFIG}".') commentline = '' values = force_var = force_default = instance \ = name = var = default = None for counter, line in enumerate(f, 1): line = line.lstrip() # parse for DEFAULT variable m_default = regex_default.match(line) # DEFAULT variable if m_default: replace_default[m_default.group(1)] \ = m_default.group(2) continue # Comment intended to use for the manpage if line.startswith('#?'): if commentline \ and ';' not in commentline \ and not commentline.endswith('\\n'): commentline += ' ' commentline += line.lstrip('#?').rstrip('\n') continue # Simple comments are ignored if line.startswith('#'): commentline = '' continue # e.g. "return self.profileListValue('snapshots.include', ('str:value', 'int:type'), [], profile_id)" # becomes # ('profile', 'List', 'snapshots.include', "('str:value', 'int:type')", '[]') m = c_list.match(line) if not m: # e.g. "return self.profileBoolValue('snapshots.use_checksum', False, profile_id)" # becomes # ('profile', 'Bool', 'snapshots.use_checksum', '', 'False') m = c.match(line) # Ignore undocumented (without "#?" comments) variables. if m and not commentline: continue 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, r'\-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: print(f'Write into "{MAN}".') 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() backintime-1.5.2/doc/000077500000000000000000000000001465446530500144065ustar00rootroot00000000000000backintime-1.5.2/doc/ENCRYPT_TRANSITION.md000066400000000000000000000121221465446530500176240ustar00rootroot00000000000000# Transition of the encryption feature in _Back In Time_ July 2024 This document outlines the current status of the encryption feature in _Back In Time_. Support for encrypted snapshot profiles is undergoing significant changes. * [Short summary](#short-summary) * [Rational](#rational) * [Alternatives to EncFS](#alternatives-to-encfs) * [Planned steps of the transition process](#planned-steps-of-the-transition-process) * [About EncFS security issues](#about-encfs-security-issues) * [Further readings and resources](#further-readings-and-resources) ## Short summary - To realize encrypted snapshots in _Back In Time_, [EncFS] is used. - EncFS has known security issues and also is no longer maintained. - EncFS library will be **removed** from _Back In Time_ **around the year 2029**. This will happen in slow and small steps with sufficient advance warnings and lead time. - In the best case, EncFS will be replaced with an alternative library (expected to be [GoCryptFS]). - The current maintenance team does not have the resources to implement an alternative for EncFS. So extern contributors are need to step in. - If GoCryptFS or another alternative will be implemented, depending on project resources and contributor availability. ## Rational Removing [EncFS] is necessary because it has known security issues, the upstream project is not active anymore and its maintainer himself recommends to replace EncFS. To keep _Back In Time_ secure and maintenable there is no alternative to deprecat EncFS in _Back In Time_ and finally remove it. The necessity to remove EncFS exists regardless of whether an alternative for this library is implemented or not. ## Alternatives to EncFS The [EncFS] maintainer himself [recommends to switch](https://github.com/vgough/encfs?tab=readme-ov-file#status) to [GoCryptFS]. It seems to work similar to EncFS. Therefore, according to the [current state of research and discussion](https://github.com/bit-team/backintime/issues/1734), GoCryptFS is the preferred choice for a solution. It was also discussed if file system encryption (e.g. [LUKS]) could be an option. In this case _Back In Time_ won't need an encryption feature anymore because the file system tools do take care of it. It might be an option for some of the affected users but [it was also shown](https://github.com/bit-team/backintime/issues/1734#issuecomment-2151875246) that file system encryption is not an option in all use cases. Therefore, LUKS might not be the first choice solution, but is better then nothing in case the project won't find a contributor for replacing EncFS with GoCryptFS or something else. The project also is open for other alternative solutions. ## Planned steps of the transition process The transition is a process *not fixed* in all details and planned to take until the *year 2029 or 2030*. The project will try to adapt to users needs and other extern issues. Therefore the plan is not written in stone. The goal is to have slow and transparent steps in a timeline of multiple years until round about the year 2029 or 2030 when Debian 15 will be released. Current stable Debian is version 12. The transition is scheduled around the release cycles of Debian GNU Linux because Debian has very long release cycles and is the base for most of the distributions out there. 1. Year 2024: Clear and strong warning about the planned removing or replacement of EncFS ([#1735](https://github.com/bit-team/backintime/issues/1734)). Planned for the upcoming release 1.5.0 reaching Ubuntu 24.10 ("Oracular Oriole"). 2. After Debian 13 released (year 2025 or 2026): Disable creation of new EncFS profiles. This become "relevant" for "Debian stable" users round about year 2027/28 when Debian 14 is released. 3. After Debian 14 released (Year 2027 or 2028): Remove EncFS in upstream BIT. This will affect rolling release GNU Linux distributions (e.g. Arch) and upcoming Ubuntu releases. 4. Debian 15 in year 2029 or 2030: Our transformation then has reached Debian stable. ## About EncFS security issues - EncFS Security Audit - https://defuse.ca/audits/encfs.htm (as updated blog post) - https://sourceforge.net/p/encfs/mailman/message/31849549/ (original mailing list entry) - [EncFS#314](https://github.com/vgough/encfs/issues/314) (a **not-fixed** meta issue with a list of several open issues related to the Security Audit) - [EncFS#659](https://github.com/vgough/encfs/issues/659) - [EncFS#9](https://github.com/vgough/encfs/issues/9) - [EncFS - Ubuntu Users Wiki (German)](https://wiki.ubuntuusers.de/Archiv/EncFS) ## Further readings and resources - The meta issue [#1734](https://github.com/bit-team/backintime/issues/1734) about the transition, its current state and related steps and issues. - First discussion about deprecating EncFS was in [#1549](https://github.com/bit-team/backintime/issues/1549). - Our [mailing list](https://mail.python.org/mailman3/lists/bit-dev.python.org). - [EncFS] - [GoCryptFS] [EncFS]: https://github.com/vgough/encfs [GoCryptFS]: https://github.com/rfjakob/gocryptfs [LUKS]: https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup backintime-1.5.2/make-tarball.sh000077500000000000000000000010451465446530500165340ustar00rootroot00000000000000#!/bin/bash VER=`cat VERSION` CURRENT=$(pwd) NEW="backintime-$VER" cd .. if [[ -n "$(which git)" ]] && [[ -x "$(which git)" ]]; then git clone ${CURRENT} ${NEW} else cp -aR ${CURRENT} ${NEW} fi tar cfz backintime-$VER.tar.gz \ ${NEW}/AUTHORS \ ${NEW}/CHANGES \ ${NEW}/LICENSE \ ${NEW}/README.md \ ${NEW}/FAQ.md \ ${NEW}/CONTRIBUTING.md \ ${NEW}/HISTORY.md \ ${NEW}/TRANSLATIONS \ ${NEW}/VERSION \ ${NEW}/updateversion.sh \ ${NEW}/common \ ${NEW}/qt \ ${NEW}/doc rm -rf backintime-$VER backintime-1.5.2/qt/000077500000000000000000000000001465446530500142655ustar00rootroot00000000000000backintime-1.5.2/qt/aboutdlg.py000066400000000000000000000121671465446530500164470ustar00rootroot00000000000000"""The About dialog.""" # Back In Time # Copyright (C) 2008-2022 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 re import pathlib from PyQt6.QtWidgets import (QLabel, QVBoxLayout, QHBoxLayout, QDialog, QDialogButtonBox) from PyQt6.QtCore import Qt, QSize import tools import messagebox import backintime class AboutDlg(QDialog): """The about dialog accessible from the Help menu in the main window.""" def __init__(self, parent=None): """Initialize and layout.""" super().__init__(parent) self.parent = parent self.config = parent.config import icon # pylint: disable=import-outside-toplevel self.setWindowTitle(_('About') + ' ' + self.config.APP_NAME) logo = QLabel('Icon') logo.setPixmap(icon.BIT_LOGO.pixmap(QSize(48, 48))) name = self._create_name_and_version_label() homepage = QLabel( self._to_a_href('https://github.com/bit-team/backintime')) homepage.setTextInteractionFlags( Qt.TextInteractionFlag.LinksAccessibleByMouse) homepage.setOpenExternalLinks(True) bit_copyright = QLabel(self.config.COPYRIGHT + '\n') vlayout = QVBoxLayout(self) hlayout = QHBoxLayout() hlayout.addWidget(logo) hlayout.addWidget(name) hlayout.addStretch() vlayout.addLayout(hlayout) vlayout.addWidget(homepage) vlayout.addWidget(bit_copyright) button_box_left = QDialogButtonBox(self) for label, slot in ((_('Authors'), self._msgbox_authors), (_('Translations'), self._msgbox_translations), (_('License'), self._msgbox_license)): btn = button_box_left.addButton( label, QDialogButtonBox.ButtonRole.ActionRole) btn.clicked.connect(slot) button_box_right = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok) button_box_right.accepted.connect(self.accept) hlayout = QHBoxLayout() hlayout.addWidget(button_box_left) hlayout.addWidget(button_box_right) vlayout.addLayout(hlayout) def _create_name_and_version_label(self): version = backintime.__version__ info = tools.get_git_repository_info( # should be the repos root folder path=pathlib.Path(__file__).parent.parent, hash_length=8) try: git_version \ = f" git branch '{info['branch']}' hash '{info['hash']}'" except TypeError: git_version = '' name = QLabel( f'

{self.config.APP_NAME} {version}

{git_version}') name.setAlignment( Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop) return name def _msgbox_authors(self): file_path = pathlib.Path(tools.docPath()) / 'AUTHORS' content = self._read_about_content(file_path) return messagebox.showInfo(self, _('Authors'), content) def _msgbox_translations(self): file_path = pathlib.Path(tools.docPath()) / 'TRANSLATIONS' content = self._read_about_content(file_path) return messagebox.showInfo(self, _('Translations'), content) def _msgbox_license(self): file_path = pathlib.Path(tools.docPath()) / 'LICENSE' content = self._read_about_content(file_path) return messagebox.showInfo(self, _('License'), content) def _read_about_content(self, file_path): content = file_path.read_text('utf-8') # Convert URLs and Email into content = re.sub(r'<(.*?)>', self._to_a_href, content) # HTML line breaks content = re.sub(r'\n', '
', content) return content def _to_a_href(self, m): """Create a HTML a-tag out of Website and EMail URIs. Args: m (str, re.Match): Match or string to convert. Examples: - 'https://foo.bar' becomes '
https://foo.bar' - 'foo@bar.com' becomes 'foo@bar.com' """ try: raw_string = m.group(1) except AttributeError: raw_string = m if '@' in raw_string: return f'{raw_string}' return f'{raw_string}' backintime-1.5.2/qt/app.py000066400000000000000000002255101465446530500154240ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 sys if not os.getenv('DISPLAY', ''): os.putenv('DISPLAY', ':0.0') import pathlib import re import subprocess import shutil import signal from contextlib import contextmanager from tempfile import TemporaryDirectory # We need to import common/tools.py import qttools_path qttools_path.registerBackintimePath('common') # Workaround until the codebase is rectified/equalized. import tools tools.initiate_translation(None) import qttools import backintime import tools import logger import snapshots import guiapplicationinstance import mount import progress import encfsmsgbox from exceptions import MountException from PyQt6.QtGui import (QAction, QShortcut, QDesktopServices, QPalette, QIcon, QFileSystemModel) from PyQt6.QtWidgets import (QWidget, QFrame, QMainWindow, QToolButton, QLabel, QLineEdit, QCheckBox, QListWidget, QTreeView, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QStyledItemDelegate, QVBoxLayout, QStackedLayout, QSplitter, QGroupBox, QMenu, QToolBar, QProgressBar, QMessageBox, QInputDialog, QDialog, QApplication, ) from PyQt6.QtCore import (Qt, QObject, QPoint, pyqtSlot, pyqtSignal, QTimer, QThread, QEvent, QSortFilterProxyModel, QDir, QUrl) import settingsdialog import snapshotsdialog import logviewdialog from restoredialog import RestoreDialog import languagedialog import messagebox from aboutdlg import AboutDlg class MainWindow(QMainWindow): def __init__(self, config, appInstance, qapp): QMainWindow.__init__(self) self.config = config self.appInstance = appInstance self.qapp = qapp self.snapshots = snapshots.Snapshots(config) self.lastTakeSnapshotMessage = None self.tmpDirs = [] self.firstUpdateAll = True self.disableProfileChanged = False # "Magic" object handling shutdown procedure in different desktop # environments. self.shutdown = tools.ShutDown() # Import on module level not possible because of Qt restrictions. import icon globals()['icon'] = icon # window icon self.qapp.setWindowIcon(icon.BIT_LOGO) # shortcuts without buttons self._create_shortcuts_without_actions() self._create_actions() self._create_menubar() self._create_main_toolbar() # timeline (left widget) self.timeLine = qttools.TimeLine(self) self.timeLine.updateFilesView.connect(self.updateFilesView) # right widget self.filesWidget = QGroupBox(self) filesLayout = QVBoxLayout(self.filesWidget) left, top, right, bottom = filesLayout.getContentsMargins() filesLayout.setContentsMargins(0, 0, right, 0) # main splitter self.mainSplitter = QSplitter(Qt.Orientation.Horizontal, self) self.mainSplitter.addWidget(self.timeLine) self.mainSplitter.addWidget(self.filesWidget) # FilesView toolbar self.toolbar_filesview = self._create_and_get_filesview_toolbar() filesLayout.addWidget(self.toolbar_filesview) # mouse button navigation self.mouseButtonEventFilter = ExtraMouseButtonEventFilter(self) self.setMouseButtonNavigation() # second splitter: # part of files-layout self.secondSplitter = QSplitter(self) self.secondSplitter.setOrientation(Qt.Orientation.Horizontal) self.secondSplitter.setContentsMargins(0, 0, 0, 0) filesLayout.addWidget(self.secondSplitter) # places self.places = QTreeWidget(self) self.places.setRootIsDecorated(False) self.places.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.places.setHeaderLabel(_('Shortcuts')) self.places.header().setSectionsClickable(True) self.places.header().setSortIndicatorShown(True) self.places.header().setSectionHidden(1, True) self.places.header().setSortIndicator( int(self.config.profileIntValue('qt.places.SortColumn', 1)), Qt.SortOrder(self.config.profileIntValue( 'qt.places.SortOrder', Qt.SortOrder.AscendingOrder)) ) self.placesSortLoop = {self.config.currentProfile(): False} self.secondSplitter.addWidget(self.places) self.places.header().sortIndicatorChanged.connect(self.sortPlaces) # files view stacked layout widget = QWidget(self) self.stackFilesView = QStackedLayout(widget) self.secondSplitter.addWidget(widget) # folder don't exist label self.lblFolderDontExists = QLabel( _("This folder doesn't exist\nin the current selected snapshot."), self) qttools.setFontBold(self.lblFolderDontExists) self.lblFolderDontExists.setFrameShadow(QFrame.Shadow.Sunken) self.lblFolderDontExists.setFrameShape(QFrame.Shape.Panel) self.lblFolderDontExists.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter) self.stackFilesView.addWidget(self.lblFolderDontExists) # list files view self.filesView = QTreeView(self) self.stackFilesView.addWidget(self.filesView) self.filesView.setRootIsDecorated(False) self.filesView.setAlternatingRowColors(True) self.filesView.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.filesView.setItemsExpandable(False) self.filesView.setDragEnabled(False) self.filesView.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) self.filesView.header().setSectionsClickable(True) self.filesView.header().setSectionsMovable(False) self.filesView.header().setSortIndicatorShown(True) self.filesViewModel = QFileSystemModel(self) self.filesViewModel.setRootPath(QDir().rootPath()) self.filesViewModel.setReadOnly(True) self.filesViewModel.setFilter(QDir.Filter.AllDirs | QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot | QDir.Filter.Hidden) self.filesViewProxyModel = QSortFilterProxyModel(self) self.filesViewProxyModel.setDynamicSortFilter(True) self.filesViewProxyModel.setSourceModel(self.filesViewModel) self.filesView.setModel(self.filesViewProxyModel) self.filesViewDelegate = QStyledItemDelegate(self) self.filesView.setItemDelegate(self.filesViewDelegate) sortColumn = self.config.intValue( 'qt.main_window.files_view.sort.column', 0) sortOrder = self.config.boolValue( 'qt.main_window.files_view.sort.ascending', True) sortOrder = Qt.SortOrder.AscendingOrder if sortOrder else Qt.SortOrder.DescendingOrder self.filesView.header().setSortIndicator(sortColumn, sortOrder) self.filesViewModel.sort( self.filesView.header().sortIndicatorSection(), self.filesView.header().sortIndicatorOrder()) self.filesView.header() \ .sortIndicatorChanged.connect(self.filesViewModel.sort) self.stackFilesView.setCurrentWidget(self.filesView) # self.setCentralWidget(self.mainSplitter) # context menu for Files View self.filesView.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.filesView.customContextMenuRequested \ .connect(self.contextMenuClicked) self.contextMenu = QMenu(self) self.contextMenu.addAction(self.act_restore) self.contextMenu.addAction(self.act_restore_to) self.contextMenu.addAction(self.act_snapshots_dialog) self.contextMenu.addSeparator() self.btnAddInclude = self.contextMenu.addAction( icon.ADD, _('Add to Include')) self.btnAddExclude = self.contextMenu.addAction( icon.ADD, _('Add to Exclude')) self.btnAddInclude.triggered.connect(self.btnAddIncludeClicked) self.btnAddExclude.triggered.connect(self.btnAddExcludeClicked) self.contextMenu.addSeparator() self.contextMenu.addAction(self.act_show_hidden) # ProgressBar layoutWidget = QWidget() layout = QVBoxLayout(layoutWidget) layout.setContentsMargins(0, 0, 0, 0) layoutWidget.setContentsMargins(0, 0, 0, 0) layoutWidget.setLayout(layout) self.progressBar = QProgressBar(self) self.progressBar.setMinimum(0) self.progressBar.setMaximum(100) self.progressBar.setValue(0) self.progressBar.setTextVisible(False) self.progressBar.setContentsMargins(0, 0, 0, 0) self.progressBar.setFixedHeight(5) self.progressBar.setVisible(False) self.progressBarDummy = QWidget() self.progressBarDummy.setContentsMargins(0, 0, 0, 0) self.progressBarDummy.setFixedHeight(5) self.status = QLabel(self) self.status.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.status) layout.addWidget(self.progressBar) layout.addWidget(self.progressBarDummy) self.statusBar().addWidget(layoutWidget, 100) self.status.setText(_('Done')) self.snapshotsList = [] self.sid = snapshots.RootSnapshot(self.config) self.path = self.config.profileStrValue( 'qt.last_path', self.config.strValue('qt.last_path', '/') ) self.widget_current_path.setText(self.path) self.path_history = tools.PathHistory(self.path) # restore size and position x = self.config.intValue('qt.main_window.x', -1) y = self.config.intValue('qt.main_window.y', -1) if x >= 0 and y >= 0: self.move(x, y) w = self.config.intValue('qt.main_window.width', 800) h = self.config.intValue('qt.main_window.height', 500) self.resize(w, h) mainSplitterLeftWidth = self.config.intValue( 'qt.main_window.main_splitter_left_w', 150) mainSplitterRightWidth = self.config.intValue( 'qt.main_window.main_splitter_right_w', 450) sizes = [mainSplitterLeftWidth, mainSplitterRightWidth] self.mainSplitter.setSizes(sizes) secondSplitterLeftWidth = self.config.intValue( 'qt.main_window.second_splitter_left_w', 150) secondSplitterRightWidth = self.config.intValue( 'qt.main_window.second_splitter_right_w', 300) sizes = [secondSplitterLeftWidth, secondSplitterRightWidth] self.secondSplitter.setSizes(sizes) filesViewColumnNameWidth = self.config.intValue( 'qt.main_window.files_view.name_width', -1) filesViewColumnSizeWidth = self.config.intValue( 'qt.main_window.files_view.size_width', -1) filesViewColumnDateWidth = self.config.intValue( 'qt.main_window.files_view.date_width', -1) if (filesViewColumnNameWidth > 0 and filesViewColumnSizeWidth > 0 and filesViewColumnDateWidth > 0): self.filesView.header().resizeSection(0, filesViewColumnNameWidth) self.filesView.header().resizeSection(1, filesViewColumnSizeWidth) self.filesView.header().resizeSection(2, filesViewColumnDateWidth) # Force dialog to import old configuration if not config.isConfigured(): message = _( '{app_name} appears to be running for the first time as no ' 'configuration is found.' ).format(app_name=self.config.APP_NAME) message = f'{message}\n\n' message = message + _( 'Import an existing configuration (from a backup target ' 'folder or another computer)?') answer = messagebox.warningYesNo(self, message) if answer == QMessageBox.StandardButton.Yes: settingsdialog.RestoreConfigDialog(self).exec() settingsdialog.SettingsDialog(self).exec() if not config.isConfigured(): return profile_id = config.currentProfile() # mount try: mnt = mount.Mount(cfg=self.config, profile_id=profile_id, parent=self) hash_id = mnt.mount() except MountException as ex: messagebox.critical(self, str(ex)) else: self.config.setCurrentHashId(hash_id) if not config.canBackup(profile_id): msg = _("Can't find snapshots folder.") + '\n' \ + _('If it is on a removable drive please plug it in and then ' 'press OK.') messagebox.critical(self, msg) self.filesViewProxyModel.layoutChanged.connect(self.dirListerCompleted) # populate lists self.updateProfiles() self.comboProfiles.currentIndexChanged \ .connect(self.comboProfileChanged) self.filesView.setFocus() self.updateSnapshotActions() # signals self.timeLine.itemSelectionChanged.connect(self.timeLineChanged) self.places.currentItemChanged.connect(self.placesChanged) self.filesView.activated.connect(self.filesViewItemActivated) self.forceWaitLockCounter = 0 self.timerRaiseApplication = QTimer(self) self.timerRaiseApplication.setInterval(1000) self.timerRaiseApplication.setSingleShot(False) self.timerRaiseApplication.timeout.connect(self.raiseApplication) self.timerRaiseApplication.start() self.timerUpdateTakeSnapshot = QTimer(self) self.timerUpdateTakeSnapshot.setInterval(1000) self.timerUpdateTakeSnapshot.setSingleShot(False) self.timerUpdateTakeSnapshot.timeout.connect(self.updateTakeSnapshot) self.timerUpdateTakeSnapshot.start() SetupCron(self).start() # Finished countdown of manual GUI starts if 0 == self.config.manual_starts_countdown(): # Do nothing if English is the current used language if self.config.language_used != 'en': # Show the message only if the current used language is # translated equal or less then 97% self._open_approach_translator_dialog(cutoff=97) # BIT counts down how often the GUI was started. Until the end of that # countdown a dialog with a text about contributing to translating # BIT is presented to the users. self.config.decrement_manual_starts_countdown() # If the encfs-deprecation warning was never shown before if self.config.boolValue('internal.msg_shown_encfs') == False: # Are there profiles using EncFS? encfs_profiles = [] for pid in self.config.profiles(): if 'encfs' in self.config.snapshotsMode(pid): encfs_profiles.append( f'{self.config.profileName(pid)} ({pid})') # EncFS deprecation warning (#1734, #1735) if encfs_profiles: dlg = encfsmsgbox.EncfsExistsWarning(self, encfs_profiles) dlg.exec() self.config.setBoolValue('internal.msg_shown_encfs', True) @property def showHiddenFiles(self): return self.config.boolValue('qt.show_hidden_files', False) # TODO The qt.show_hidden_files key should be a constant instead of a duplicated string @showHiddenFiles.setter def showHiddenFiles(self, value): self.config.setBoolValue('qt.show_hidden_files', value) def _create_actions(self): """Create all action objects used by this main window. All actions are stored as instance attributes to ``self`` and their names begin with ``act_``. The actions can be added to GUI elements (menu entries, buttons) in later steps. Note: All actions used in the main window and its child widgets should be created in this function. Note: Shortcuts need to be strings in a list even if it is only one entry. It is done this way to spare one ``if...else`` statement deciding between `QAction.setShortcuts()` and `QAction.setShortcut()` (singular; without ``s`` at the end). """ action_dict = { # because of "icon" # pylint: disable=undefined-variable # 'Name of action attribute in "self"': ( # ICON, Label text, # trigger_handler_function, # keyboard shortcuts (type list[str]) # tooltip # ), 'act_take_snapshot': ( icon.TAKE_SNAPSHOT, _('Take a snapshot'), self.btnTakeSnapshotClicked, ['Ctrl+S'], _('Use modification time & size for file change detection.')), 'act_take_snapshot_checksum': ( icon.TAKE_SNAPSHOT, _('Take a snapshot (checksum mode)'), self.btnTakeSnapshotChecksumClicked, ['Ctrl+Shift+S'], _('Use checksums for file change detection.')), 'act_pause_take_snapshot': ( icon.PAUSE, _('Pause snapshot process'), lambda: os.kill(self.snapshots.pid(), signal.SIGSTOP), None, None), 'act_resume_take_snapshot': ( icon.RESUME, _('Resume snapshot process'), lambda: os.kill(self.snapshots.pid(), signal.SIGCONT), None, None), 'act_stop_take_snapshot': ( icon.STOP, _('Stop snapshot process'), self.btnStopTakeSnapshotClicked, None, None), 'act_update_snapshots': ( icon.REFRESH_SNAPSHOT, _('Refresh snapshot list'), self.btnUpdateSnapshotsClicked, ['F5', 'Ctrl+R'], None), 'act_name_snapshot': ( icon.SNAPSHOT_NAME, _('Name snapshot'), self.btnNameSnapshotClicked, ['F2'], None), 'act_remove_snapshot': ( icon.REMOVE_SNAPSHOT, _('Remove snapshot'), self.btnRemoveSnapshotClicked, ['Delete'], None), 'act_snapshot_logview': ( icon.VIEW_SNAPSHOT_LOG, _('View snapshot log'), self.btnSnapshotLogViewClicked, None, None), 'act_last_logview': ( icon.VIEW_LAST_LOG, _('View last log'), self.btnLastLogViewClicked, None, None), 'act_settings': ( icon.SETTINGS, _('Manage profiles…'), self.btnSettingsClicked, ['Ctrl+Shift+P'], None), 'act_shutdown': ( icon.SHUTDOWN, _('Shutdown'), None, None, _('Shut down system after snapshot has finished.')), 'act_setup_language': ( icon.LANGUAGE, _('Setup language…'), self.slot_setup_language, None, None), 'act_quit': ( icon.EXIT, _('Exit'), self.close, ['Ctrl+Q'], None), 'act_help_help': ( icon.HELP, _('Help'), self.btnHelpClicked, ['F1'], None), 'act_help_configfile': ( icon.HELP, _('Profiles config file'), self.btnHelpConfigClicked, None, None), 'act_help_website': ( icon.WEBSITE, _('Website'), self.btnWebsiteClicked, None, None), 'act_help_changelog': ( icon.CHANGELOG, _('Changelog'), self.btnChangelogClicked, None, None), 'act_help_faq': ( icon.FAQ, _('FAQ'), self.btnFaqClicked, None, None), 'act_help_question': ( icon.QUESTION, _('Ask a question'), self.btnAskQuestionClicked, None, None), 'act_help_bugreport': ( icon.BUG, _('Report a bug'), self.btnReportBugClicked, None, None), 'act_help_translation': ( icon.LANGUAGE, _('Translation'), self.slot_help_translation, None, _('Shows the message about participation ' 'in translation again.')), 'act_help_encryption': ( icon.ENCRYPT, _('Encryption Transition (EncFS)'), self.slot_help_encryption, None, _('Shows the message about EncFS removal again.')), 'act_help_about': ( icon.ABOUT, _('About'), self.btnAboutClicked, None, None), 'act_restore': ( icon.RESTORE, _('Restore'), self.restoreThis, None, _('Restore the selected files or folders to the ' 'original destination.')), 'act_restore_to': ( icon.RESTORE_TO, _('Restore to …'), self.restoreThisTo, None, _('Restore the selected files or folders to a ' 'new destination.')), 'act_restore_parent': ( icon.RESTORE, 'RESTORE PARENT (DEBUG)', self.restoreParent, None, _('Restore the currently shown folder and all its contents ' 'to the original destination.')), 'act_restore_parent_to': ( icon.RESTORE_TO, 'RESTORE PARENT TO (DEBUG)', self.restoreParentTo, None, _('Restore the currently shown folder and all its contents ' 'to a new destination.')), 'act_folder_up': ( icon.UP, _('Up'), self.btnFolderUpClicked, ['Alt+Up', 'Backspace'], None), 'act_show_hidden': ( icon.SHOW_HIDDEN, _('Show hidden files'), None, ['Ctrl+H'], None), 'act_snapshots_dialog': ( icon.SNAPSHOTS, _('Compare snapshots…'), self.btnSnapshotsClicked, None, None), } for attr in action_dict: ico, txt, slot, keys, tip = action_dict[attr] # Create action (with icon) action = QAction(ico, txt, self) if ico else \ QAction(txt, self) # Connect handler function if slot: action.triggered.connect(slot) # Add keyboardshortcuts if keys: action.setShortcuts(keys) # Tooltip if tip: action.setToolTip(tip) # populate the action to "self" setattr(self, attr, action) # Fine tuning self.act_shutdown.toggled.connect(self.btnShutdownToggled) self.act_shutdown.setCheckable(True) self.act_shutdown.setEnabled(self.shutdown.canShutdown()) self.act_pause_take_snapshot.setVisible(False) self.act_resume_take_snapshot.setVisible(False) self.act_stop_take_snapshot.setVisible(False) self.act_show_hidden.setCheckable(True) self.act_show_hidden.setChecked(self.showHiddenFiles) self.act_show_hidden.toggled.connect(self.btnShowHiddenFilesToggled) def _create_shortcuts_without_actions(self): """Create shortcuts that are not related to a visual element in the GUI and don't have an QAction instance because of that. """ shortcut_list = ( ('Alt+Left', self.btnFolderHistoryPreviousClicked), ('Alt+Right', self.btnFolderHistoryNextClicked), ('Alt+Down', self.btnOpenCurrentItemClicked), ) for keys, slot in shortcut_list: shortcut = QShortcut(keys, self) shortcut.activated.connect(slot) def _create_menubar(self): """Create the menubar and connect it to actions.""" menu_dict = { # The application name itself shouldn't be translated but the # shortcut indicator (marked with &) should be translated and # decided by the translator. _('Back In &Time'): ( self.act_setup_language, self.act_shutdown, self.act_quit, ), _('&Backup'): ( self.act_take_snapshot, self.act_take_snapshot_checksum, self.act_settings, self.act_snapshots_dialog, self.act_name_snapshot, self.act_remove_snapshot, self.act_snapshot_logview, self.act_last_logview, self.act_update_snapshots, ), _('&Restore'): ( self.act_restore, self.act_restore_to, self.act_restore_parent, self.act_restore_parent_to, ), _('&Help'): ( self.act_help_help, self.act_help_configfile, self.act_help_website, self.act_help_changelog, self.act_help_faq, self.act_help_question, self.act_help_bugreport, self.act_help_translation, self.act_help_encryption, self.act_help_about, ) } for key in menu_dict: menu = self.menuBar().addMenu(key) menu.addActions(menu_dict[key]) menu.setToolTipsVisible(True) # The action of the restore menu. It is used by the menuBar and by the # files toolbar. # It is populated to "self" because it's state to be altered. # See "self._enable_restore_ui_elements()" for details. self.act_restore_menu = self.menuBar().actions()[2] # fine tuning. # Attention: Take care of the actions() index here when modifying the # main menu! backup = self.menuBar().actions()[1].menu() backup.insertSeparator(self.act_settings) backup.insertSeparator(self.act_snapshot_logview) help = self.menuBar().actions()[-1].menu() help.insertSeparator(self.act_help_website) help.insertSeparator(self.act_help_about) restore = self.act_restore_menu.menu() restore.insertSeparator(self.act_restore_parent) restore.setToolTipsVisible(True) def _create_main_toolbar(self): """Create the main toolbar and connect it to actions.""" toolbar = self.addToolBar('main') toolbar.setFloatable(False) # Drop-Down: Profiles self.comboProfiles = qttools.ProfileCombo(self) self.comboProfilesAction = toolbar.addWidget(self.comboProfiles) actions_for_toolbar = [ self.act_take_snapshot, self.act_pause_take_snapshot, self.act_resume_take_snapshot, self.act_stop_take_snapshot, self.act_update_snapshots, self.act_name_snapshot, self.act_remove_snapshot, self.act_snapshot_logview, self.act_last_logview, self.act_settings, self.act_shutdown, ] # Add each action to toolbar for act in actions_for_toolbar: toolbar.addAction(act) # Assume an explicit tooltip if it is different from "text()". # Note that Qt use "text()" as "toolTip()" by default. if act.toolTip() != act.text(): if QApplication.instance().isRightToLeft(): # RTL/BIDI language like Hebrew button_tip = f'{act.toolTip()} :{act.text()}' else: # (default) LTR language (e.g. English) button_tip = f'{act.text()}: {act.toolTip()}' toolbar.widgetForAction(act).setToolTip(button_tip) # toolbar sub menu: take snapshot submenu_take_snapshot = QMenu(self) submenu_take_snapshot.addAction(self.act_take_snapshot) submenu_take_snapshot.addAction(self.act_take_snapshot_checksum) submenu_take_snapshot.setToolTipsVisible(True) # get the toolbar buttons widget... button_take_snapshot = toolbar.widgetForAction(self.act_take_snapshot) # ...and add the menu to it button_take_snapshot.setMenu(submenu_take_snapshot) button_take_snapshot.setPopupMode( QToolButton.ToolButtonPopupMode.MenuButtonPopup) # separators and stretchers toolbar.insertSeparator(self.act_settings) toolbar.insertSeparator(self.act_shutdown) def _create_and_get_filesview_toolbar(self): """Create the filesview toolbar object, connect it to actions and return it for later use. Returns: The toolbar object.""" toolbar = QToolBar(self) toolbar.setFloatable(False) actions_for_toolbar = [ self.act_folder_up, self.act_show_hidden, self.act_restore, self.act_snapshots_dialog, ] toolbar.addActions(actions_for_toolbar) # LineEdit widget to display the current path self.widget_current_path = QLineEdit(self) self.widget_current_path.setReadOnly(True) toolbar.insertWidget(self.act_show_hidden, self.widget_current_path) # Restore sub menu restore_sub_menu = self.act_restore_menu.menu() # get the toolbar buttons widget... button_restore = toolbar.widgetForAction(self.act_restore) # ...and add the menu to it button_restore.setMenu(restore_sub_menu) button_restore.setPopupMode( QToolButton.ToolButtonPopupMode.MenuButtonPopup) # Fine tuning toolbar.insertSeparator(self.act_restore) return toolbar def closeEvent(self, event): if self.shutdown.askBeforeQuit(): msg = _('If you close this window, Back In Time will not be able ' 'to shut down your system when the snapshot is finished.') msg = msg + '\n' msg = msg + _('Do you really want to close it?') answer = messagebox.warningYesNo(self, msg) if answer != QMessageBox.StandardButton.Yes: return event.ignore() self.qapp.removeEventFilter(self.mouseButtonEventFilter) self.config.setStrValue('qt.last_path', self.path) self.config.setProfileStrValue('qt.last_path', self.path) self.config.setProfileIntValue( 'qt.places.SortColumn', self.places.header().sortIndicatorSection()) self.config.setProfileIntValue( 'qt.places.SortOrder', self.places.header().sortIndicatorOrder()) self.config.setIntValue('qt.main_window.x', self.x()) self.config.setIntValue('qt.main_window.y', self.y()) self.config.setIntValue('qt.main_window.width', self.width()) self.config.setIntValue('qt.main_window.height', self.height()) sizes = self.mainSplitter.sizes() self.config.setIntValue('qt.main_window.main_splitter_left_w', sizes[0]) self.config.setIntValue('qt.main_window.main_splitter_right_w', sizes[1]) sizes = self.secondSplitter.sizes() self.config.setIntValue('qt.main_window.second_splitter_left_w', sizes[0]) self.config.setIntValue('qt.main_window.second_splitter_right_w', sizes[1]) self.config.setIntValue('qt.main_window.files_view.name_width', self.filesView.header().sectionSize(0)) self.config.setIntValue('qt.main_window.files_view.size_width', self.filesView.header().sectionSize(1)) self.config.setIntValue('qt.main_window.files_view.date_width', self.filesView.header().sectionSize(2)) self.config.setIntValue('qt.main_window.files_view.sort.column', self.filesView.header().sortIndicatorSection()) self.config.setBoolValue('qt.main_window.files_view.sort.ascending', self.filesView.header().sortIndicatorOrder() == Qt.SortOrder.AscendingOrder) self.filesViewModel.deleteLater() #umount try: mnt = mount.Mount(cfg = self.config, parent = self) mnt.umount(self.config.current_hash_id) except MountException as ex: messagebox.critical(self, str(ex)) self.config.save() # cleanup temporary local copies of files which were opened in GUI for d in self.tmpDirs: d.cleanup() event.accept() def updateProfiles(self): if self.disableProfileChanged: return self.disableProfileChanged = True self.comboProfiles.clear() profiles = self.config.profilesSortedByName() for profile_id in profiles: self.comboProfiles.addProfileID(profile_id) if profile_id == self.config.currentProfile(): self.comboProfiles.setCurrentProfileID(profile_id) self.comboProfilesAction.setVisible(len(profiles) > 1) self.updateProfile() self.disableProfileChanged = False def updateProfile(self): self.updateTimeLine() self.updatePlaces() self.updateFilesView(0) # EncFS deprecation warning (see #1734) current_mode = self.config.snapshotsMode(self.config.currentProfile()) if current_mode in ('local_encfs', 'ssh_encfs'): # Show the profile specific warning dialog only once per profile. if self.config.profileBoolValue('msg_shown_encfs') is False: self.config.setProfileBoolValue('msg_shown_encfs', True) dlg = encfsmsgbox.EncfsCreateWarning(self) dlg.exec() def comboProfileChanged(self, index): if self.disableProfileChanged: return profile_id = self.comboProfiles.currentProfileID() if not profile_id: return old_profile_id = self.config.currentProfile() if profile_id != old_profile_id: self.remount(profile_id, old_profile_id) self.config.setCurrentProfile(profile_id) self.config.setProfileIntValue( 'qt.places.SortColumn', self.places.header().sortIndicatorSection(), old_profile_id) self.config.setProfileIntValue( 'qt.places.SortOrder', self.places.header().sortIndicatorOrder(), old_profile_id) self.placesSortLoop[old_profile_id] = False self.places.header().setSortIndicator( int(self.config.profileIntValue( 'qt.places.SortColumn', 1, profile_id)), Qt.SortOrder(self.config.profileIntValue( 'qt.places.SortOrder', Qt.SortOrder.AscendingOrder, profile_id)) ) self.config.setProfileStrValue( 'qt.last_path', self.path, old_profile_id) path = self.config.profileStrValue( 'qt.last_path', self.path, profile_id) if not path == self.path: self.path = path self.path_history.reset(self.path) self.widget_current_path.setText(self.path) self.updateProfile() def remount(self, new_profile_id, old_profile_id): try: mnt = mount.Mount(cfg = self.config, profile_id = old_profile_id, parent = self) hash_id = mnt.remount(new_profile_id) except MountException as ex: messagebox.critical(self, str(ex)) else: self.config.setCurrentHashId(hash_id) def raiseApplication(self): raiseCmd = self.appInstance.raiseCommand() if raiseCmd is None: return logger.debug("Raise cmd: %s" %raiseCmd, self) self.qapp.alert(self) def updateTakeSnapshot(self, force_wait_lock=False): """Update the statusbar and progress indicator with latest message from the snapshot message file. This method is called via a timeout event. See `self.timerUpdateTakeSnapshot`. Also see `Snapshots.takeSnapshotMessage()` for further details. """ if force_wait_lock: self.forceWaitLockCounter = 10 busy = self.snapshots.busy() if busy: self.forceWaitLockCounter = 0 paused = tools.processPaused(self.snapshots.pid()) else: paused = False if self.forceWaitLockCounter > 0: self.forceWaitLockCounter = self.forceWaitLockCounter - 1 fake_busy = busy or self.forceWaitLockCounter > 0 message = _('Working:') takeSnapshotMessage = self.snapshots.takeSnapshotMessage() if fake_busy: if takeSnapshotMessage is None: takeSnapshotMessage = (0, '…') elif takeSnapshotMessage is None: takeSnapshotMessage = self.lastTakeSnapshotMessage if takeSnapshotMessage is None: takeSnapshotMessage = (0, _('Done')) force_update = False if fake_busy: # What is this? if self.act_take_snapshot.isEnabled(): self.act_take_snapshot.setEnabled(False) if not self.act_stop_take_snapshot.isVisible(): for action in (self.act_pause_take_snapshot, self.act_resume_take_snapshot, self.act_stop_take_snapshot): action.setEnabled(True) self.act_take_snapshot.setVisible(False) self.act_pause_take_snapshot.setVisible(not paused) self.act_resume_take_snapshot.setVisible(paused) self.act_stop_take_snapshot.setVisible(True) elif not self.act_take_snapshot.isEnabled(): force_update = True self.act_take_snapshot.setEnabled(True) self.act_take_snapshot.setVisible(True) for action in (self.act_pause_take_snapshot, self.act_resume_take_snapshot, self.act_stop_take_snapshot): action.setVisible(False) # TODO: check if there is a more elegant way than always get a # new snapshot list which is very expensive (time) snapshotsList = snapshots.listSnapshots(self.config) if snapshotsList != self.snapshotsList: self.snapshotsList = snapshotsList self.updateTimeLine(False) takeSnapshotMessage = (0, _('Done')) else: if takeSnapshotMessage[0] == 0: takeSnapshotMessage = (0, _('Done, no backup needed')) self.shutdown.shutdown() if takeSnapshotMessage != self.lastTakeSnapshotMessage or force_update: self.lastTakeSnapshotMessage = takeSnapshotMessage if fake_busy: message = '{}: {}'.format( _('Working'), self.lastTakeSnapshotMessage[1].replace('\n', ' ') ) elif takeSnapshotMessage[0] == 0: message = self.lastTakeSnapshotMessage[1].replace('\n', ' ') else: message = '{}: {}'.format( _('Error'), self.lastTakeSnapshotMessage[1].replace('\n', ' ') ) self.status.setText(message) pg = progress.ProgressFile(self.config) if pg.fileReadable(): self.progressBar.setVisible(True) self.progressBarDummy.setVisible(False) pg.load() self.progressBar.setValue(pg.intValue('percent')) message = ' | '.join(self.getProgressBarFormat(pg, message)) self.status.setText(message) else: self.progressBar.setVisible(False) self.progressBarDummy.setVisible(True) #if not fake_busy: # self.lastTakeSnapshotMessage = None def getProgressBarFormat(self, pg, message): d = ( ('sent', '{}:'.format(_('Sent'))), ('speed', '{}:'.format(_('Speed'))), ('eta', '{}:'.format(_('ETA'))) ) yield '{}%'.format(pg.intValue('percent')) for key, txt in d: value = pg.strValue(key, '') if not value: continue yield txt + ' ' + value yield message def placesChanged(self, item, previous): if item is None: return path = str(item.data(0, Qt.ItemDataRole.UserRole)) if not path: return if path == self.path: return self.path = path self.path_history.append(path) self.updateFilesView(3) def addPlace(self, name, path, icon): """ Dev note (buhtz, 2024-01-14): Parts of that code are redundant with qttools.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: item.setFont(0, qttools.fontBold(item.font(0))) # 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.Window)) self.places.addTopLevelItem(item) if path == self.path: self.places.setCurrentItem(item) return item def updatePlaces(self): self.places.clear() self.addPlace(_('Global'), '', '') self.addPlace(_('Root'), '/', 'computer') self.addPlace(_('Home'), os.path.expanduser('~'), 'user-home') # "Now" or a specific snapshot selected? if self.sid.isRoot: # Use snapshots profiles list of include files and folders include_entries = self.config.include() else: # Determine folders from the snapshot itself base = os.path.expanduser('~') if not os.path.isdir(self.sid.pathBackup(base)): # Folder not mounted. We can skip for the next updatePlaces() return folders = [i.name for i in os.scandir(self.sid.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.places.header().sortIndicatorSection(): indic = self.places.header().sortIndicatorOrder() reverse = True if indic == Qt.SortOrder.DescendingOrder else False include_folders = sorted(include_folders, reverse=reverse) self.addPlace(_('Backup folders'), '', '') for folder in include_folders: self.addPlace(folder, folder, 'document-save') def sortPlaces(self, newColumn, newOrder, force = False): profile_id = self.config.currentProfile() if newColumn == 0 and newOrder == Qt.SortOrder.AscendingOrder: if profile_id in self.placesSortLoop and self.placesSortLoop[profile_id]: newColumn, newOrder = 1, Qt.SortOrder.AscendingOrder self.places.header().setSortIndicator(newColumn, newOrder) self.placesSortLoop[profile_id] = False else: self.placesSortLoop[profile_id] = True self.updatePlaces() def updateSnapshotActions(self, item = None): enabled = False if item is None: item = self.timeLine.currentItem() if not item is None: if not item.snapshotID().isRoot: enabled = True # update remove/name snapshot buttons self.act_name_snapshot.setEnabled(enabled) self.act_remove_snapshot.setEnabled(enabled) self.act_snapshot_logview.setEnabled(enabled) def timeLineChanged(self): item = self.timeLine.currentItem() self.updateSnapshotActions(item) if item is None: return sid = item.snapshotID() if not sid or sid == self.sid: return self.sid = sid self.updatePlaces() self.updateFilesView(2) def updateTimeLine(self, refreshSnapshotsList=True): self.timeLine.clear() self.timeLine.addRoot(snapshots.RootSnapshot(self.config)) if refreshSnapshotsList: self.snapshotsList = [] thread = FillTimeLineThread(self) thread.addSnapshot.connect(self.timeLine.addSnapshot) thread.finished.connect(self.timeLine.checkSelection) thread.start() else: for sid in self.snapshotsList: item = self.timeLine.addSnapshot(sid) self.timeLine.checkSelection() def btnTakeSnapshotClicked(self): backintime.takeSnapshotAsync(self.config) self.updateTakeSnapshot(True) def btnTakeSnapshotChecksumClicked(self): backintime.takeSnapshotAsync(self.config, checksum = True) self.updateTakeSnapshot(True) def btnStopTakeSnapshotClicked(self): os.kill(self.snapshots.pid(), signal.SIGKILL) self.act_stop_take_snapshot.setEnabled(False) self.act_pause_take_snapshot.setEnabled(False) self.act_resume_take_snapshot.setEnabled(False) self.snapshots.setTakeSnapshotMessage(0, 'Snapshot terminated') def btnUpdateSnapshotsClicked(self): self.updateTimeLine() self.updateFilesView(2) def btnNameSnapshotClicked(self): item = self.timeLine.currentItem() if item is None: return sid = item.snapshotID() if sid.isRoot: return name = sid.name new_name, accept = QInputDialog.getText(self, _('Snapshot Name'), '', text = name) if not accept: return new_name = new_name.strip() if name == new_name: return sid.name = new_name item.updateText() def btnLastLogViewClicked (self): with self.suspendMouseButtonNavigation(): logviewdialog.LogViewDialog(self).show() # no SID argument in constructor means "show last log" def btnSnapshotLogViewClicked (self): item = self.timeLine.currentItem() if item is None: return sid = item.snapshotID() if sid.isRoot: return with self.suspendMouseButtonNavigation(): dlg = logviewdialog.LogViewDialog(self, sid) dlg.show() if sid != dlg.sid: self.timeLine.setCurrentSnapshotID(dlg.sid) def btnRemoveSnapshotClicked (self): def hideItem(item): try: item.setHidden(True) except RuntimeError: #item has been deleted #probably because user pressed refresh pass # try to use filter(..) items = [item for item in self.timeLine.selectedItems() if not isinstance(item, snapshots.RootSnapshot)] if not items: return question_msg = '{}\n{}'.format( ngettext( 'Are you sure you want to remove this snapshot?', 'Are you sure you want to remove these snapshots?', len(items) ), '\n'.join([item.snapshotID().displayName for item in items])) answer = messagebox.warningYesNo(self, question_msg) if answer != QMessageBox.StandardButton.Yes: return for item in items: item.setDisabled(True) if item is self.timeLine.currentItem(): self.timeLine.selectRootItem() thread = RemoveSnapshotThread(self, items) thread.refreshSnapshotList.connect(self.updateTimeLine) thread.hideTimelineItem.connect(hideItem) thread.start() def btnSettingsClicked(self): with self.suspendMouseButtonNavigation(): settingsdialog.SettingsDialog(self).show() def btnShutdownToggled(self, checked): self.shutdown.activate_shutdown = checked def contextMenuClicked(self, point): self.contextMenu.exec(self.filesView.mapToGlobal(point)) def btnAboutClicked(self): with self.suspendMouseButtonNavigation(): dlg = AboutDlg(self) dlg.exec() def btnHelpClicked(self): self.openManPage('backintime') def btnHelpConfigClicked(self): self.openManPage('backintime-config') def btnWebsiteClicked(self): self.openUrl('https://github.com/bit-team/backintime') def btnChangelogClicked(self): def aHref(m): if m.group(0).count('@'): return '%(url)s' % {'url': m.group(0)} else: return '%(url)s' % {'url': m.group(0)} def aHref_lp(m): return '%(txt)s' % {'txt': m.group(0), 'id': m.group(1)} changelog_path = pathlib.Path(tools.docPath()) / 'CHANGES' msg = changelog_path.read_text('utf-8') msg = re.sub(r'https?://[^) \n]*', aHref, msg) msg = re.sub(r'(?:LP:|bug) ?#?(\d+)', aHref_lp, msg) msg = re.sub(r'\n', '
', msg) messagebox.showInfo(self, _('Changelog'), msg) def btnFaqClicked(self): self.openUrl('https://github.com/bit-team/backintime/blob/-/FAQ.md') def btnAskQuestionClicked(self): self.openUrl('https://github.com/bit-team/backintime/issues') def btnReportBugClicked(self): self.openUrl('https://github.com/bit-team/backintime/issues/new') def openUrl(self, url): return QDesktopServices.openUrl(QUrl(url)) def openManPage(self, man_page): if not tools.checkCommand('man'): messagebox.critical(self, "Couldn't find 'man' to show the help page. Please install 'man'") return env = os.environ env['MANWIDTH'] = '80' proc = subprocess.Popen(['man', man_page], stdout = subprocess.PIPE, universal_newlines = True, env = env) out, err = proc.communicate() messagebox.showInfo(self, 'Manual Page {}'.format(man_page), out) def btnShowHiddenFilesToggled(self, checked): self.showHiddenFiles = checked self.updateFilesView(1) def backupOnRestore(self): cb = QCheckBox(_( 'Create backup copies with trailing {suffix}\n' 'before overwriting or removing local elements.').format( suffix=self.snapshots.backupSuffix())) cb.setChecked(self.config.backupOnRestore()) qttools.set_wrapped_tooltip( cb, [ _("Newer versions of files will be renamed with trailing " "{suffix} before restoring. If you don't need them anymore " "you can remove them with the following command:").format( suffix=self.snapshots.backupSuffix()), 'find ./ -name "*{suffix}" -delete'.format( suffix=self.snapshots.backupSuffix()) ] ), return { 'widget': cb, 'retFunc': cb.isChecked, 'id': 'backup' } def restoreOnlyNew(self): cb = QCheckBox(_('Only restore elements which do not exist or\n' 'are newer than those in destination.\n' 'Using "rsync --update" option.')) qttools.set_wrapped_tooltip( cb, ["From 'man rsync':", "", "This forces rsync to skip any files which exist on the " "destination and have a modified time that is newer than the " "source file. (If an existing destination file has a " "modification time equal to the source file’s, it will be " "updated if the sizes are different.)", "", "Note that this does not affect the copying of dirs, symlinks, " "or other special files. Also, a difference of file format " "between the sender and receiver is always considered to be " "important enough for an update, no matter what date is on the " "objects. In other words, if the source has a directory where " "the destination has a file, the transfer would occur regardless " "of the timestamps.", "", "This option is a transfer rule, not an exclude, so it doesn’t " "affect the data that goes into the file-lists, and thus it " "doesn’t affect deletions. It just limits the files that the " "receiver requests to be transferred."] ) return {'widget': cb, 'retFunc': cb.isChecked, 'id': 'only_new'} def listRestorePaths(self, paths): fileList = QListWidget() fileList.addItems(paths) fileList.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) return {'widget': fileList, 'retFunc': None} def deleteOnRestore(self): cb = QCheckBox(_('Remove newer elements in original folder.')) qttools.set_wrapped_tooltip( cb, _('Restore selected files or folders to the original destination ' 'and delete files or folders which are not in the snapshot. Be ' 'extremely careful because this will delete files and folders ' 'which were excluded during taking the snapshot.') ) return {'widget': cb, 'retFunc': cb.isChecked, 'id': 'delete'} def confirmRestore(self, paths, restoreTo = None): if restoreTo: msg = ngettext( # singular 'Do you really want to restore this element into the ' 'new folder\n{path}?', # plural 'Do you really want to restore these elements into the ' 'new folder\n{path}?', len(paths)).format(path=restoreTo) else: msg = ngettext( # singular 'Do you really want to restore this element?', # plural 'Do you really want to restore these elements?', len(paths)) confirm, opt = messagebox.warningYesNoOptions(self, msg, (self.listRestorePaths(paths), self.backupOnRestore(), self.restoreOnlyNew(), self.deleteOnRestore())) return (confirm, opt) def confirmDelete(self, warnRoot = False, restoreTo = None): if restoreTo: msg = _('Are you sure you want to remove all newer files ' 'in {path}?').format(path=restoreTo) else: msg = _('Are you sure you want to remove all newer files in your ' 'original folder?') if warnRoot: msg = f'

{msg}

' msg = msg + _( '{BOLD}Warning{BOLDEND}: Deleting files in the filesystem ' 'root could break your entire system.').format( BOLD='', BOLDEND='') msg = msg + '

' answer = messagebox.warningYesNo(self, msg) return answer == QMessageBox.StandardButton.Yes def restoreThis(self): if self.sid.isRoot: return paths = [f for f, idx in self.multiFileSelected(fullPath = True)] with self.suspendMouseButtonNavigation(): confirm, opt = self.confirmRestore(paths) if not confirm: return if opt['delete'] and not self.confirmDelete(warnRoot = '/' in paths): return rd = RestoreDialog(self, self.sid, paths, **opt) rd.exec() def restoreThisTo(self): if self.sid.isRoot: return paths = [f for f, idx in self.multiFileSelected(fullPath = True)] with self.suspendMouseButtonNavigation(): restoreTo = qttools.getExistingDirectory(self, _('Restore to …')) if not restoreTo: return restoreTo = self.config.preparePath(restoreTo) confirm, opt = self.confirmRestore(paths, restoreTo) if not confirm: return if opt['delete'] and not self.confirmDelete(warnRoot = '/' in paths, restoreTo = restoreTo): return rd = RestoreDialog(self, self.sid, paths, restoreTo, **opt) rd.exec() def restoreParent(self): if self.sid.isRoot: return with self.suspendMouseButtonNavigation(): confirm, opt = self.confirmRestore((self.path,)) if not confirm: return if opt['delete'] and not self.confirmDelete(warnRoot = self.path == '/'): return rd = RestoreDialog(self, self.sid, self.path, **opt) rd.exec() def restoreParentTo(self): if self.sid.isRoot: return with self.suspendMouseButtonNavigation(): restoreTo = qttools.getExistingDirectory(self, _('Restore to …')) if not restoreTo: return restoreTo = self.config.preparePath(restoreTo) confirm, opt = self.confirmRestore((self.path,), restoreTo) if not confirm: return if opt['delete'] and not self.confirmDelete(warnRoot = self.path == '/', restoreTo = restoreTo): return rd = RestoreDialog(self, self.sid, self.path, restoreTo, **opt) rd.exec() def btnSnapshotsClicked(self): path, idx = self.fileSelected(fullPath = True) with self.suspendMouseButtonNavigation(): dlg = snapshotsdialog.SnapshotsDialog(self, self.sid, path) if dlg.exec() == QDialog.DialogCode.Accepted: if dlg.sid != self.sid: self.timeLine.setCurrentSnapshotID(dlg.sid) def btnFolderUpClicked(self): if len(self.path) <= 1: return path = os.path.dirname(self.path) if self.path == path: return self.path = path self.path_history.append(self.path) self.updateFilesView(0) def btnFolderHistoryPreviousClicked(self): self._folderHistoryClicked(self.path_history.previous()) def btnFolderHistoryNextClicked(self): self._folderHistoryClicked(self.path_history.next()) def _folderHistoryClicked(self, path): full_path = self.sid.pathBackup(path) if (os.path.isdir(full_path) and self.sid.isExistingPathInsideSnapshotFolder(path)): self.path = path self.updateFilesView(0) def btnOpenCurrentItemClicked(self): path, idx = self.fileSelected() if not path: return self.openPath(path) def btnAddIncludeClicked(self): paths = [f for f, idx in self.multiFileSelected(fullPath = True)] include = self.config.include() updatePlaces = False for item in paths: if os.path.isdir(item): include.append((item, 0)) updatePlaces = True else: include.append((item, 1)) self.config.setInclude(include) if updatePlaces: self.updatePlaces() def btnAddExcludeClicked(self): paths = [f for f, idx in self.multiFileSelected(fullPath = True)] exclude = self.config.exclude() exclude.extend(paths) self.config.setExclude(exclude) def filesViewItemActivated(self, model_index): if self.qapp.keyboardModifiers() and Qt.ControlModifier: return if model_index is None: return rel_path = str(self.filesViewProxyModel.data(model_index)) if not rel_path: return self.openPath(rel_path) def tmpCopy(self, full_path, sid = None): """ Create a temporary local copy of the file ``full_path`` and add the temp folder to ``self.tmpDirs`` which will remove them on exit. Args: full_path (str): path to original file sid (snapshots.SID): snapshot ID used as temp folder suffix Returns: str: temporary path to file """ if sid: sid = '_' + sid.sid d = TemporaryDirectory(suffix = sid) tmp_file = os.path.join(d.name, os.path.basename(full_path)) if os.path.isdir(full_path): shutil.copytree(full_path, tmp_file) else: shutil.copy(full_path, d.name) self.tmpDirs.append(d) return tmp_file def openPath(self, rel_path): rel_path = os.path.join(self.path, rel_path) full_path = self.sid.pathBackup(rel_path) # The class "GenericNonSnapshot" indicates that "Now" is selected # in the snapshots timeline widget. if (os.path.exists(full_path) and (isinstance(self.sid, snapshots.GenericNonSnapshot) # "Now" or self.sid.isExistingPathInsideSnapshotFolder(rel_path))): if os.path.isdir(full_path): self.path = rel_path self.path_history.append(rel_path) self.updateFilesView(0) else: # 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.tmpCopy(full_path, self.sid) file_url = QUrl('file://' + full_path) self.run = QDesktopServices.openUrl(file_url) @pyqtSlot(int) def updateFilesView(self, changed_from, selected_file = None, show_snapshots = False): #0 - files view change directory, 1 - files view, 2 - time_line, 3 - places if 0 == changed_from or 3 == changed_from: selected_file = '' if 0 == changed_from: # update places self.places.setCurrentItem(None) for place_index in range(self.places.topLevelItemCount()): item = self.places.topLevelItem(place_index) if self.path == str(item.data(0, Qt.ItemDataRole.UserRole)): self.places.setCurrentItem(item) break text = '' if self.sid.isRoot: text = _('Now') else: name = self.sid.displayName # buhtz (2023-07)3 blanks at the end of that string as a # workaround to a visual issue where the last character was # cutoff. Not sure if this is DE and/or theme related. # Wasn't able to reproduc in an MWE. Remove after refactoring. text = '{}: {} '.format(_('Snapshot'), name) self.filesWidget.setTitle(text) # try to keep old selected file if selected_file is None: selected_file, idx = self.fileSelected() self.selected_file = selected_file # update files view full_path = self.sid.pathBackup(self.path) if os.path.isdir(full_path): if self.showHiddenFiles: self.filesViewProxyModel.setFilterRegularExpression(r'') else: self.filesViewProxyModel.setFilterRegularExpression(r'^[^\.]') model_index = self.filesViewModel.setRootPath(full_path) proxy_model_index = self.filesViewProxyModel.mapFromSource(model_index) self.filesView.setRootIndex(proxy_model_index) self.toolbar_filesview.setEnabled(False) self.stackFilesView.setCurrentWidget(self.filesView) # TODO: find a signal for this self.dirListerCompleted() else: self._enable_restore_ui_elements(False) self.act_snapshots_dialog.setEnabled(False) self.stackFilesView.setCurrentWidget(self.lblFolderDontExists) # show current path self.widget_current_path.setText(self.path) self.act_restore_parent.setText( _('Restore {path}').format(path=self.path)) self.act_restore_parent_to.setText( _('Restore {path} to …').format(path=self.path)) # update folder_up button state self.act_folder_up.setEnabled(len(self.path) > 1) def _enable_restore_ui_elements(self, enable): """Enable or disable all buttons and menu entries related to the restore feature. Args: enable(bool): Enable or disable. If a specific snapshot is selected in the timeline widget then all restore UI elements are enabled. If "Now" (the first/root) is selected in the timeline all UI elements related to restoring should be disabled. """ # The whole sub-menu incl. its button/entry. The related UI elements # are the "Restore" entry in the main-menu and the toolbar button in # the files-view toolbar. self.act_restore_menu.setEnabled(enable) # This two entries do appear, independent from the sub-menu above, in # the context menu of the files view. self.act_restore.setEnabled(enable) self.act_restore_to.setEnabled(enable) def dirListerCompleted(self): row_count = self.filesViewProxyModel.rowCount( self.filesView.rootIndex()) has_files = row_count > 0 # update restore button state enable = not self.sid.isRoot and has_files # TODO(buhtz) self.btnRestoreMenu.setEnabled(enable) self._enable_restore_ui_elements(enable) # update snapshots button state self.act_snapshots_dialog.setEnabled(has_files) # enable files toolbar self.toolbar_filesview.setEnabled(True) # select selected_file found = False if self.selected_file: index = self.filesView.indexAt(QPoint(0,0)) if not index.isValid(): return while index.isValid(): file_name = (str(self.filesViewProxyModel.data(index))) if file_name == self.selected_file: # TODO: doesn't work reliable self.filesView.setCurrentIndex(index) found = True break index = self.filesView.indexBelow(index) self.selected_file = '' if not found and has_files: self.filesView.setCurrentIndex(self.filesViewProxyModel.index(0, 0)) def fileSelected(self, fullPath=False): """Return path and index of the currently in Files View highlighted (selected) file. Args: fullPath(bool): Resolve relative to a full path. Returns: (tuple): Path as a string and the index. """ idx = qttools.indexFirstColumn(self.filesView.currentIndex()) selected_file = str(self.filesViewProxyModel.data(idx)) if selected_file == '/': # nothing is selected selected_file = '' idx = self.filesViewProxyModel.mapFromSource( self.filesViewModel.index(self.path, 0)) if fullPath: # resolve to full path selected_file = os.path.join(self.path, selected_file) return (selected_file, idx) def multiFileSelected(self, fullPath = False): count = 0 for idx in self.filesView.selectedIndexes(): if idx.column() > 0: continue selected_file = str(self.filesViewProxyModel.data(idx)) if selected_file == '/': continue count += 1 if fullPath: selected_file = os.path.join(self.path, selected_file) yield (selected_file, idx) if not count: # nothing is selected idx = self.filesViewProxyModel.mapFromSource(self.filesViewModel.index(self.path, 0)) if fullPath: selected_file = self.path else: selected_file = '' yield (selected_file, idx) def setMouseButtonNavigation(self): self.qapp.installEventFilter(self.mouseButtonEventFilter) @contextmanager def suspendMouseButtonNavigation(self): self.qapp.removeEventFilter(self.mouseButtonEventFilter) yield self.setMouseButtonNavigation() def _open_approach_translator_dialog(self, cutoff=101): code = self.config.language_used name, perc = tools.get_native_language_and_completeness(code) if perc > cutoff: return dlg = languagedialog.ApproachTranslatorDialog(self, name, perc) dlg.exec() # |-------| # | Slots | # |-------| def slot_setup_language(self): """Show a modal language settings dialog and modify the UI language settings.""" dlg = languagedialog.LanguageDialog( used_language_code=self.config.language_used, configured_language_code=self.config.language()) dlg.exec() # Apply/OK pressed & the language value modified if dlg.result() == 1 and self.config.language() != dlg.language_code: self.config.setLanguage(dlg.language_code) messagebox.info(_('The language settings take effect only after ' 'restarting Back In Time.'), widget_to_center_on=dlg) def slot_help_translation(self): self._open_approach_translator_dialog() def slot_help_encryption(self): dlg = encfsmsgbox.EncfsExistsWarning(self, ['(not determined)']) dlg.exec() class ExtraMouseButtonEventFilter(QObject): """ globally catch mouse buttons 4 and 5 (mostly used as back and forward) and assign it to browse in file history. Dev Note (Germar): Maybe use Qt.BackButton and Qt.ForwardButton instead. """ def __init__(self, mainWindow): self.mainWindow = mainWindow super(ExtraMouseButtonEventFilter, self).__init__() def eventFilter(self, receiver, event): if (event.type() == QEvent.Type.MouseButtonPress and event.button() in (Qt.MouseButton.XButton1, Qt.MouseButton.XButton2)): if event.button() == Qt.MouseButton.XButton1: self.mainWindow.btnFolderHistoryPreviousClicked() if event.button() == Qt.MouseButton.XButton2: self.mainWindow.btnFolderHistoryNextClicked() return True else: return super(ExtraMouseButtonEventFilter, self) \ .eventFilter(receiver, event) class RemoveSnapshotThread(QThread): """ remove snapshots in background thread so GUI will not freeze """ refreshSnapshotList = pyqtSignal() hideTimelineItem = pyqtSignal(qttools.SnapshotItem) def __init__(self, parent, items): self.config = parent.config self.snapshots = parent.snapshots self.items = items super(RemoveSnapshotThread, self).__init__(parent) def run(self): last_snapshot = snapshots.lastSnapshot(self.config) renew_last_snapshot = False #inhibit suspend/hibernate during delete self.config.inhibitCookie = tools.inhibitSuspend(toplevel_xid = self.config.xWindowId, reason = 'deleting snapshots') for item, sid in [(x, x.snapshotID()) for x in self.items]: self.snapshots.remove(sid) self.hideTimelineItem.emit(item) if sid == last_snapshot: renew_last_snapshot = True self.refreshSnapshotList.emit() #set correct last snapshot again if renew_last_snapshot: self.snapshots.createLastSnapshotSymlink(snapshots.lastSnapshot(self.config)) #release inhibit suspend if self.config.inhibitCookie: self.config.inhibitCookie = tools.unInhibitSuspend(*self.config.inhibitCookie) class FillTimeLineThread(QThread): """ add snapshot IDs to timeline in background """ addSnapshot = pyqtSignal(snapshots.SID) def __init__(self, parent): self.parent = parent self.config = parent.config super(FillTimeLineThread, self).__init__(parent) def run(self): for sid in snapshots.iterSnapshots(self.config): self.addSnapshot.emit(sid) self.parent.snapshotsList.append(sid) self.parent.snapshotsList.sort() class SetupCron(QThread): """ Check crontab entries on startup. """ def __init__(self, parent): self.config = parent.config super(SetupCron, self).__init__(parent) def run(self): self.config.setupCron() if __name__ == '__main__': cfg = backintime.startApp('backintime-qt') raiseCmd = '' if len(sys.argv) > 1: raiseCmd = '\n'.join(sys.argv[1 :]) appInstance = guiapplicationinstance.GUIApplicationInstance(cfg.appInstanceFile(), raiseCmd) cfg.PLUGIN_MANAGER.load(cfg = cfg) cfg.PLUGIN_MANAGER.appStart() logger.openlog() qapp = qttools.createQApplication(cfg.APP_NAME) translator = qttools.initiate_translator(cfg.language()) qapp.installTranslator(translator) mainWindow = MainWindow(cfg, appInstance, qapp) if cfg.isConfigured(): cfg.xWindowId = mainWindow.winId() mainWindow.show() qapp.exec() cfg.PLUGIN_MANAGER.appExit() appInstance.exitApplication() logger.closelog() # must be last line (log until BiT "dies" ;-) backintime-1.5.2/qt/backintime-qt000077500000000000000000000022151465446530500167430ustar00rootroot00000000000000#!/bin/sh # Back In Time # Copyright (C) 2008-2022 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. #fixing gray window error #https://launchpad.net/bugs/1493020 export QT_GRAPHICSSYSTEM="native" CUR_PATH="$(dirname $(readlink -m $0))" if [ -f "${CUR_PATH}/app.py" ]; then APP_PATH=$CUR_PATH else APP_PATH=$(readlink -m "${CUR_PATH}/../share/backintime/qt") fi /usr/bin/python3 -Es ${APP_PATH}/app.py "$@" backintime-1.5.2/qt/backintime-qt-root.desktop000066400000000000000000000007151465446530500213740ustar00rootroot00000000000000[Desktop Entry] Name=Back In Time (root) GenericName=Backup Exec=/usr/bin/backintime-qt_polkit %f Icon=document-save Terminal=false X-MultipleArgs=false Type=Application StartupNotify=true Categories=Qt;System; Comment=Simple backup system Comment[de]=Ein simples Backup-Programm inspiriert von »Time Machine«, »TimeVault« und dem »flyback project«. Comment[sl]=Enostaven sistem ustvarjanja varnostnih kopij Keywords=automatic;snapshot;restore;rsync;root;backintime-1.5.2/qt/backintime-qt.desktop000066400000000000000000000006711465446530500204140ustar00rootroot00000000000000[Desktop Entry] Name=Back In Time GenericName=Backup Exec=backintime-qt Icon=document-save Terminal=false X-MultipleArgs=false Type=Application StartupNotify=true Categories=Qt;KDE;GNOME;System; Comment=Simple backup system Comment[sl]=Enostaven sistem ustvarjanja varnostnih kopij Comment[de]=Ein simples Backup-Programm inspiriert von »Time Machine«, »TimeVault« und dem »flyback project«. Keywords=automatic;snapshot;restore;rsync; backintime-1.5.2/qt/backintime-qt_polkit000077500000000000000000000005351465446530500203300ustar00rootroot00000000000000#!/bin/sh if [ "x$XDG_SESSION_TYPE" = "xwayland" ]; then # PREFIX="env QT_QPA_PLATFORM=wayland-egl XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" # Empty prefix to use the default Qt platform plugin (normally xcb) to fix #836 and #1350 PREFIX="" else # X11 PREFIX="" fi pkexec --disable-internal-agent $PREFIX "/usr/bin/backintime-qt" "$@" backintime-1.5.2/qt/configure000077500000000000000000000201171465446530500161750ustar00rootroot00000000000000#!/bin/sh #clean up if [ -e Makefile ]; then rm Makefile; fi #tmp files MAKEFILE="$(mktemp)" UNINSTALL_FILES="$(mktemp)" UNINSTALL_DIRS="$(mktemp)" #set default options PYTHON="/usr/bin/python3" USR_BIN_FILES="backintime-qt backintime-qt_polkit" DBUS_SERVICE_FILES="net.launchpad.backintime.serviceHelper.service" usage () { echo "Usage:" 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 "--python=PYTHON_BINARY" echo "\tuse PYTHON_BINARY to start Python3" } addInstallFiles () { file=$1 dest=$2 mode=$3 if [ -z "$mode" ]; then mode=644 fi for i in $(ls $file); do addInstallFile "$i" "$dest" "$mode" done } addInstallFile () { file=$1 dest=$2 mode=$3 if [ -z "$mode" ]; then mode=644 fi printf "\tinstall --mode=$mode $file \$(DEST)$dest\n" >> ${MAKEFILE} addUninstallFile "$file" "$dest" } addInstallFileRename () { file=$1 dest=$2 mode=$3 if [ -z "$mode" ]; then mode=644 fi printf "\tinstall --mode=$mode $file \$(DEST)$dest\n" >> ${MAKEFILE} addUninstallFileRename "$dest" } addUninstallFile () { file=$(basename "$1") dest=$2 printf "\trm -f \$(DEST)$dest/$file\n" >> ${UNINSTALL_FILES} } addUninstallFileRename () { file=$1 printf "\trm -f \$(DEST)$file\n" >> ${UNINSTALL_FILES} } addInstallDir () { dest=$1 printf "\tinstall -d \$(DEST)$dest\n" >> ${MAKEFILE} addUninstallDir "$dest" } addUninstallDir () { dest=$1 printf "\tif [ -d \$(DEST)$dest ]; then rmdir --ignore-fail-on-non-empty \$(DEST)$dest; fi\n" >> ${UNINSTALL_DIRS} } addComment () { printf "\t# Install $1\n" >> ${MAKEFILE} printf "\t# Uninstall files $1\n" >> ${UNINSTALL_FILES} printf "\t# Uninstall directory $1\n" >> ${UNINSTALL_DIRS} } addNewline () { printf "\n" >> ${MAKEFILE} printf "\n" >> ${UNINSTALL_FILES} printf "\n" >> ${UNINSTALL_DIRS} } #get commandline arguments unknown_args="" for arg in $*; do 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" fi if [ ! -f "$PYTHON" ]; then echo "Warning: \"${PYTHON}\" not found on this computer" fi #patch python command #use 'python' or 'python3' to start Python Version 3.x if [ -n "$(sed -e "s#^/usr/bin/python3\? #${PYTHON} #gw /dev/stdout" -i $USR_BIN_FILES)" ] \ && [ -n "$(sed -e "s#^Exec=/usr/bin/python3\? #Exec=${PYTHON} #gw /dev/stdout" -i $DBUS_SERVICE_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 #start Makefile printf "PREFIX=/usr\n" >> ${MAKEFILE} printf "DEST=\$(DESTDIR)\$(PREFIX)\n\n" >> ${MAKEFILE} printf "all:\tbuild\n\n" >> ${MAKEFILE} printf "build:\tcompress\n\n" >> ${MAKEFILE} printf "clean:\n" >> ${MAKEFILE} printf "\trm -f po/*.mo\n" >> ${MAKEFILE} printf "\trm -f man/C/*.gz\n\n" >> ${MAKEFILE} # Create install and uninstall target printf "install:\n" >> ${MAKEFILE} # Migration printf "\t# Clean-up installed old files that were renamed or moved in later BiT versions\n" >> ${MAKEFILE} printf "\trm -f \$(DEST)/etc/dbus-1/system.d/net.launchpad.backintime.serviceHelper.conf\n" >> ${MAKEFILE} printf "\trm -f \$(DEST)/share/backintime/plugins/qt4plugin.py\n" >> ${MAKEFILE} addNewline printf "\t# Inject version string into source files\n" >> ${MAKEFILE} printf "\t(cd .. && ./updateversion.sh)\n" >> ${MAKEFILE} addNewline addComment "python" addUninstallDir "/share/backintime/qt/__pycache__" addUninstallFile "*.pyc" "/share/backintime/qt/__pycache__" addInstallDir "/share/backintime/qt" addInstallFiles "*.py" "/share/backintime/qt" addNewline addComment "plugin" addUninstallDir "/share/backintime/plugins/__pycache__" addUninstallFile "*.pyc" "/share/backintime/plugins/__pycache__" addInstallDir "/share/backintime/plugins" addInstallFiles "plugins/*.py" "/share/backintime/plugins" addUninstallDir "/share/backintime" addNewline addComment "application" addInstallDir "/bin" addInstallFile "backintime-qt" "/bin" "755" addInstallFile "backintime-qt_polkit" "/bin" "755" addInstallDir "/share/metainfo" addInstallFile "io.github.bit_team.back_in_time.gui.metainfo.xml" "/share/metainfo" addNewline addComment "dbus service" addInstallDir "/share/dbus-1/system-services" addInstallFiles "net.launchpad.backintime*.service" "/share/dbus-1/system-services" addUninstallDir "/share/dbus-1" addNewline addComment "dbus conf" addInstallDir "/share/dbus-1/system.d" addInstallFiles "net.launchpad.backintime*.conf" "/share/dbus-1/system.d" addUninstallDir "/share/dbus-1" addUninstallDir "/share" addNewline addComment "polkit action" addInstallDir "/share/polkit-1/actions" addInstallFiles "net.launchpad.backintime*.policy" "/share/polkit-1/actions" addUninstallDir "/share/polkit-1" addNewline addComment "documentation" addInstallDir "/share/doc/backintime-qt" addInstallFile "../AUTHORS" "/share/doc/backintime-qt" addInstallFile "../LICENSE" "/share/doc/backintime-qt" addInstallFile "../README.md" "/share/doc/backintime-qt" addInstallFile "../TRANSLATIONS" "/share/doc/backintime-qt" addInstallFile "../CHANGES" "/share/doc/backintime-qt" addNewline addComment ".desktop" addInstallDir "/share/applications" addInstallFiles "*.desktop" "/share/applications" addNewline addComment "man" addInstallDir "/share/man/man1" addInstallFile "man/C/backintime-qt.1.gz" "/share/man/man1" addUninstallDir "/share/man" addNewline addComment "icons" for f in "scalable" "48x48" "32x32" "24x24" "22x22" "16x16"; do addInstallDir "/share/icons/hicolor/${f}/actions" addInstallFile "icons/${f}/actions/show-hidden.svg" "/share/icons/hicolor/${f}/actions" addUninstallDir "/share/icons/hicolor/${f}" done addUninstallDir "/share/icons/hicolor" addUninstallDir "/share/icons" addUninstallDir "/share" addNewline #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} # Uninstall printf "uninstall: uninstall_files uninstall_dirs\n\n" >> ${MAKEFILE} printf "uninstall_files:\n" >> ${MAKEFILE} cat ${UNINSTALL_FILES} >> ${MAKEFILE} printf "uninstall_dirs:\n" >> ${MAKEFILE} cat ${UNINSTALL_DIRS} >> ${MAKEFILE} #copy Makefile mv ${MAKEFILE} Makefile chmod 644 Makefile #clean up for i in "${UNINSTALL_FILES}" "${UNINSTALL_DIRS}"; do if [ -e "$i" ]; then rm "$i" fi done # check python version PYTHON_VERSION_REQUIRED="3.8" 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" backintime-1.5.2/qt/encfsmsgbox.py000066400000000000000000000070231465446530500171570ustar00rootroot00000000000000# SPDX-FileCopyrightText: © 2024 Christian BUHTZ # # SPDX-License-Identifier: GPL-2.0 # # This file is part of the program "Back In time" which is released under GNU # General Public License v2 (GPLv2). # See file LICENSE or go to . """Message box warning about EncFS deprecation. See #1734 and #1735 for details """ from PyQt6.QtGui import QCursor from PyQt6.QtWidgets import QLabel, QToolTip, QMessageBox from bitbase import URL_ENCRYPT_TRANSITION class _EncfsWarningBase(QMessageBox): """Base clase for Warning boxes in context of EncFS decprecation. """ def __init__(self, parent, text, informative_text): super().__init__(parent) self.setWindowTitle(_('Warning')) self.setIcon(QMessageBox.Icon.Warning) self.setText(text) self.setInformativeText(informative_text) # Set link tooltips (via hovering) on the QLabels for label in self.findChildren(QLabel): label.linkHovered.connect( lambda url: QToolTip.showText( QCursor.pos(), url.replace('https://', ''))) class EncfsCreateWarning(_EncfsWarningBase): """Warning box when using EncFS encrypting while creating a new profile or modify an existing one. """ def __init__(self, parent): text = _('Support for EncFS will be discontinued in the ' 'foreseeable future. It is not recommended to use that ' 'mode for a profile furthermore.') informative_text = _( 'A decision on a replacement for continued support of encrypted ' 'backups is still pending, depending on project resources and ' 'contributor availability. More details are available in this ' '{whitepaper}.' ).format(whitepaper='{}'.format( URL_ENCRYPT_TRANSITION, _('whitepaper'))) super().__init__(parent, text, informative_text) class EncfsExistsWarning(_EncfsWarningBase): """Warning box when encrypted profiles exists. """ def __init__(self, parent, profiles): # DevNote: Code looks ugly because we need to take the needs of # translators into account. Also the limitations of Qt's RichText # feature need to be considered. text = _('The support for encrypted snapshot profiles is undergoing ' 'significant changes, and EncFS will be removed in the ' 'foreseeable future.') profiles = '
    ' \ + ''.join(f'
  • {profile}
  • ' for profile in profiles) \ + '
' info_paragraphs = ( _('The following profile(s) use encryption with EncFS:'), profiles, _('A decision on a replacement for continued support of encrypted ' 'backups is still pending, depending on project resources and ' 'contributor availability. Users are invited to join this ' 'discussion. Updated details on the next steps are ' 'available in this {whitepaper}.').format( whitepaper='{}'.format( URL_ENCRYPT_TRANSITION, _('whitepaper'))), _('This message will not be shown again. This dialog is ' 'available at any time via the help menu.'), _('Your Back In Time Team') ) informative_text = ''.join( [f'

{par}

' for par in info_paragraphs]) super().__init__(parent, text, informative_text) backintime-1.5.2/qt/icon.py000066400000000000000000000134331465446530500155730ustar00rootroot00000000000000# Copyright (C) 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. # # 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 PyQt6.QtGui import QIcon import logger logger.debug("Checking if the current theme contains the BiT icon...") # If the current theme does not even contain the "document-save" icon # try to use another well-known working theme (if it is installed): for theme in ('ubuntu-mono-dark', 'gnome', 'breeze', 'breeze dark', 'hicolor', 'adwaita', 'adwaita-dark', 'yaru', 'oxygen'): # Check if the current theme does provide the BiT "logo" icon # (otherwise the theme is not fully/correctly installed) # and use this theme then for all icons # Note: "hicolor" does currently (2022) use different icon names # (not fully compliant to the freedesktop.org spec) # and is not recommended as main theme (it is meant as fallback only). if not QIcon.fromTheme('document-save').isNull(): logger.debug(f"Found an installed theme: {QIcon.themeName()}") break # try next theme (activate it)... QIcon.setThemeName(theme) logger.debug(f"Probing theme: {theme} (activated as {QIcon.themeName()})") if QIcon.fromTheme('document-save').isNull(): logger.error("No supported theme installed (missing icons). " "Please consult the project web site for instructions " "how to fix this.") # Dev note: Please prefer choosing icons from the freedesktop.org spec # to improve the chance that the icon is available and # each installed theme: # https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html # # If there is chance that an icon may not always be available use # the second argument of QIcon.fromTheme() to provide a fallback # icon from the freedesktop.org spec. # BackInTime Logo # TODO If we knew for sure that the global var "qapp" exists then # we could use a built-in "standard" Qt icon as fallback if the theme does # not provide the icon. # => wait for icon.py refactoring than improve this: # qapp.style().standardIcon(QStyle.SP_DialogSaveButton) BIT_LOGO = QIcon.fromTheme('document-save') BIT_LOGO_INFO = QIcon.fromTheme('document-save-as') #Main toolbar TAKE_SNAPSHOT = BIT_LOGO PAUSE = QIcon.fromTheme('media-playback-pause') RESUME = QIcon.fromTheme('media-playback-start') STOP = QIcon.fromTheme('media-playback-stop') REFRESH_SNAPSHOT = QIcon.fromTheme('view-refresh') SNAPSHOT_NAME = QIcon.fromTheme('stock_edit', QIcon.fromTheme('gtk-edit', QIcon.fromTheme('edit-rename', QIcon.fromTheme('accessories-text-editor')))) REMOVE_SNAPSHOT = QIcon.fromTheme('edit-delete') VIEW_SNAPSHOT_LOG = QIcon.fromTheme('text-plain', QIcon.fromTheme('text-x-generic')) VIEW_LAST_LOG = QIcon.fromTheme('document-open-recent') # 'document-open-recent') # ('document-new') SETTINGS = QIcon.fromTheme('gtk-preferences', QIcon.fromTheme('configure')) SHUTDOWN = QIcon.fromTheme('system-shutdown') EXIT = QIcon.fromTheme('gtk-close', QIcon.fromTheme('application-exit')) #Help menu HELP = QIcon.fromTheme('help-contents') WEBSITE = QIcon.fromTheme('go-home') CHANGELOG = QIcon.fromTheme('format-justify-fill') FAQ = QIcon.fromTheme('help-faq', QIcon.fromTheme('help-hint')) QUESTION = QIcon.fromTheme('stock_dialog-question', QIcon.fromTheme('help-feedback')) BUG = QIcon.fromTheme('stock_dialog-error', QIcon.fromTheme('tools-report-bug')) ABOUT = QIcon.fromTheme('help-about') #Files toolbar UP = QIcon.fromTheme('go-up') SHOW_HIDDEN = QIcon.fromTheme('view-hidden', # currently only in Breeze (see #1159) QIcon.fromTheme('show-hidden', # icon installed with BiT! QIcon.fromTheme('list-add'))) RESTORE = QIcon.fromTheme('edit-undo') RESTORE_TO = QIcon.fromTheme('document-revert') SNAPSHOTS = QIcon.fromTheme('file-manager', QIcon.fromTheme('view-list-details', QIcon.fromTheme('system-file-manager'))) #Snapshot dialog DIFF_OPTIONS = SETTINGS DELETE_FILE = REMOVE_SNAPSHOT SELECT_ALL = QIcon.fromTheme('edit-select-all') #Restore dialog RESTORE_DIALOG = VIEW_SNAPSHOT_LOG #Settings dialog SETTINGS_DIALOG = SETTINGS PROFILE_EDIT = SNAPSHOT_NAME ADD = QIcon.fromTheme('list-add') REMOVE = QIcon.fromTheme('list-remove') FOLDER = QIcon.fromTheme('folder') FILE = VIEW_SNAPSHOT_LOG EXCLUDE = REMOVE_SNAPSHOT DEFAULT_EXCLUDE = QIcon.fromTheme('emblem-important') INVALID_EXCLUDE = QIcon.fromTheme('emblem-ohno', QIcon.fromTheme('face-surprise')) ENCRYPT = QIcon.fromTheme('lock', QIcon.fromTheme('security-high')) LANGUAGE = QIcon.fromTheme('preferences-desktop-locale') backintime-1.5.2/qt/icons/000077500000000000000000000000001465446530500154005ustar00rootroot00000000000000backintime-1.5.2/qt/icons/16x16/000077500000000000000000000000001465446530500161655ustar00rootroot00000000000000backintime-1.5.2/qt/icons/16x16/actions/000077500000000000000000000000001465446530500176255ustar00rootroot00000000000000backintime-1.5.2/qt/icons/16x16/actions/show-hidden.svg000066400000000000000000000333671465446530500225730ustar00rootroot00000000000000 show-hidden image/svg+xml show-hidden 2015-12-22 Germar Reitze eye Icon for showing hidden files in BackInTime https://github.com/bit-team/backintime backintime-1.5.2/qt/icons/22x22/000077500000000000000000000000001465446530500161575ustar00rootroot00000000000000backintime-1.5.2/qt/icons/22x22/actions/000077500000000000000000000000001465446530500176175ustar00rootroot00000000000000backintime-1.5.2/qt/icons/22x22/actions/show-hidden.svg000066400000000000000000000334541465446530500225620ustar00rootroot00000000000000 show-hidden image/svg+xml show-hidden 2015-12-22 Germar Reitze eye Icon for showing hidden files in BackInTime https://github.com/bit-team/backintime backintime-1.5.2/qt/icons/24x24/000077500000000000000000000000001465446530500161635ustar00rootroot00000000000000backintime-1.5.2/qt/icons/24x24/actions/000077500000000000000000000000001465446530500176235ustar00rootroot00000000000000backintime-1.5.2/qt/icons/24x24/actions/show-hidden.svg000066400000000000000000000334501465446530500225620ustar00rootroot00000000000000 show-hidden image/svg+xml show-hidden 2015-12-22 Germar Reitze eye Icon for showing hidden files in BackInTime https://github.com/bit-team/backintime backintime-1.5.2/qt/icons/32x32/000077500000000000000000000000001465446530500161615ustar00rootroot00000000000000backintime-1.5.2/qt/icons/32x32/actions/000077500000000000000000000000001465446530500176215ustar00rootroot00000000000000backintime-1.5.2/qt/icons/32x32/actions/show-hidden.svg000066400000000000000000000333751465446530500225660ustar00rootroot00000000000000 show-hidden image/svg+xml show-hidden 2015-12-22 Germar Reitze eye Icon for showing hidden files in BackInTime https://github.com/bit-team/backintime backintime-1.5.2/qt/icons/48x48/000077500000000000000000000000001465446530500161775ustar00rootroot00000000000000backintime-1.5.2/qt/icons/48x48/actions/000077500000000000000000000000001465446530500176375ustar00rootroot00000000000000backintime-1.5.2/qt/icons/48x48/actions/show-hidden.svg000066400000000000000000000333241465446530500225760ustar00rootroot00000000000000 show-hidden image/svg+xml show-hidden 2015-12-22 Germar Reitze eye Icon for showing hidden files in BackInTime https://github.com/bit-team/backintime backintime-1.5.2/qt/icons/scalable/000077500000000000000000000000001465446530500171465ustar00rootroot00000000000000backintime-1.5.2/qt/icons/scalable/actions/000077500000000000000000000000001465446530500206065ustar00rootroot00000000000000backintime-1.5.2/qt/icons/scalable/actions/show-hidden.svg000066400000000000000000000333241465446530500235450ustar00rootroot00000000000000 show-hidden image/svg+xml show-hidden 2015-12-22 Germar Reitze eye Icon for showing hidden files in BackInTime https://github.com/bit-team/backintime backintime-1.5.2/qt/io.github.bit_team.back_in_time.gui.metainfo.xml000066400000000000000000000064311465446530500254550ustar00rootroot00000000000000 io.github.bit_team.back_in_time.gui Back In Time Backup tool for GNU Linux desktop using rsync with its hard-links feature to save storage space FSFAP GPL-2.0

It is an easy-to-use backup tool for files and folders. It runs on GNU Linux and provides a command line tool "backintime" and a Qt5 GUI "backintime-qt". It uses "rsync" in the back to take manual or scheduled snapshots and stores them locally or remotely through SSH. Each snapshot is in its own folder with copies of the original files, but unchanged files are hard-linked between snapshots to save space. It was inspired by "FlyBack".

You only need to specify 3 things:

  1. What folders to back up.
  2. Where to save snapshots.
  3. The backup frequency (manual, every hour, every day, every month).
document-save System Archiving Backup Rsync GUI https://github.com/bit-team/backintime https://github.com/bit-team/backintime/issues https://github.com/bit-team/backintime/blob/dev/FAQ.md https://backintime.readthedocs.io/ https://translate.codeberg.org/engage/backintime https://mail.python.org/mailman3/lists/bit-dev.python.org https://github.com/bit-team/backintime https://github.com/bit-team/backintime/blob/dev/CONTRIBUTING.md backintime-qt.desktop backintime-qt-root.desktop Main window https://translate.codeberg.org/media/screenshots/bit_16to9_960x540_mainwindow.png Setup of SSH snapshot profile https://translate.codeberg.org/media/screenshots/bit_16to9_1280x720_general_ssh.png bit-dev@python.org
backintime-1.5.2/qt/languagedialog.py000066400000000000000000000227041465446530500176070ustar00rootroot00000000000000"""Dialog window to choose GUI display language. """ from PyQt6.QtCore import Qt, QSize, QTimer from PyQt6.QtGui import QCursor from PyQt6.QtWidgets import (QApplication, QDialog, QWidget, QScrollArea, QGridLayout, QLayout, QVBoxLayout, QDialogButtonBox, QRadioButton, QLabel, QToolTip, ) import tools import qttools import languages class LanguageDialog(QDialog): """Dialog to choose GUI language.""" def __init__(self, used_language_code: str, configured_language_code: str): super().__init__() self.used_language_code = used_language_code self.configured_language_code = configured_language_code self.setWindowTitle(_('Setup language')) self.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, True) scroll = QScrollArea(self) scroll.setVerticalScrollBarPolicy( Qt.ScrollBarPolicy.ScrollBarAsNeeded) scroll.setHorizontalScrollBarPolicy( Qt.ScrollBarPolicy.ScrollBarAlwaysOff) scroll.setWidget(self._language_widget()) self._scroll = scroll # Fit the width of scrollarea to its content new_width = self._calculate_scroll_area_width() self._scroll.setMinimumWidth(new_width) buttonbox = QDialogButtonBox( QDialogButtonBox.StandardButton.Cancel | QDialogButtonBox.StandardButton.Ok, self) buttonbox.accepted.connect(self.accept) buttonbox.rejected.connect(self.reject) layout = QVBoxLayout(self) layout.addWidget(scroll) layout.addWidget(buttonbox) def _calculate_scroll_area_width(self): """Credits: - https://stackoverflow.com/a/9081579/4865723 - https://stackoverflow.com/a/76738806/4865723 """ widget_width = self._scroll.widget().sizeHint().width() scrollbar_width = self._scroll.verticalScrollBar().sizeHint().width() return widget_width + scrollbar_width def _create_radio_button(self, lang_code, label, tooltip) -> QRadioButton: r = QRadioButton(label, self) r.setToolTip(tooltip) r.toggled.connect(self.slot_radio) r.lang_code = lang_code # Is it the current used AND configured language? if (r.lang_code == self.used_language_code and r.lang_code == self.configured_language_code): r.setChecked(True) # "System default" elif self.configured_language_code == '' and r.lang_code is None: r.setChecked(True) return r def _language_widget(self): grid = QGridLayout() widget = QWidget(self) widget.setLayout(grid) # Entry: System default language label = 'System default' translated_label = _('System default') # If translation for that term exists... if label != translated_label: # ...combine source and translated version. label = f'{label}\n{translated_label}' tooltip = _('Use operating systems language.') code = None r = self._create_radio_button(code, label, tooltip) grid.addWidget(r, 1, 1) # Sort by language code but keep English on top langs = tools.get_language_names(self.used_language_code) sorted_codes = sorted(langs.keys()) sorted_codes.remove('en') sorted_codes = ['en'] + sorted_codes # Number of columns used for radio buttons number_of_columns = 3 # Low-resolution screens (XGA or less) if QApplication.primaryScreen().size().width() <= 1024: # Use one columns less number_of_columns -= 1 # Calculate number of entries (rows) per column per_col_n = len(sorted_codes) / number_of_columns per_col_n = int(per_col_n) + 1 col = 1 for idx, code in enumerate(sorted_codes, 2): names = langs[code] try: label = names[0] except TypeError: # Happens when no name for the language codes is available. # "names" is "None" in that case. label = code tooltip = f'Language code "{code}" unknown.' else: # Add language name in its native representation # if Native letters available in current font # but prevent duplications like e.g. "Deutsch\nDeutsch" if label != names[1] and qttools.can_render(names[1], widget): label = f'{names[1]}\n{label}' # Tooltip: Language code tooltip = f'{names[2]} ({code})' # Tooltip: completeness of translation try: complete = languages.completeness[code] except KeyError: pass else: tooltip = tooltip + '\n' \ + _('Translated: {percent}').format( percent=f'{complete}%') # Create button r = self._create_radio_button(code, label, tooltip) # Calculate buttons location row = idx - ((col - 1) * per_col_n) if row > per_col_n: row = 1 col = col + 1 # Add the button grid.addWidget(r, row, col) return widget def slot_radio(self, _): """Radio button state toggled.""" btn = self.sender() if btn.isChecked(): self.language_code = btn.lang_code class ApproachTranslatorDialog(QDialog): """Present a message to the users to motivate them contributing to the translation of Back In Time. """ # ToDo (2023-08): Move to packages meta-data (pyproject.toml). _URL_PLATFORM = 'https://translate.codeberg.org/engage/backintime' _URL_PROJECT = 'https://github.com/bit-team/backintime' @classmethod def _complete_text(cls, language, percent): txt = _( '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' ) # Wrap paragraphs in

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

{t}

' # Insert data in placeholder variables. platform_url \ = f'' \ + _('translation platform') \ + '' project_url \ = f'Back In Time ' \ + _('Website') \ + ' ' result = result.format( language=f'{language}', perc=f'{percent} %', translation_platform_url=platform_url, back_in_time_project_website=project_url ) return result def __init__(self, parent, language_name, completeness): super().__init__(parent) # screen_width = QApplication.primaryScreen().size().width() # min_width = 300 if screen_width <= 1080 else 450 self.setMinimumWidth(400) # Note: Take into account that not only the self.setWindowTitle(_('Your translation')) self.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, True) txt = self._complete_text(language_name, completeness) widget = QLabel(txt, 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) def resizeEvent(self, event): """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://', '')) backintime-1.5.2/qt/logviewdialog.py000066400000000000000000000233361465446530500175020ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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. from PyQt6.QtGui import QFont from PyQt6.QtWidgets import (QDialog, QLabel, QPlainTextEdit, QVBoxLayout, QHBoxLayout, QComboBox, QDialogButtonBox, QCheckBox, ) from PyQt6.QtCore import QFileSystemWatcher import qttools import snapshots import encfstools import snapshotlog import tools class LogViewDialog(QDialog): def __init__(self, parent, sid=None, systray=False): """ Instantiate a snapshot log file viewer Args: parent: sid (:py:class:`SID`): snapshot ID whose log file shall be shown (``None`` = show last log) systray (bool): TODO Show log from systray icon or from App (boolean) """ if systray: super(LogViewDialog, self).__init__() else: super(LogViewDialog, self).__init__(parent) self.config = parent.config self.snapshots = parent.snapshots self.mainWindow = parent self.sid = sid self.enableUpdate = False self.decode = None w = self.config.intValue('qt.logview.width', 800) h = self.config.intValue('qt.logview.height', 500) self.resize(w, h) import icon self.setWindowIcon(icon.VIEW_SNAPSHOT_LOG) if self.sid is None: self.setWindowTitle(_('Last Log View')) else: self.setWindowTitle(_('Snapshot Log View')) self.mainLayout = QVBoxLayout(self) layout = QHBoxLayout() self.mainLayout.addLayout(layout) # profiles self.lblProfile = QLabel(_('Profile:'), self) layout.addWidget(self.lblProfile) self.comboProfiles = qttools.ProfileCombo(self) layout.addWidget(self.comboProfiles, 1) self.comboProfiles.currentIndexChanged.connect(self.profileChanged) # snapshots self.lblSnapshots = QLabel(_('Snapshots:'), self) layout.addWidget(self.lblSnapshots) self.comboSnapshots = qttools.SnapshotCombo(self) layout.addWidget(self.comboSnapshots, 1) self.comboSnapshots.currentIndexChanged.connect(self.comboSnapshotsChanged) if self.sid is None: self.lblSnapshots.hide() self.comboSnapshots.hide() if self.sid or systray: self.lblProfile.hide() self.comboProfiles.hide() # filter layout.addWidget(QLabel(_('Filter:'), self)) self.comboFilter = QComboBox(self) layout.addWidget(self.comboFilter, 1) self.comboFilter.currentIndexChanged.connect(self.comboFilterChanged) self.comboFilter.addItem(_('All'), snapshotlog.LogFilter.NO_FILTER) # Note about ngettext plural forms: n=102 means "Other" in Arabic and # "Few" in Polish. # Research in translation community indicate this as the best fit to # the meaning of "all". self.comboFilter.addItem(' + '.join((_('Errors'), _('Changes'))), snapshotlog.LogFilter.ERROR_AND_CHANGES) self.comboFilter.setCurrentIndex(self.comboFilter.count() - 1) self.comboFilter.addItem(_('Errors'), snapshotlog.LogFilter.ERROR) self.comboFilter.addItem(_('Changes'), snapshotlog.LogFilter.CHANGES) self.comboFilter.addItem(ngettext('Information', 'Information', 2), snapshotlog.LogFilter.INFORMATION) self.comboFilter.addItem(_('rsync transfer failures (experimental)'), snapshotlog.LogFilter.RSYNC_TRANSFER_FAILURES) # text view self.txtLogView = QPlainTextEdit(self) self.txtLogView.setFont(QFont('Monospace')) self.txtLogView.setReadOnly(True) self.txtLogView.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap) self.mainLayout.addWidget(self.txtLogView) # self.mainLayout.addWidget( QLabel(_('[E] Error, [I] Information, [C] Change'))) # decode path self.cbDecode = QCheckBox(_('decode paths'), self) self.cbDecode.stateChanged.connect(self.cbDecodeChanged) self.mainLayout.addWidget(self.cbDecode) # buttons buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Close) self.mainLayout.addWidget(buttonBox) buttonBox.rejected.connect(self.close) self.updateSnapshots() self.updateDecode() self.updateProfiles() # watch for changes in log file self.watcher = QFileSystemWatcher(self) if self.sid is None: # only watch if we show the last log log = self.config.takeSnapshotLogFile( self.comboProfiles.currentProfileID()) self.watcher.addPath(log) # passes the path to the changed file to updateLog() self.watcher.fileChanged.connect(self.updateLog) def cbDecodeChanged(self): if self.cbDecode.isChecked(): if not self.decode: self.decode = encfstools.Decode(self.config) else: if self.decode is not None: self.decode.close() self.decode = None self.updateLog() def profileChanged(self, index): if not self.enableUpdate: return profile_id = self.comboProfiles.currentProfileID() self.mainWindow.comboProfiles.setCurrentProfileID(profile_id) self.mainWindow.comboProfileChanged(None) self.updateDecode() self.updateLog() def comboSnapshotsChanged(self, index): if not self.enableUpdate: return self.sid = self.comboSnapshots.currentSnapshotID() self.updateLog() def comboFilterChanged(self, index): self.updateLog() def updateProfiles(self): current_profile_id = self.config.currentProfile() 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.enableUpdate = True self.updateLog() if len(profiles) <= 1: self.lblProfile.setVisible(False) self.comboProfiles.setVisible(False) def updateSnapshots(self): if self.sid: self.comboSnapshots.clear() for sid in snapshots.iterSnapshots(self.config): self.comboSnapshots.addSnapshotID(sid) if sid == self.sid: self.comboSnapshots.setCurrentSnapshotID(sid) def updateDecode(self): if self.config.snapshotsMode() == 'ssh_encfs': self.cbDecode.show() else: self.cbDecode.hide() if self.cbDecode.isChecked(): self.cbDecode.setChecked(False) def updateLog(self, watchPath=None): """ Show the log file of the current snapshot in the GUI Args: watchPath: FQN to a log file (as string) whose changes are watched via ``QFileSystemWatcher``. In case of changes this function is called with the log file and only the new lines in the log file are appended to the log file widget in the GUI Use ``None`` if a complete log file shall be shown at once. """ if not self.enableUpdate: return mode = self.comboFilter.itemData(self.comboFilter.currentIndex()) # TODO This expressions is hard to understand (watchPath is not a boolean!) if watchPath and self.sid is None: # remove path from watch to prevent multiple updates at the same time self.watcher.removePath(watchPath) # append only new lines to txtLogView log = snapshotlog.SnapshotLog(self.config, self.comboProfiles.currentProfileID()) for line in log.get(mode = mode, decode = self.decode, skipLines = self.txtLogView.document().lineCount() - 1): self.txtLogView.appendPlainText(line) # re-add path to watch after 5sec delay alarm = tools.Alarm(callback = lambda: self.watcher.addPath(watchPath), overwrite = False) alarm.start(5) elif self.sid is None: log = snapshotlog.SnapshotLog(self.config, self.comboProfiles.currentProfileID()) self.txtLogView.setPlainText('\n'.join(log.get(mode = mode, decode = self.decode))) else: self.txtLogView.setPlainText('\n'.join(self.sid.log(mode, decode = self.decode))) def closeEvent(self, event): self.config.setIntValue('qt.logview.width', self.width()) self.config.setIntValue('qt.logview.height', self.height()) event.accept() backintime-1.5.2/qt/man/000077500000000000000000000000001465446530500150405ustar00rootroot00000000000000backintime-1.5.2/qt/man/C/000077500000000000000000000000001465446530500152225ustar00rootroot00000000000000backintime-1.5.2/qt/man/C/backintime-qt.1000066400000000000000000000115731465446530500200430ustar00rootroot00000000000000.TH backintime-qt 1 "August 2024" "version 1.5.2" "USER COMMANDS" .SH NAME backintime-qt \- a simple backup tool. .SH SYNOPSIS .B backintime-qt [\-\-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. This is the Qt version. For more information about Back In Time see backintime man page. .PP If you want to run it as root you need to use 'backintime-qt_polkit'. .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 characters 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 characters 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 SEE ALSO .BR backintime-qt (1), .BR backintime-config (1), .BR backintime-askpass (1) .PP \fBBack In Time\fP project website: https://github.com/bit-team/backintime .PP \fBBack In Time\fP mailing list: https://mail.python.org/mailman3/lists/bit-dev.python.org .SH AUTHOR \fBBack In Time\fP Team backintime-1.5.2/qt/messagebox.py000066400000000000000000000104151465446530500167750ustar00rootroot00000000000000# Copyright (C) 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. # # 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 PyQt6.QtCore import QTimer, Qt from PyQt6.QtWidgets import QApplication, QMessageBox, QInputDialog, QLineEdit,\ QDialog, QVBoxLayout, QLabel, QDialogButtonBox, QScrollArea import qttools def askPasswordDialog(parent, title, prompt, language_code, timeout): if parent is None: app = qttools.createQApplication() translator = qttools.initiate_translator(language_code) app.installTranslator(translator) import icon dialog = QInputDialog() timer = QTimer() if not timeout is None: timer.timeout.connect(dialog.reject) timer.setInterval(timeout * 1000) timer.start() dialog.setWindowIcon(icon.BIT_LOGO) dialog.setWindowTitle(title) dialog.setLabelText(prompt) dialog.setTextEchoMode(QLineEdit.EchoMode.Password) QApplication.processEvents() ret = dialog.exec() timer.stop() if ret: password = dialog.textValue() else: password = '' 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. Returns: Nothing. """ QMessageBox.information( widget_to_center_on, title if title else ngettext('Information', 'Information', 1), text) def critical(parent, msg): return QMessageBox.critical( parent, _('Error'), msg, buttons=QMessageBox.StandardButton.Ok, defaultButton=QMessageBox.StandardButton.Ok) def warningYesNo(parent, msg): return QMessageBox.question( parent, _('Question'), msg, buttons=QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, defaultButton=QMessageBox.StandardButton.No) def warningYesNoOptions(parent, msg, options = ()): # Create a dialog dlg = QDialog(parent) dlg.setWindowTitle(_('Question')) layout = QVBoxLayout() dlg.setLayout(layout) # Initial message label = QLabel(msg) layout.addWidget(label) # Add optional elements for opt in options: layout.addWidget(opt['widget']) # Button box buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Yes | QDialogButtonBox.StandardButton.No) buttonBox.button(QDialogButtonBox.StandardButton.No).setDefault(True) layout.addWidget(buttonBox) buttonBox.accepted.connect(dlg.accept) buttonBox.rejected.connect(dlg.reject) # Show and ask user for the answer ret = dlg.exec() return ( ret, { opt['id']:opt['retFunc']() for opt in options if opt['retFunc'] is not None } ) def showInfo(parent, title, msg): """Show extended information dialog with framed and scrollable text area. """ dlg = QDialog(parent) dlg.setWindowTitle(title) vlayout = QVBoxLayout(dlg) label = QLabel(msg) label.setTextInteractionFlags( Qt.TextInteractionFlag.LinksAccessibleByMouse) label.setOpenExternalLinks(True) scroll_area = QScrollArea() scroll_area.setWidget(label) buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok) buttonBox.accepted.connect(dlg.accept) vlayout.addWidget(scroll_area) vlayout.addWidget(buttonBox) return dlg.exec() backintime-1.5.2/qt/net.launchpad.backintime.policy000066400000000000000000000035351465446530500223450ustar00rootroot00000000000000 BackInTime https://github.com/bit-team/backintime document-save Authentication is required to run Back In Time as root. Start Back In Time GUI as root. auth_admin auth_admin auth_admin_keep /usr/bin/backintime-qt true Authentication is required to add Udev rules. This will install Udev rules which will start Back In Time if a drive get connected. auth_admin auth_admin_keep auth_admin_keep Authentication is required to delete Udev rules. This will delete Udev rules. auth_admin auth_admin_keep auth_admin_keep backintime-1.5.2/qt/net.launchpad.backintime.serviceHelper.conf000066400000000000000000000016031465446530500245640ustar00rootroot00000000000000 system backintime-1.5.2/qt/net.launchpad.backintime.serviceHelper.service000066400000000000000000000002121465446530500252720ustar00rootroot00000000000000[D-BUS Service] Name=net.launchpad.backintime.serviceHelper Exec=/usr/bin/python3 -Es /usr/share/backintime/qt/serviceHelper.py User=root backintime-1.5.2/qt/plugins/000077500000000000000000000000001465446530500157465ustar00rootroot00000000000000backintime-1.5.2/qt/plugins/notifyplugin.py000066400000000000000000000036051465446530500210530ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 pluginmanager import subprocess class NotifyPlugin(pluginmanager.Plugin): def __init__(self): self.user = '' 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): 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 backintime-1.5.2/qt/plugins/systrayiconplugin.py000066400000000000000000000102111465446530500221210ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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. # Jan 23, 2023: This file was named "qt4plugin.py" until now # and renamed to better indicate the purpose # (and qt4 is no longer valid - we are using qt5 now for long). # The old class name "QtPlugin" was also renamed to: # SysTrayIconPlugin # TODO 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! import sys import os import pluginmanager import tools import logger import gettext import subprocess _ = gettext.gettext if not os.getenv('DISPLAY', ''): os.putenv('DISPLAY', ':0.0') class SysTrayIconPlugin(pluginmanager.Plugin): 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 except Exception as e: logger.debug(f"Could not ask Qt if system tray is available: {repr(e)}") logger.debug("No system tray available to show the BiT system tray icon") return False def isGui(self): return True def processBegin(self): try: logger.debug("Trying to start systray icon sub process...") path = os.path.join(tools.backintimePath('qt'), 'qtsystrayicon.py') cmd = [sys.executable, path, self.snapshots.config.currentProfile()] if logger.DEBUG: cmd.append("--debug") # HACK to propagate DEBUG logging level to sub process self.process = subprocess.Popen(cmd) # self.process = subprocess.Popen([sys.executable, path, self.snapshots.config.currentProfile()]) except: pass def processEnd(self): if not self.process is 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 backintime-1.5.2/qt/qtsystrayicon.py000066400000000000000000000214211465446530500175730ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 subprocess import signal import textwrap # 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 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 QRegion class QtSysTrayIcon: def __init__(self): self.snapshots = snapshots.Snapshots() self.config = self.snapshots.config self.decode = None 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) self.qapp = qttools.createQApplication(self.config.APP_NAME) translator = qttools.initiate_translator(self.config.language()) self.qapp.installTranslator(translator) self.qapp.setQuitOnLastWindowClosed(False) import icon self.icon = icon # What does this code do? Make the import accessible? self.qapp.setWindowIcon(icon.BIT_LOGO) self.status_icon = QSystemTrayIcon(icon.BIT_LOGO) #self.status_icon.actionCollection().clear() self.contextMenu = QMenu() self.menuProfileName = self.contextMenu.addAction( _('Profile: {profile_name}').format(profile_name=self.config.profileName())) qttools.setFontBold(self.menuProfileName) 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.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.setVisible(False) self.btnStop = self.contextMenu.addAction(icon.STOP, _('Stop snapshot process')) self.btnStop.triggered.connect(self.onBtnStop) self.contextMenu.addSeparator() 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.toggled.connect(self.onBtnDecode) 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 {appname}').format(appname=self.config.APP_NAME) ) self.startBIT.triggered.connect(self.onStartBIT) 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.RenderFlag.DrawChildren ) self.first_error = self.config.notify() self.popup = None self.last_message = None self.timer = QTimer() self.timer.timeout.connect(self.updateInfo) def prepareExit(self): self.timer.stop() if not self.status_icon is None: self.status_icon.hide() self.status_icon = None if not self.popup is None: self.popup.deleteLater() self.popup = None self.qapp.processEvents() def run(self): 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.prepareExit() def updateInfo(self): # Exit this systray icon "app" when the snapshots is taken 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) message = self.snapshots.takeSnapshotMessage() if message is None and self.last_message is None: 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(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') ## 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.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: value = pg.strValue(key, '') if not value: continue yield txt + ' ' + value def onStartBIT(self): 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): if checked: self.decode = encfstools.Decode(self.config) self.last_message = None self.updateInfo() else: self.decode = None def onBtnStop(self): 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') if __name__ == '__main__': logger.openlog() if "--debug" in sys.argv: # HACK: Minimal arg parsing to enable debug-level logging logger.DEBUG = True logger.debug("Sub process tries to show systray icon...") logger.debug(f"qtsystrayicon.py call args: {str(sys.argv)}") QtSysTrayIcon().run() backintime-1.5.2/qt/qttools.py000066400000000000000000000527201465446530500163520ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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. """Some helper functions and additional classes in context of Qt. - Helpers for Qt Fonts. - Helpers about path manipulation. - FiledialogShowHidden - MyTreeView (used RestoreConfigDialog) - TimeLine (might be the snapshot list in the left part of the GUI) - TimeLineItem, SnapshotItem, HeaderItem - SortedcomboBox, SnapshotCombo, ProfileCombo - Menu (tooltips in menus) """ import os import sys import textwrap from typing import Union, Iterable from PyQt6.QtGui import (QAction, QFont, QPalette, QIcon) from PyQt6.QtCore import (QDir, Qt, pyqtSlot, pyqtSignal, QModelIndex, QTranslator, QLocale, QLibraryInfo, QT_VERSION_STR) from PyQt6.QtWidgets import (QWidget, QFileDialog, QAbstractItemView, QListView, QTreeView, QDialog, QApplication, QStyleFactory, QTreeWidget, QTreeWidgetItem, QComboBox, QSystemTrayIcon) from datetime import (datetime, date, timedelta) from calendar import monthrange from packaging.version import Version from qttools_path import registerBackintimePath registerBackintimePath('common') import snapshots # noqa: E402 import tools # noqa: E402 import logger # noqa: E402 # |---------------| # | Font handling | # |---------------| def fontBold(font): font.setWeight(QFont.Weight.Bold) return font def setFontBold(widget): widget.setFont(fontBold(widget.font())) def fontNormal(font): font.setWeight(QFont.Weight.Normal) return font def setFontNormal(widget): widget.setFont(fontNormal(widget.font())) 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 # |--------------------------------| # | Widget modification & creation | # |--------------------------------| def set_wrapped_tooltip(widget: 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 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. """ # Always use tuple or list if isinstance(tooltip, str): tooltip = (tooltip, ) result = [] for paragraph in tooltip: result.append('\n'.join( textwrap.wrap(paragraph, wrap_length) )) widget.setToolTip('\n'.join(result)) # |---------------------| # | Misc / Uncatgorized | # |---------------------| 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): """File dialog able to display hidden files.""" def __init__(self, parent, *args, **kwargs): super(FileDialogShowHidden, self).__init__(parent, *args, **kwargs) self.setOption(QFileDialog.Option.DontUseNativeDialog, True) self.setOption(QFileDialog.Option.HideNameFilterDetails, True) showHiddenAction = QAction(self) showHiddenAction.setShortcut('Ctrl+H') showHiddenAction.triggered.connect(self.toggleShowHidden) self.addAction(showHiddenAction) self.showHidden(hiddenFiles(parent)) def showHidden(self, enable): if enable: self.setFilter(self.filter() | QDir.Filter.Hidden) elif self.filter() & QDir.Filter.Hidden: self.setFilter(self.filter() ^ QDir.Filter.Hidden) def toggleShowHidden(self): self.showHidden(not QDir.Filter(self.filter() & QDir.Filter.Hidden)) def getExistingDirectories(parent, *args, **kwargs): """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 """ dlg = FileDialogShowHidden(parent, *args, **kwargs) dlg.setFileMode(dlg.FileMode.Directory) dlg.setOption(dlg.Option.ShowDirsOnly, True) mode = QAbstractItemView.SelectionMode.ExtendedSelection dlg.findChildren(QListView)[0].setSelectionMode(mode) dlg.findChildren(QTreeView)[0].setSelectionMode(mode) if dlg.exec() == QDialog.DialogCode.Accepted: return dlg.selectedFiles() return [str(), ] def getExistingDirectory(parent, *args, **kwargs): """Workaround to give control about hidden folders""" dlg = FileDialogShowHidden(parent, *args, **kwargs) dlg.setFileMode(dlg.FileMode.Directory) dlg.setOption(dlg.Option.ShowDirsOnly, True) if dlg.exec() == QDialog.DialogCode.Accepted: return dlg.selectedFiles()[0] return str() def getOpenFileNames(parent, *args, **kwargs): """ Workaround to give control about hidden files """ dlg = FileDialogShowHidden(parent, *args, **kwargs) dlg.setFileMode(dlg.FileMode.ExistingFiles) if dlg.exec() == QDialog.DialogCode.Accepted: return dlg.selectedFiles() return [str(), ] def getOpenFileName(parent, *args, **kwargs): """Workaround to give control about hidden files""" dlg = FileDialogShowHidden(parent, *args, **kwargs) dlg.setFileMode(dlg.FileMode.ExistingFile) if dlg.exec() == QDialog.DialogCode.Accepted: return dlg.selectedFiles()[0] return str() def hiddenFiles(parent): try: return parent.parent.showHiddenFiles except Exception: pass try: return parent.showHiddenFiles except Exception: pass return False def createQApplication(app_name='Back In Time'): global qapp try: return qapp # "singleton pattern": Reuse already instantiated qapp except NameError: pass if (Version(QT_VERSION_STR) >= Version('5.6') and hasattr(Qt, 'AA_EnableHighDpiScaling')): QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) qapp = QApplication(sys.argv) qt_platform_name = "" try: # 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 the doc-dev # folder qt_platform_name = qapp.platformName() logger.debug(f"QT QPA platform plugin: {qt_platform_name}") logger.debug( "QT_QPA_PLATFORMTHEME=" f"{os.environ.get('QT_QPA_PLATFORMTHEME') or ''}") # styles and themes determine the look & feel of the GUI logger.debug( "QT_STYLE_OVERRIDE=" f"{os.environ.get('QT_STYLE_OVERRIDE') or ''}") logger.debug(f"QT active style: {qapp.style().objectName()}") logger.debug(f"QT fallback style: {QIcon.fallbackThemeName()}") logger.debug(f"QT supported styles: {QStyleFactory.keys()}") logger.debug(f"themeSearchPaths: {str(QIcon.themeSearchPaths())}") logger.debug( f"fallbackSearchPaths: {str(QIcon.fallbackSearchPaths())}") # The Back In Time system tray icon can only be shown if the desktop # environment supports this logger.debug("Is SystemTray available: " f"{str(QSystemTrayIcon.isSystemTrayAvailable())}") except Exception as e: logger.debug( f"Error reading QT QPA platform plugin or style: {repr(e)}") qapp.setApplicationName(app_name) try: if tools.isRoot(): qapp.setApplicationName(app_name + " (root)") logger.debug("Trying to set App ID for root user") qapp.setDesktopFileName("backintime-qt-root") else: logger.debug("Trying to set App ID for non-privileged user") qapp.setDesktopFileName("backintime-qt") except Exception as e: logger.warning( "Could not set App ID (required for Wayland App icon and more)") logger.warning("Reason: " + repr(e)) if (os.geteuid() == 0 and qapp.style().objectName().lower() == 'windows' and 'GTK+' in QStyleFactory.keys()): qapp.setStyle('GTK+') # With "--debug" arg show the QT QPA platform name in the main window's # title if logger.DEBUG: qapp.setApplicationName( f"{qapp.applicationName()} [{qt_platform_name}]") return qapp 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() 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 was not able to install a translator for language code ' f'"{language_code}". Deactivate translation and falling back to ' 'the source language (English).') tools.set_lc_time_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.EditTrigger.NoEditTriggers) self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) self.setHeaderLabels([_('Snapshots'), 'foo']) self.setSortingEnabled(True) self.sortByColumn(1, Qt.SortOrder.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 = [] # Today todayMin = datetime.combine(self.now, datetime.min.time()) todayMax = datetime.combine(self.now, datetime.max.time()) self.headerData.append((_('Today'), todayMin, todayMax)) # Yesterday 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)) # This week 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)) # Last week 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)) # 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)) # 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.ItemDataRole.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.ItemDataRole.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 {time}').format(time=sid.lastChecked)) def updateText(self): sid = self.snapshotID() self.setText(0, sid.displayName) class HeaderItem(TimeLineItem): def __init__(self, name, sid): """ Dev note (buhtz, 2024-01-14): Parts of that code are redundant with app.py::MainWindow.addPlace(). """ super(HeaderItem, self).__init__() self.setText(0, name) self.setFont(0, fontBold(self.font(0))) palette = QApplication.instance().palette() self.setForeground( 0, palette.color(QPalette.ColorRole.PlaceholderText)) self.setBackground( 0, palette.color(QPalette.ColorRole.Window)) self.setFlags(Qt.ItemFlag.NoItemFlags) self.setData(0, Qt.ItemDataRole.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.SortOrder.AscendingOrder self.sortRole = Qt.ItemDataRole.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.ItemDataRole.UserRole: sortObject = userData else: sortObject = text the_list = [ self.itemData(i, self.sortRole) for i in range(self.count())] the_list.append(sortObject) reverse_sort = self.sortOrder == Qt.SortOrder.DescendingOrder the_list.sort(reverse=reverse_sort) index = the_list.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.SortOrder.DescendingOrder self.sortRole = Qt.ItemDataRole.UserRole def addSnapshotID(self, sid): assert isinstance(sid, snapshots.SID), \ f'sid is not snapshots.SID type: {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 backintime-1.5.2/qt/qttools_path.py000066400000000000000000000026611465446530500173650ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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. """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 os import sys def backintimePath(*path): return os.path.abspath(os.path.join(__file__, os.pardir, os.pardir, *path)) def registerBackintimePath(*path): """Find duplicate in common/tools.py """ path = backintimePath(*path) if path not in sys.path: sys.path.insert(0, path) backintime-1.5.2/qt/restoredialog.py000066400000000000000000000106101465446530500175000ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 from PyQt6.QtGui import * from PyQt6.QtWidgets import * from PyQt6.QtCore import * import tools class RestoreDialog(QDialog): def __init__(self, parent, sid, what, where = '', **kwargs): super(RestoreDialog, self).__init__(parent) self.resize(600, 500) self.config = parent.config self.snapshots = parent.snapshots self.sid = sid 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) 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.LineWrapMode.NoWrap) self.txtLogView.setMaximumBlockCount(100000) self.mainLayout.addWidget(self.txtLogView) #buttons buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Close) showLog = buttonBox.addButton(_('Show full Log'), QDialogButtonBox.ButtonRole.ActionRole) self.mainLayout.addWidget(buttonBox) self.btnClose = buttonBox.button(QDialogButtonBox.StandardButton.Close) self.btnClose.setEnabled(False) buttonBox.rejected.connect(self.close) showLog.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(self.logFile))) #restore in separate thread self.thread = RestoreThread(self) self.thread.finished.connect(self.threadFinished) #refresh log every 200ms self.refreshTimer = QTimer(self) self.refreshTimer.setInterval(200) self.refreshTimer.setSingleShot(False) self.refreshTimer.timeout.connect(self.refreshLog) def refreshLog(self): """ get new log from thread """ newLog = self.thread.buffer[:] size = len(newLog) if size: self.thread.mutex.lock() self.thread.buffer = self.thread.buffer[size:] self.thread.mutex.unlock() self.txtLogView.appendPlainText(newLog.rstrip('\n')) def exec(self): #inhibit suspend/hibernate during restore self.config.inhibitCookie = tools.inhibitSuspend(toplevel_xid = self.config.xWindowId, reason = 'restoring') self.show() self.refreshTimer.start() self.thread.start() super(RestoreDialog, self).exec() self.refreshTimer.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) 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__() self.parent = parent self.log = open(parent.logFile, 'wt') 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) self.log.close() def callback(self, line, *args): """ write into log file and provide thread save string for log window """ line += '\n' self.log.write(line) self.mutex.lock() self.buffer += line self.mutex.unlock() backintime-1.5.2/qt/serviceHelper.py000066400000000000000000000325571465446530500174530ustar00rootroot00000000000000# (from BackInTime) # Copyright (C) 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. # # 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 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. # # 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. # # 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. # # 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/ import os import re from subprocess import Popen, PIPE try: import pwd except ImportError: pwd = None import dbus import dbus.service import dbus.mainloop # pylint: disable-next=import-error,useless-suppression import dbus.mainloop.pyqt6 # pylint: disable-next=import-error,useless-suppression from dbus.mainloop.pyqt6 import DBusQtMainLoop from PyQt6.QtCore import QCoreApplication UDEV_RULES_PATH = '/etc/udev/rules.d/99-backintime-%s.rules' class InvalidChar(dbus.DBusException): _dbus_error_name = 'net.launchpad.backintime.InvalidChar' class InvalidCmd(dbus.DBusException): _dbus_error_name = 'net.launchpad.backintime.InvalidCmd' class LimitExceeded(dbus.DBusException): _dbus_error_name = 'net.launchpad.backintime.LimitExceeded' class PermissionDeniedByPolicy(dbus.DBusException): _dbus_error_name = 'com.ubuntu.DeviceDriver.PermissionDeniedByPolicy' class UdevRules(dbus.service.Object): def __init__(self, conn=None, object_path=None, bus_name=None): super(UdevRules, self).__init__(conn, object_path, bus_name) # the following variables are used by _checkPolkitPrivilege self.polkit = None self.enforce_polkit = True 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 = 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() 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 only well known commands and switches are used whitelist = ( (self.nice, r'^-n'), (self.ionice, r'(^-c|^-n)'), ) while parts: for c, switches in whitelist: if parts[0] == c: parts.pop(0) while parts and re.match(switches, parts[0]): parts.pop(0) break else: break 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 " f"cmd/parameter ({parts[0]})") 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) @dbus.service.method("net.launchpad.backintime.serviceHelper.UdevRules", 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. This is done on the service side to prevent malicious code to run as root. """ # 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 chars = re.findall(r'[^a-zA-Z0-9-]', uuid) if chars: raise InvalidChar("Parameter 'uuid' contains invalid character(s) %s" % '|'.join(set(chars))) self._validateCmd(cmd) info = SenderInfo(sender, conn) user = info.connectionUnixUser() owner = info.nameOwner() 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) #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 destination file after user authenticated as admin. This will first check if there are any changes between 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 if not owner in self.tmpDict or not self.tmpDict[owner]: self.delete(sender, conn) return False #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') 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 """ 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') 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 """ info = SenderInfo(sender, conn) self._clean(info.nameOwner()) def _clean(self, owner): if owner in self.tmpDict: del self.tmpDict[owner] def _initPolkit(self): if self.polkit is None: self.polkit = dbus.Interface(dbus.SystemBus().get_object( 'org.freedesktop.PolicyKit1', '/org/freedesktop/PolicyKit1/Authority', False), 'org.freedesktop.PolicyKit1.Authority') def _checkPolkitPrivilege(self, sender, conn, privilege): # from jockey """ Verify that sender has a given PolicyKit privilege. sender is the sender's (private) D-BUS name, such as ":1:42" (sender_keyword in @dbus.service.methods). conn is the dbus.Connection object (connection_keyword in @dbus.service.methods). privilege is the PolicyKit privilege string. This method returns if the caller is privileged, and otherwise throws a PermissionDeniedByPolicy exception. """ if sender is None and conn is None: # called locally, not through D-BUS return if not self.enforce_polkit: # that happens for testing purposes when running on the session # bus, and it does not make sense to restrict operations here return # 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': # 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): 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') def connectionUnixUser(self): uid = self.dbus_info.GetConnectionUnixUser(self.sender) if pwd: return pwd.getpwuid(uid).pw_name else: return uid def nameOwner(self): return self.dbus_info.GetNameOwner(self.sender) def connectionPid(self): return self.dbus_info.GetConnectionUnixProcessID(self.sender) if __name__ == '__main__': DBusQtMainLoop(set_as_default=True) app = QCoreApplication([]) bus = dbus.SystemBus() name = dbus.service.BusName("net.launchpad.backintime.serviceHelper", bus) object = UdevRules(bus, '/UdevRules') print("Running BIT service.") app.exec() backintime-1.5.2/qt/settingsdialog.py000066400000000000000000003213201465446530500176600ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 copy import re import getpass from PyQt6.QtGui import (QIcon, QFont, QPalette, QBrush, QColor, QCursor, QFileSystemModel) from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QGridLayout, QDialogButtonBox, QMessageBox, QInputDialog, QGroupBox, QScrollArea, QFrame, QWidget, QTabWidget, QComboBox, QLabel, QPushButton, QToolButton, QLineEdit, QSpinBox, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QHeaderView, QCheckBox, QMenu, QProgressBar, QPlainTextEdit, QToolTip) from PyQt6.QtCore import (Qt, QDir, QSortFilterProxyModel, QThread, pyqtSignal) import config import tools import qttools import mount import messagebox import snapshots import sshtools import logger import encfsmsgbox from exceptions import MountException, NoPubKeyLogin, KnownHost from bitbase import URL_ENCRYPT_TRANSITION 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): if self._checkbox.isChecked(): return { 'host': self.host_edit.text(), 'port': self.port_edit.text(), 'user': self.user_edit.text(), } else: return { 'host': '', 'port': '', 'user': '', } 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(_('Manage profiles')) 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) 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.Shape.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 deprecation (#1734, #1735) self.encfsWarning = self._create_label_encfs_deprecation() 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.ToolButtonStyle.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.ToolButtonStyle.ToolButtonIconOnly) self.btnSshPrivateKeyFile.setIcon(icon.FOLDER) self.btnSshPrivateKeyFile.setToolTip( _('Choose an existing private key file (normally named "id_rsa")')) self.btnSshPrivateKeyFile.setMinimumSize(32, 28) hlayout3.addWidget(self.btnSshPrivateKeyFile) self.btnSshPrivateKeyFile.clicked \ .connect(self.btnSshPrivateKeyFileClicked) self.btnSshKeyGen = QToolButton(self) self.btnSshKeyGen.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly) self.btnSshKeyGen.setIcon(icon.ADD) qttools.set_wrapped_tooltip( self.btnSshKeyGen, _('Create a new SSH key without password (not allowed if a ' 'private key file is already selected).') ) self.btnSshKeyGen.setMinimumSize(32, 28) hlayout3.addWidget(self.btnSshKeyGen) self.btnSshKeyGen.clicked.connect(self.btnSshKeyGenClicked) # Disable SSH key generation button if a key file is already set self.txtSshPrivateKeyFile.textChanged \ .connect(lambda x: self.btnSshKeyGen.setEnabled(not x)) qttools.equalIndent(self.lblSshHost, self.lblSshPath, self.lblSshCipher) self.wdgSshProxy = SshProxyWidget( self, self.config.sshProxyHost(), self.config.sshProxyPort(), self.config.sshProxyUser() ) vlayout.addWidget(self.wdgSshProxy) # 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.EchoMode.Password) hlayout1.addWidget(self.txtPassword1) self.lblPassword2 = QLabel(_('Password'), self) hlayout2.addWidget(self.lblPassword2) self.txtPassword2 = QLineEdit(self) self.txtPassword2.setEchoMode(QLineEdit.EchoMode.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) # import gettext # Regular schedule modes for that combo box schedule_modes_dict = { config.Config.NONE: _('Disabled'), config.Config.AT_EVERY_BOOT: _('At every boot/reboot'), config.Config._5_MIN: ngettext( 'Every {n} minute', 'Every {n} minutes', 5).format(n=5), config.Config._10_MIN: ngettext( 'Every {n} minute', 'Every {n} minutes', 10).format(n=10), config.Config._30_MIN: ngettext( 'Every {n} minute', 'Every {n} minutes', 30).format(n=30), config.Config._1_HOUR: ngettext( 'Every hour', 'Every {n} hours', 1).format(n=1), config.Config._2_HOURS: ngettext( 'Every {n} hour', 'Every {n} hours', 2).format(n=2), config.Config._4_HOURS: ngettext( 'Every {n} hour', 'Every {n} hours', 4).format(n=4), config.Config._6_HOURS: ngettext( 'Every {n} hour', 'Every {n} hours', 6).format(n=6), config.Config._12_HOURS: ngettext( 'Every {n} hour', 'Every {n} hours', 12).format(n=12), config.Config.CUSTOM_HOUR: _('Custom hours'), config.Config.DAY: _('Every day'), config.Config.REPEATEDLY: _('Repeatedly (anacron)'), config.Config.UDEV: _('When drive gets connected (udev)'), config.Config.WEEK: _('Every week'), config.Config.MONTH: _('Every month'), config.Config.YEAR: _('Every year') } self.fillCombo(self.comboSchedule, schedule_modes_dict) self.lblScheduleDay = QLabel(_('Day:'), self) self.lblScheduleDay.setContentsMargins(5, 0, 0, 0) self.lblScheduleDay.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.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.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) glayout.addWidget(self.lblScheduleWeekday, 2, 0) self.comboScheduleWeekday = QComboBox(self) glayout.addWidget(self.comboScheduleWeekday, 2, 1) sunday = datetime.date(2011, 11, 6) for d in range(1, 8): self.comboScheduleWeekday.addItem( QIcon(), (sunday + datetime.timedelta(days=d)).strftime('%A'), d ) self.lblScheduleTime = QLabel(_('Hour:'), self) self.lblScheduleTime.setContentsMargins(5, 0, 0, 0) self.lblScheduleTime.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.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.AlignmentFlag.AlignRight | Qt.AlignmentFlag.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.AlignmentFlag.AlignRight | Qt.AlignmentFlag.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) REPEATEDLY_UNITS = { config.Config.HOUR: _('Hour(s)'), config.Config.DAY: _('Day(s)'), config.Config.WEEK: _('Week(s)'), config.Config.MONTH: _('Month(s)')} self.fillCombo(self.comboScheduleRepeatedUnit, 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) self.cbScheduleDebug = QCheckBox(self) self.cbScheduleDebug.setText(_('Enable logging of debug messages')) qttools.set_wrapped_tooltip( self.cbScheduleDebug, [ _('Writes debug-level messages into the system log via ' '"--debug".'), _('Caution: Only use this temporarily for diagnostics, as it ' 'generates a large amount of output.') ] ) glayout.addWidget(self.cbScheduleDebug, 8, 0) # 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.SelectionMode.ExtendedSelection) self.listInclude.setRootIsDecorated(False) self.listInclude.setHeaderLabels( [_('Include files and folders'), 'Count']) self.listInclude.header().setSectionResizeMode( 0, QHeaderView.ResizeMode.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(_( "{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.lblSshEncfsExcludeWarning.setWordWrap(True) layout.addWidget(self.lblSshEncfsExcludeWarning) self.listExclude = QTreeWidget(self) self.listExclude.setSelectionMode( QAbstractItemView.SelectionMode.ExtendedSelection) self.listExclude.setRootIsDecorated(False) self.listExclude.setHeaderLabels( [_('Exclude patterns, files or folders'), 'Count']) self.listExclude.header().setSectionResizeMode( 0, QHeaderView.ResizeMode.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._label_exclude_recommend = QLabel('', self) self._label_exclude_recommend.setWordWrap(True) layout.addWidget(self._label_exclude_recommend) 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) qttools.set_wrapped_tooltip( self.cbExcludeBySize, [ _('Exclude files bigger than value in {size_unit}.') .format(size_unit='MiB'), _("With 'Full rsync mode' disabled, this will only impact " "new files since for rsync, this is a transfer option, not " "an exclusion option. Therefore, large files that have " "been backed up previously will persist in snapshots even " "if they have been modified.") ] ) 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.Shape.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) REMOVE_OLD_BACKUP_UNITS = { config.Config.DAY: _('Day(s)'), config.Config.WEEK: _('Week(s)'), config.Config.YEAR: _('Year(s)')} self.fillCombo(self.comboRemoveOlderUnit, 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) MIN_FREE_SPACE_UNITS = { config.Config.DISK_UNIT_MB: 'MiB', config.Config.DISK_UNIT_GB: 'GiB' } self.fillCombo(self.comboFreeSpaceUnit, 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 removal:'), 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( '{} {}!'.format( _('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(_('week(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.Shape.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')) qttools.set_wrapped_tooltip( self.cbGlobalFlock, _('Other snapshots will be blocked until the current snapshot ' 'is done. This is a global option. So it will affect all ' 'profiles for this user. But you need to activate this for all ' 'other users, too.') ) layout.addWidget(self.cbGlobalFlock) self.cbBackupOnRestore = QCheckBox( _('Backup replaced files on restore'), self) qttools.set_wrapped_tooltip( self.cbBackupOnRestore, _("Newer versions of files will be renamed with trailing {suffix} " "before restoring. If you don't need them anymore you can " "remove them with {cmd}").format( suffix=self.snapshots.backupSuffix(), cmd='find ./ -name "*{suffix}" -delete'.format( suffix=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 whether 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) # Note about ngettext plural forms: n=102 means "Other" in Arabic and # "Few" in Polish. # Research in translation community indicate this as the best fit to # the meaning of "all". 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.Shape.NoFrame) self.tabs.addTab(scrollArea, _('E&xpert Options')) layoutWidget = QWidget(self) layout = QVBoxLayout(layoutWidget) label = QLabel(_('Caution: Change these options only if you really ' 'know what you are doing.'), self) qttools.setFontBold(label) layout.addWidget(label) label = QLabel(_("Run 'rsync' with '{cmd}':").format(cmd='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 '{cmd}':").format(cmd='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 '{cmd}':").format(cmd='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) qttools.set_wrapped_tooltip( self.cbRedirectStdoutInCron, _('Cron will automatically send an email with attached output ' 'of cronjobs if an 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) qttools.set_wrapped_tooltip( self.cbRedirectStderrInCron, _('Cron will automatically send an email with attached errors ' 'of cronjobs if an 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) qttools.set_wrapped_tooltip( self.cbBwlimit, [ "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.cbPreserveAcl = QCheckBox(_('Preserve ACL'), self) qttools.set_wrapped_tooltip( self.cbPreserveAcl, [ "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.' ] ) layout.addWidget(self.cbPreserveAcl) self.cbPreserveXattr = QCheckBox( _('Preserve extended attributes (xattr)'), self) qttools.set_wrapped_tooltip( self.cbPreserveXattr, [ "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.' ] ) layout.addWidget(self.cbPreserveXattr) self.cbCopyUnsafeLinks = QCheckBox( _('Copy unsafe links (works only with absolute links)'), self) qttools.set_wrapped_tooltip( self.cbCopyUnsafeLinks, [ "Uses 'rsync --copy-unsafe-links'. From 'man rsync':", 'This tells rsync to copy the referent of symbolic links that ' 'point outside the copied tree. Absolute symlinks are also ' 'treated like ordinary files, and so are any symlinks in the ' 'source path itself when --relative is used. This option has ' 'no additional effect if --copy-links was also specified.' ] ) layout.addWidget(self.cbCopyUnsafeLinks) self.cbCopyLinks = QCheckBox( _('Copy links (dereference symbolic links)'), self) qttools.set_wrapped_tooltip( self.cbCopyLinks, [ "Uses 'rsync --copy-links'. From 'man rsync':", 'When symlinks are encountered, the item that they point to ' '(the referent) is copied, rather than the symlink. In older ' 'versions of rsync, this option also had the side-effect of ' 'telling the receiving side to follow symlinks, such as ' 'symlinks to directories. In a modern rsync such as this one,' ' you will need to specify --keep-dirlinks (-K) to get this ' 'extra behavior. The only exception is when sending files to ' 'an rsync that is too old to understand -K -- in that case, ' 'the -L option will still have the side-effect of -K on that ' 'older receiving rsync.' ] ) layout.addWidget(self.cbCopyLinks) # one file system option self.cbOneFileSystem = QCheckBox( _('Restrict to one file system'), self) qttools.set_wrapped_tooltip( self.cbOneFileSystem, [ "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.' ] ) layout.addWidget(self.cbOneFileSystem) # additional rsync options hlayout = QHBoxLayout() layout.addLayout(hlayout) tooltip = _('Options must be quoted e.g. {example}.').format( example='--exclude-from="/path/to/my exclude file"') self.cbRsyncOptions = QCheckBox( _('Paste additional options to rsync'), self) self.cbRsyncOptions.setToolTip(tooltip) hlayout.addWidget(self.cbRsyncOptions) self.txtRsyncOptions = QLineEdit(self) self.txtRsyncOptions.setToolTip(tooltip) 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) 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.cbRsyncOptions.text(), rsync_options_value \ ='--rsync-path="FOO=bar:\\$FOO /usr/bin/rsync"'), '', '{default}: {def_value}'.format( default=_('default'), def_value=self.config.DEFAULT_SSH_PREFIX) ] qttools.set_wrapped_tooltip(self.cbSshPrefix, tooltip) hlayout.addWidget(self.cbSshPrefix) self.txtSshPrefix = QLineEdit(self) qttools.set_wrapped_tooltip(self.txtSshPrefix, tooltip) 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')) qttools.set_wrapped_tooltip( self.cbSshCheckPing, _('Warning: If disabled and the remote host is not available, ' 'this could lead to some weird errors.') ) self.cbSshCheckCommands = QCheckBox( _('Check if remote host supports all necessary commands.')) qttools.set_wrapped_tooltip( self.cbSshCheckCommands, _('Warning: If disabled and the remote host does not support all ' 'necessary commands, 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.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, parent=self) btnRestore = buttonBox.addButton( _('Restore Config'), QDialogButtonBox.ButtonRole.ResetRole) btnUserCallback = buttonBox.addButton( _('Edit user-callback'), QDialogButtonBox.ButtonRole.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 _create_label_encfs_deprecation(self): # encfs deprecation warning (see #1734, #1735) label = QLabel('{}: {}'.format( _('Warning'), _('Support for EncFS will be discontinued in the foreseeable ' 'future. A decision on a replacement for continued support of ' 'encrypted backups is still pending, depending on project ' 'resources and contributor availability. More details are ' 'available in this {whitepaper}.').format( whitepaper='{}'.format( URL_ENCRYPT_TRANSITION, _('whitepaper')) ) )) label.setWordWrap(True) label.setOpenExternalLinks(True) # Show URL in tooltip without anoing http-protocol prefix. label.linkHovered.connect( lambda url: QToolTip.showText( QCursor.pos(), url.replace('https://', '')) ) return label 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): question = _('Are you sure you want to delete ' 'the profile "{name}"?').format( name=self.config.profileName()) if self.questionHandler(question): 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 _update_exclude_recommend_label(self): """Update the label about recommended exclude patterns.""" # Default patterns that are not still in the list widget recommend = list(filter( lambda val: not self.listExclude.findItems( val, Qt.MatchFlag.MatchFixedString), self.config.DEFAULT_EXCLUDE )) if not recommend: text = _('{BOLD}Highly recommended{ENDBOLD}: (All recommendations ' 'already included.)').format( BOLD='', ENDBOLD='') else: text = _('{BOLD}Highly recommended{ENDBOLD}: {files}').format( BOLD='', ENDBOLD='', files=', '.join(sorted(recommend))) self._label_exclude_recommend.setText(text) 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()) self.cbScheduleDebug.setChecked(self.config.scheduleDebug()) # 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 = Qt.SortOrder( self.config.profileIntValue('qt.settingsdialog.include.SortOrder', Qt.SortOrder.AscendingOrder) ) self.listInclude.sortItems(includeSortColumn, includeSortOrder) # TAB: Exclude self.listExclude.clear() for exclude in self.config.exclude(): self._add_exclude_pattern(exclude) self.cbExcludeBySize.setChecked(self.config.excludeBySizeEnabled()) self.spbExcludeBySize.setValue(self.config.excludeBySize()) excludeSortColumn = int(self.config.profileIntValue( 'qt.settingsdialog.exclude.SortColumn', 1)) excludeSortOrder = Qt.SortOrder( self.config.profileIntValue('qt.settingsdialog.exclude.SortOrder', Qt.SortOrder.AscendingOrder) ) self.listExclude.sortItems(excludeSortColumn, excludeSortOrder) self._update_exclude_recommend_label() # 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.cbOneFileSystem.setChecked(self.config.oneFileSystem()) 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): item_data = self.comboSchedule.itemData( self.comboSchedule.currentIndex()) if item_data == self.config.CUSTOM_HOUR: # TODO # Dev note (buhtz, 2024-05): IMHO checkCronPattern() is not needed # because the "crontab" command itself will validate this. See # schedule.write_crontab(). # We just need to take care to catch an the error in the GUI # and report it to the user. # An alternative solution would be a GUI element where the user # is not able to input an invalid value. See #1449 about redesign # the schedule section in the Manage Profiles dialog. 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()) sshproxy_vals = self.wdgSshProxy.values() self.config.setSshProxyHost(sshproxy_vals['host']) self.config.setSshProxyPort(sshproxy_vals['port']) self.config.setSshProxyUser(sshproxy_vals['user']) 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(): question = '{}\n{}'.format( _('You did not choose a private key file for SSH.'), _('Would you like to generate a new password-less ' 'public/private key pair?')) if self.questionHandler(question): self.btnSshKeyGenClicked() if not self.txtSshPrivateKeyFile.text(): return False if not os.path.isfile(self.txtSshPrivateKeyFile.text()): self.errorHandler( _('Private key file "{file}" does not exist.') .format(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.SortOrder.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.ItemDataRole.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.SortOrder.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())) self.config.setScheduleDebug(self.cbScheduleDebug.isChecked()) # 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.setOneFileSystem(self.cbOneFileSystem.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) question = _('Would you like to copy your public SSH key to ' 'the remote host to enable password-less login?') 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(), askPass=tools.which('backintime-askpass'), cipher=self.config.sshCipher() ) if self.questionHandler(question) and rc_copy_id: 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 = '{}\n\n{}'.format( _("The authenticity of host {host} can't be " "established.").format( host=self.config.sshHost()), _('{keytype} key fingerprint is:').format( 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): answer = messagebox.warningYesNo(self, message) return answer == QMessageBox.StandardButton.Yes 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.ItemDataRole.UserRole, data[1]) self.listIncludeCount += 1 item.setText(1, str(self.listIncludeCount).zfill(6)) item.setData(1, Qt.ItemDataRole.UserRole, self.listIncludeCount) self.listInclude.addTopLevelItem(item) if self.listInclude.currentItem() is None: self.listInclude.setCurrentItem(item) return item def _add_exclude_pattern(self, pattern): item = QTreeWidgetItem() item.setText(0, pattern) item.setData(0, Qt.ItemDataRole.UserRole, pattern) self._formatExcludeItem(item) # Add item to the widget self.listExclude.addTopLevelItem(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)) self._update_exclude_recommend_label() def addExclude(self, pattern): """Initiate adding a new exclude pattern to the list widget. See `_add_exclude_pattern()` also. """ if not pattern: return # Duplicate? if self.listExclude.findItems(pattern, Qt.MatchFlag.MatchFixedString): return # Create new entry and add it to the list widget. item = self._add_exclude_pattern(pattern) # Select/highlight that entry. self.listExclude.setCurrentItem(item) self._update_exclude_recommend_label() def btnExcludeAddClicked(self): dlg = QInputDialog(self) dlg.setInputMode(QInputDialog.InputMode.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): """Development Note (buhtz 2023-12): This is a candidate for refactoring. See btnIncludeAddClicked() with much duplicated code. """ 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()): question_msg = _( '"{path}" is a symlink. The linked target will not be ' 'backed up until you include it, too.\nWould you like ' 'to include the symlink target instead?' ).format(path=path) if self.questionHandler(question_msg): 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): """Development Note (buhtz 2023-12): This is a candidate for refactoring. See btnIncludeFileClicked() with much duplicated code. """ 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()): question_msg = _( '"{path}" is a symlink. The linked target will not be ' 'backed up until you include it, too.\nWould you like ' 'to include the symlink target instead?') \ .format(path=path) if self.questionHandler(question_msg): 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: question = _('Are you sure you want to change ' 'snapshots folder?') if not self.questionHandler(question): 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}.') .format(path=key)) def comboModesChanged(self, *params): """Hide/show widget elements related to one of the four snapshot modes. """ if not params: index = self.comboModes.currentIndex() else: index = params[0] active_mode = str(self.comboModes.itemData(index)) if active_mode != self.mode: # DevNote (buhtz): Widgets of the GUI related to the four # snapshot modes are acccesed via "getattr(self, ...)". # These are 'Local', 'Ssh', 'LocalEncfs', 'SshEncfs' for mode in list(self.config.SNAPSHOT_MODES.keys()): # Hide all widgets getattr(self, 'mode%s' % tools.camelCase(mode)).hide() for mode in list(self.config.SNAPSHOT_MODES.keys()): # Show up the widget related to the selected mode. 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) # EncFS deprecation warnings (see #1734) if active_mode in ('local_encfs', 'ssh_encfs'): self.encfsWarning.setHidden(False) # Workaround to avoid showing the warning messagebox just when # opening the manage profiles dialog. if self.isVisible(): # Show the profile specific warning dialog only once per # profile. if self.config.profileBoolValue('msg_shown_encfs') is False: self.config.setProfileBoolValue('msg_shown_encfs', True) dlg = encfsmsgbox.EncfsCreateWarning(self) dlg.exec() else: self.encfsWarning.setHidden(True) 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 _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:`_formatExcludeItem` 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 _formatExcludeItem(self, item): """Modify visual appearance of an item in the exclude list widget. """ 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 customSortOrder(self, header, loop, newColumn, newOrder): if newColumn == 0 and newOrder == Qt.SortOrder.AscendingOrder: if loop: newColumn, newOrder = 1, Qt.SortOrder.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): return ' ' + _('(default: {})').format( _('enabled') if value else _('disabled')) 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(_('Import configuration')) layout = QVBoxLayout(self) layout.addWidget(self._create_hint_label()) # treeView self.treeView = qttools.MyTreeView(self) self.treeViewModel = QFileSystemModel(self) self.treeViewModel.setRootPath(QDir().rootPath()) self.treeViewModel.setReadOnly(True) self.treeViewModel.setFilter(QDir.Filter.AllDirs | QDir.Filter.NoDotAndDotDot | QDir.Filter.Hidden) self.treeViewFilterProxy = QSortFilterProxyModel(self) self.treeViewFilterProxy.setDynamicSortFilter(True) self.treeViewFilterProxy.setSourceModel(self.treeViewModel) self.treeViewFilterProxy.setFilterRegularExpression(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.ContextMenuPolicy.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.ColorRole.WindowText, QColor(205, 0, 0)) self.colorGreen = QPalette() self.colorGreen.setColor( QPalette.ColorRole.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( _('Import'), QDialogButtonBox.ButtonRole.AcceptRole) self.restoreButton.setEnabled(False) buttonBox.addButton(QDialogButtonBox.StandardButton.Cancel) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) layout.addWidget(buttonBox) self.scan.start() self.resize(600, 700) def _create_hint_label(self): """Create the label to explain how and where to find existing config file. Returns: (QLabel): The label """ samplePath = os.path.join( 'backintime', self.config.host(), getpass.getuser(), '1', snapshots.SID(datetime.datetime.now(), self.config).sid ) samplePath = f'{samplePath}' text_a = _( 'Select the snapshot folder from which the configuration ' 'file should be imported. The path may look like: {samplePath}' ).format(samplePath=samplePath) text_b = _( 'If the folder 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) return label 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 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. """ 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(), getpass.getuser()) 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 Exception as exc: logger.error( f'Unhandled branch in code! See in {__file__} ' f'SettingsDialog.searchConfig()\n{exc}') 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.setFilterRegularExpression(r'') else: self.treeViewFilterProxy.setFilterRegularExpression(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): """ prepare 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.StandardButton.Ok | QDialogButtonBox.StandardButton.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.errorHandle( '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() backintime-1.5.2/qt/snapshotsdialog.py000066400000000000000000000406611465446530500200500ustar00rootroot00000000000000# Back In Time # Copyright (C) 2008-2022 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 subprocess import shlex from PyQt6.QtGui import * from PyQt6.QtWidgets import * from PyQt6.QtCore import * import tools import restoredialog import messagebox import qttools import snapshots import logger DIFF_PARAMS = '%1 %2' if tools.checkCommand('meld'): DIFF_CMD = 'meld' elif tools.checkCommand('kompare'): DIFF_CMD = 'kompare' else: DIFF_CMD = '' class DiffOptionsDialog(QDialog): def __init__(self, parent): super(DiffOptionsDialog, self).__init__(parent) self.config = parent.config import icon self.setWindowIcon(icon.DIFF_OPTIONS) self.setWindowTitle(_('Options about comparing snapshots')) self.mainLayout = QGridLayout(self) 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(cmd, self) self.mainLayout.addWidget(self.editCmd, 0, 1) self.mainLayout.addWidget(QLabel(_('Parameters:')), 1, 0) 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) 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): """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 tools.checkCommand(cmd) == False: 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)) # 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): def __init__(self, parent, sid, path): super(SnapshotsDialog, self).__init__(parent) self.parent = parent self.config = parent.config self.snapshots = parent.snapshots self.snapshotsList = parent.snapshotsList self.qapp = parent.qapp import icon self.sid = sid self.path = path self.setWindowIcon(icon.SNAPSHOTS) self.setWindowTitle(_('Snapshots')) self.mainLayout = QVBoxLayout(self) #path self.editPath = QLineEdit(self.path, self) self.editPath.setReadOnly(True) self.mainLayout.addWidget(self.editPath) #list different snapshots only self.cbOnlyDifferentSnapshots = QCheckBox( _('Differing snapshots only'), self) self.mainLayout.addWidget(self.cbOnlyDifferentSnapshots) self.cbOnlyDifferentSnapshots.stateChanged.connect(self.cbOnlyDifferentSnapshotsChanged) #list equal snapshots only layout = QHBoxLayout() self.mainLayout.addLayout(layout) self.cbOnlyEqualSnapshots = QCheckBox( _('List only snapshots that are equal to:'), self) self.cbOnlyEqualSnapshots.stateChanged.connect( self.cbOnlyEqualSnapshotsChanged) layout.addWidget(self.cbOnlyEqualSnapshots) self.comboEqualTo = qttools.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) self.mainLayout.addWidget(self.cbDeepCheck) self.cbDeepCheck.stateChanged.connect(self.cbDeepCheckChanged) #toolbar self.toolbar = QToolBar(self) self.toolbar.setFloatable(False) self.mainLayout.addWidget(self.toolbar) #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.triggered.connect(self.restoreThisTo) self.btnRestore = self.toolbar.addAction(icon.RESTORE, _('Restore')) self.btnRestore.setMenu(menuRestore) self.btnRestore.triggered.connect(self.restoreThis) #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')) 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) # Diff layout = QHBoxLayout() self.mainLayout.addLayout(layout) self.btnDiff = QPushButton(_('Compare'), self) layout.addWidget(self.btnDiff) self.btnDiff.clicked.connect(self.btnDiffClicked) self._update_btn_diff() self.comboDiff = qttools.SnapshotCombo(self) layout.addWidget(self.comboDiff, 2) #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(_('Options'), QDialogButtonBox.ButtonRole.HelpRole) btnDiffOptions.setIcon(icon.DIFF_OPTIONS) self.mainLayout.addWidget(buttonBox) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) btnDiffOptions.clicked.connect(self.btnDiffOptionsClicked) # self.cbDeepCheck.setEnabled(False) full_path = self.sid.pathBackup(self.path) if os.path.islink(full_path): self.cbDeepCheck.hide() elif os.path.isdir(full_path): self.cbOnlyDifferentSnapshots.hide() self.cbOnlyEqualSnapshots.hide() self.comboEqualTo.hide() self.cbDeepCheck.hide() #update list and combobox self.UpdateSnapshotsAndComboEqualTo() def addSnapshot(self, sid): self.timeLine.addSnapshot(sid) #add to combo self.comboDiff.addSnapshotID(sid) if self.sid == sid: self.comboDiff.setCurrentSnapshotID(sid) self.comboDiff.checkSelection() def updateSnapshots(self): self.timeLine.clear() self.comboDiff.clear() equal_to_sid = self.comboEqualTo.currentSnapshotID() 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) for sid in snapshotsFiltered: self.addSnapshot(sid) self.updateToolbar() def UpdateComboEqualTo(self): self.comboEqualTo.clear() snapshotsFiltered = self.snapshots.filter(self.sid, self.path, self.snapshotsList) for sid in snapshotsFiltered: self.comboEqualTo.addSnapshotID(sid) if sid == self.sid: self.comboEqualTo.setCurrentSnapshotID(sid) self.comboEqualTo.checkSelection() def UpdateSnapshotsAndComboEqualTo(self): self.updateSnapshots() self.UpdateComboEqualTo() def cbOnlyDifferentSnapshotsChanged(self): enabled = self.cbOnlyDifferentSnapshots.isChecked() self.cbOnlyEqualSnapshots.setEnabled(not enabled) self.cbDeepCheck.setEnabled(enabled) self.updateSnapshots() def cbOnlyEqualSnapshotsChanged(self): enabled = self.cbOnlyEqualSnapshots.isChecked() self.comboEqualTo.setEnabled(enabled) self.cbOnlyDifferentSnapshots.setEnabled(not enabled) self.cbDeepCheck.setEnabled(enabled) self.updateSnapshots() def cbDeepCheckChanged(self): self.updateSnapshots() def updateToolbar(self): sids = self.timeLine.selectedSnapshotIDs() 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 else: enable_restore = False enable_delete = True for sid in sids: if sid.isRoot: enable_delete = False self.btnRestore.setEnabled(enable_restore) self.btnDelete.setEnabled(enable_delete) def restoreThis(self): # See #1485 as related bug report sid = self.timeLine.currentSnapshotID() if not sid.isRoot: restoredialog.restore(self, sid, self.path) # pylint: disable=E1101 def restoreThisTo(self): # See #1485 as related bug report sid = self.timeLine.currentSnapshotID() if not sid.isRoot: restoredialog.restore(self, sid, self.path, None) # pylint: disable=E1101 def timeLineChanged(self): self.updateToolbar() def timeLineExecute(self, item, column): if self.qapp.keyboardModifiers() and Qt.ControlModifier: return sid = self.timeLine.currentSnapshotID() if not sid: 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) self.run = QDesktopServices.openUrl(QUrl(full_path)) def btnDiffClicked(self): sid1 = self.timeLine.currentSnapshotID() sid2 = self.comboDiff.currentSnapshotID() if not sid1 or not sid2: return path1 = sid1.pathBackup(self.path) path2 = sid2.pathBackup(self.path) # check if the 2 paths are different if path1 == path2: messagebox.critical( self, _("You can't compare a snapshot to itself.")) return diffCmd = self.config.strValue('qt.diff.cmd', DIFF_CMD) diffParams = self.config.strValue('qt.diff.params', DIFF_PARAMS) # 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) if not isinstance(sid2, snapshots.RootSnapshot): path2 = self.parent.tmpCopy(path2, sid2) params = diffParams params = params.replace('%1', '"%s"' % path1) params = params.replace('%2', '"%s"' % path2) cmd = diffCmd + ' ' + params logger.debug(f'Compare two snapshots 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() self._update_btn_diff() def comboEqualToChanged(self, index): self.updateSnapshots() def btnDeleteClicked(self): items = self.timeLine.selectedItems() if not items: return elif len(items) == 1: msg = _('Do you really want to delete {file} in snapshot ' '{snapshot_id}?').format( file=f'"{self.path}"', snapshot_id=f'"{items[0].snapshotID()}"') else: msg = _('Do you really want to delete {file} in {count} ' 'snapshots?').format( file=f'"{self.path}"', count=len(items)) msg = _('WARNING: This cannot be revoked.') answer = messagebox.warningYesNo(self, msg) if answer == QMessageBox.StandardButton.Yes: for item in items: item.setFlags(Qt.ItemFlag.NoItemFlags) thread = RemoveFileThread(self, items) thread.started.connect(lambda: self.btnGoto.setDisabled(True)) thread.finished.connect(lambda: self.btnGoto.setDisabled(False)) thread.started.connect(lambda: self.btnDelete.setDisabled(True)) thread.finished.connect(lambda: self.btnDelete.setDisabled(False)) thread.finished.connect(self.UpdateSnapshotsAndComboEqualTo) self.btnCancel.clicked.connect(thread.terminate) thread.start() exclude = self.config.exclude() msg = _('Exclude {path} from future snapshots?').format( path=f'"{self.path}"') if self.path not in exclude: answer = messagebox.warningYesNo(self, msg) if answer == QMessageBox.StandardButton.Yes: 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) def accept(self): sid = self.timeLine.currentSnapshotID() if sid: self.sid = sid 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.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) backintime-1.5.2/qt/test/000077500000000000000000000000001465446530500152445ustar00rootroot00000000000000backintime-1.5.2/qt/test/__init__.py000066400000000000000000000000001465446530500173430ustar00rootroot00000000000000backintime-1.5.2/qt/test/test_lint.py000066400000000000000000000116741465446530500176340ustar00rootroot00000000000000import unittest import os import pathlib import subprocess import shutil from typing import Iterable ON_TRAVIS = os.environ.get('TRAVIS', '') == 'true' PYLINT_AVIALBE = not shutil.which('pylint') is None PYLINT_REASON = ('Using PyLint is mandatory on TravisCI, on other systems' 'it runs only if `pylint` is available.') class MirrorMirrorOnTheWall(unittest.TestCase): """Check all py-files in the package (incl. test files) for lints and potential bugs and if they are compliant to the coding styles (e.g. PEP8). """ def _collect_py_files(self) -> 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 current folder ' f'is {path}.') # Workaround path = path.parent # Find recursive all py-files. return path.rglob('**/*.py') @unittest.skipUnless(ON_TRAVIS or PYLINT_AVIALBE, PYLINT_REASON) def test_with_pylint(self): """Use Pylint to check for specific error codes. Some facts about PyLint - It is one of the slowest available linters. - It is able to catch lints none of the other linters """ # Pylint base command 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', # Deactivate all checks by default '--disable=all', # 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) '--max-line-length=79', # Whitelist variable names '--good-names=idx,fp', # '--reports=yes', ] # Explicit activate checks err_codes = [ 'C0305', # trailing-newlines 'C0325', # superfluous-parens 'C0410', # multiple-imports 'C0303', # trailing-whitespace '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 'W0311', # bad-indentation 'W0611', # unused-import 'I0021', # useless-suppression # 'W0611', # unused-import 'W1301', # unused-format-string-key 'W1401', # anomalous-backslash-in-string (invalid escape sequence) 'W1515', # forgotten-debug-statement # Enable asap. This list is selection of existing (not all!) # problems currently exiting in the BIT code base. Quit easy to fix # because there count is low. # 'R0201', # no-self-use # 'R0202', # no-classmethod-decorator # 'R0203', # no-staticmethod-decorator # 'R0801', # duplicate-code # 'W0123', # eval-used # 'W0237', # arguments-renamed # 'W0221', # arguments-differ # 'W0404', # reimported # 'W4902', # deprecated-method # 'W4904', # deprecated-class # 'W0603', # global-statement # 'W0614', # unused-wildcard-import # 'W0611', # unused-import # 'W0612', # unused-variable # 'W0707', # raise-missing-from ] cmd.append('--enable=' + ','.join(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 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.') backintime-1.5.2/update_language_files.py000077500000000000000000000507471465446530500205420ustar00rootroot00000000000000#!/usr/bin/env python3 """This helper script does manage transferring translations to and from the translation platform (currently Weblate). """ import sys import io import datetime import json import re import tempfile import string import shutil import pprint from pathlib import Path from subprocess import run, check_output from common import languages 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' WEBLATE_URL = 'https://translate.codeberg.org/git/backintime/common' PACKAGE_NAME = 'Back In Time' PACKAGE_VERSION = Path('VERSION').read_text('utf-8').strip() 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)') 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) 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. The function `update_po_template()` should be called before. """ print( 'Update language (po) files' + ' and remove obsolete entries' if remove_obsolete_entries else '' ) # 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) 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', '--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: Curly brackets nested: {to_check}') return False if invalid: print(f'\nERROR: Curly brackets not balanced : {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: {exc} in translation: {to_check}') return False return True def _place_holders(trans_string, src_string, flags): """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 flag "ignore-placeholder-compare" to the entry in the po-file. """ if 'ignore-placeholder-compare' in flags: return True flagmsg = 'Disable this check with flagging it with ' \ '"ignore-placeholder-compare" in its po-file.' # Compare number of curly brackets. for bracket in tuple('{}'): if src_string.count(bracket) != trans_string.count(bracket): print(f'\nERROR: Number of "{bracket}" between original ' 'source and translated string is different.\n' f'Translation: {trans_string}\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('\nERROR: Names of placeholders between original source ' 'and translated string are different.\n' f'Names in original : {org_names}\n' f'Names in translation : {trans_names}\n' f'Full translation: {trans_string}\n{flagmsg}') return False return True print('Checking syntax of po files...') # Each po file for po_path in all_po_files_in_local_dir(): # Language code determined by po-filename print(f'{po_path.with_suffix("").name}', end=' ') 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 _other_errors(entry.msgstr) or not _place_holders(entry.msgstr, entry.msgid, entry.flags)): print(f'Source string: {entry.msgid}\n') print('') def all_po_files_in_local_dir(): """All po files (recursive).""" return LOCAL_DIR.rglob('**/*.po') 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 # info # print(json.dumps(result, indent=4)) return result def create_languages_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 = update_language_names() stream = io.StringIO() pprint.pprint(names, indent=2, stream=stream, sort_dicts=True) stream.seek(0) names = stream.read() # the same with completeness dict compl_dict = create_completeness_dict() stream = io.StringIO() pprint.pprint(compl_dict, indent=2, stream=stream, sort_dicts=True) stream.seek(0) completeness = stream.read() with LANGUAGE_NAMES_PY.open('w', encoding='utf8') as handle: date_now = datetime.datetime.now().strftime('%c') handle.write( f'# Generated at {date_now} with help of package "babel" ' 'and "polib".\n') handle.write('# https://babel.pocoo.org\n') handle.write('# https://github.com/python-babel/babel\n') handle.write('\nnames = {\n') handle.write(names[1:]) handle.write('\n') handle.write('\ncompleteness = {\n') handle.write(completeness[1:]) 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 # 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 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', # Manage profiles dialog (tabs) '&General', '&Include', '&Exclude', '&Auto-remove', '&Options', 'Back In &Time', '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)}') # WORKAROUND # This source string is not a translateble string but has a shortcut # letter. # Dev note: From point of view of the translators it might make sense # making that string translatable also. But then we risk that our projects # name is translated for real. expect = ['Back In &Time'] + expect 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) 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) create_languages_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_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' ' shortcut - Check po files for redundant keyboard shortcuts ' 'using "&"\n' ' syntax - Check syntax of po files. (Also done via "weblate" ' 'command)') sys.exit(1) backintime-1.5.2/updateversion.sh000077500000000000000000000044651465446530500171010ustar00rootroot00000000000000#!/bin/bash # 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` if [[ $VERSION == *-dev ]] then VERSION+="."`git rev-parse --short HEAD` fi echo VERSION: $VERSION MAINTAINER="Germar Reitze " # MAINTAINER="BIT Team " # MAINTAINER="BIT Team " update_app_version () { echo "Update '$1'" sed -e "s/^\(\s*\)__version__ = '.*'$/\1__version__ = '$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/^\([ \]*\)