pax_global_header00006660000000000000000000000064145567354140014530gustar00rootroot0000000000000052 comment=1e37d9c4d3d8bd2f782125b78831bd1cec53bbca backintime-1.4.3/000077500000000000000000000000001455673541400136435ustar00rootroot00000000000000backintime-1.4.3/.codespellrc000066400000000000000000000022561455673541400161500ustar00rootroot00000000000000[codespell] # Folders and files to skip skip = .codespellrc,*.po,Makefile,*.desktop,.git,__pycache__,*.pyc,languages.py # 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 # Allowed (ignored) words in URLs and URIs uri-ignore-words-list=mitre # 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". Tu 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.4.3/.github/000077500000000000000000000000001455673541400152035ustar00rootroot00000000000000backintime-1.4.3/.github/ISSUE_TEMPLATE.md000066400000000000000000000011461455673541400177120ustar00rootroot00000000000000To 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 subscribtion 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.4.3/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000000461455673541400210040ustar00rootroot00000000000000Don't forget the changelog entry! ;) backintime-1.4.3/.gitignore000066400000000000000000000014061455673541400156340ustar00rootroot00000000000000# 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.4.3/.readthedocs.yaml000066400000000000000000000017271455673541400171010ustar00rootroot00000000000000# .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.4.3/.travis.yml000066400000000000000000000037301455673541400157570ustar00rootroot00000000000000# TravisCI (https://travis-ci.org) configuration file # https://docs.travis-ci.com/user/languages/python language: python os: linux arch : - amd64 - ppc64le # ensures that we have UUID filesystem mounts for proper testing dist: focal addons: # add localhost to known_hosts to prevent ssh unknown host prompt during unit tests ssh_known_hosts: localhost python: - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" 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-get -qq update # install screen, and util-linux (provides flock) for test_sshtools - sudo apt-get install -y sshfs screen util-linux jobs: exclude: #release prep# - python: "3.9" #release prep# - python: "3.10" #release prep# - python: "3.11" # Excluding this temporarily because of an Issue on Travis. Support contact established. - arch: ppc64le python: "3.12" install: - pip install pylint coveralls pyfakefs # 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: # Is a virtual environment active? Yes: if both variables are not equal. # - python -c "import sys;print(f'{sys.prefix=} {sys.base_prefix=} In virtualenv={sys.prefix != sys.base_prefix}')" # - coverage debug sys # 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.4.3/AUTHORS000066400000000000000000000003421455673541400147120ustar00rootroot00000000000000Oprea Dan () Bart de Koning () Richard Bailey () Germar Reitze () Taylor Raack () Christian Buhtz Michael Büker Jürgen Altfeld backintime-1.4.3/CHANGES000066400000000000000000001637461455673541400146570ustar00rootroot00000000000000Back In Time 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.4.3/CONTRIBUTING.md000066400000000000000000000123141455673541400160750ustar00rootroot00000000000000# How to contribute to _Back In Time_ 😊 **Thanks for taking the time to contribute!** The maintenance team will welcome all types of contributions. No contribution will be rejected just because it doesn't fit to our quality standards, guidelines or rules. Every contribution is reviewed and if needed will be improved together with the maintainers. Please always make a new branch when preparing a Pull Request ("PR") or a patch. Baseline that feature or bug fix branch on `dev` (the latest development state). When open a pull request please make sure that it targets `bit-team:dev`. Please take the following best practices into account if possible (to 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 - Follow [Google Style Guide](https://sphinxcontrib-napoleon.readthedocs.org/en/latest/example_google.html) for docstrings (see our own [HOWTO about doc generation](common/doc-dev/1_doc_maintenance_howto.md)). - Be careful when using automatic formatters like `black` and please mention the use of it 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 you can use `pytest`. - Try to create new unit tests if appropriated. Use Pythons regular `unittest` instead of `pytest`. If you know the difference please try follow the _Classical (aka Detroit) school_ instead of _London (aka mockist) school_. ## Index - [Resources](#resources) - [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) - [Further reading](#further-reading) - [Licensing of contributed material](#licensing-of-contributed-material) ## Resources - [Mailing list _bit-dev_](https://mail.python.org/mailman3/lists/bit-dev.python.org/) for development related topics - [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 documents](common/doc-dev/README.md) ## 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` - `python3-keyring` - `python3-dbus` - `python3-packaging` - Recommended - `sshfs` - `encfs` * Runtime dependencies for the GUI - `x11-utils` - `python3-pyqt5` - `python3-dbus.mainloop.pyqt5` - `libnotify-bin` - `policykit-1` - `qttranslations5-l10n` - `qtwayland5` (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` ## Further reading - https://www.contribution-guide.org - https://mozillascience.github.io/working-open-workshop/contributing ## 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. Sept 2023 backintime-1.4.3/FAQ.md000066400000000000000000001222071455673541400146000ustar00rootroot00000000000000# 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) - [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) - [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) - [Common problems and solutions](#common-problems-and-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) - [Error Handling](#error-handling) * [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) * [Version >= 1.2.0 works very slow / Unchanged files are backed up](#version--120-works-very-slow--unchanged-files-are-backed-up) * [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) * [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) - [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). ## 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). ## 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. ## Common problems and 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) ## Error Handling ### 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 ### 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. ### 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-Remove - Plugin- and user-callback support ### 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 ## Testing & Building ### SSH related tests are skipped Some tests require an available SSH server. They get skipped if this is not the case. Please see [Setup SSH Server to run unit tests](#setup-ssh-server-to-run-unit-tests). ### Setup SSH Server to run unit tests 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) 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. backintime-1.4.3/LICENSE000066400000000000000000000431031455673541400146510ustar00rootroot00000000000000 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.4.3/README.md000066400000000000000000000254771455673541400151410ustar00rootroot00000000000000[![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, Christian Buhtz, Michael Büker, Jürgen Altfeld It is an easy-to-use backup tool for files and folders. It runs on GNU Linux (sorry - not on Windows or OS X/macOS) and provides a command line tool `backintime` and a Qt5 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 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](https://en.wikipedia.org/wiki/FlyBack). You only need to specify 3 things: * What folders to back up. * Where to save snapshots. * The backup frequency (manual, every hour, every day, every month). ## Maintenance status A small team (Christian Buhtz, Michael Büker and Jürgen Altfeld) has started in summer 2022 to get things moving again after the development of this project has been dormant for a while. We do the development in our spare time and have to prioritize so stick with us, we all ♥️ _Back In Time_. 😁 We are currently focusing 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). If you are interested in the development, please see [CONTRIBUTING](CONTRIBUTING.md) and have a look on [open issues](https://github.com/bit-team/backintime/issues) especially those labeled as [good first](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). ## Index - [Documentation, FAQs, Support](#documentation-faqs-support) - [Installation](#installation) - [Known Problems and Workarounds](#known-problems-and-workarounds) - [CONTRIBUTING](CONTRIBUTING.md) ## Documentation, FAQs, Support * [End user documentation](https://backintime.readthedocs.org/) (not totally up-to-date) * [FAQ - Frequently Asked Questions](FAQ.md) * [Source code documentation for developers](https://backintime-dev.readthedocs.org) * Use [Issues](https://github.com/bit-team/backintime/issues) to ask questions and report bugs. * [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. ## 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) - RTE "module 'qttools' has no attribute 'initate_translator'" with encFS when prompting the user for a password (#1553) - [Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).](#warning-apt-key-is-deprecated-manage-keyring-files-in-trustedgpgd-instead-see-apt-key8) - [`qt5_probing.py` may hang with high CPU usage when running BiT as `root` via `cron`](#qt5_probingpy-may-hang-with-high-cpu-usage-when-running-bit-as-root-via-cron) In older releases: - [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) - [Python 3.10 compatibility and Ubuntu version](#python-310-compatibility-and-ubuntu-version) ### 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. #### 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 manually install _Back In Time_ as described in the [Installation](#installation) section here. 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/). You can currently ignore this warning until we have found a reliable way to support all Ubuntu distros (older and newer ones). This issue is tracked in [#1338](https://github.com/bit-team/backintime/issues/1338). #### `qt5_probing.py` may hang with high CPU usage when running BiT as `root` via `cron` See the related issue #1592 The only reliable work-around is to delete (or move into another folder) the file `/usr/share/backintime/common/qt5_probing.py`: `mv /usr/share/backintime/common/qt5_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 Qt5-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 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)). The problem is [fixed](https://github.com/bit-team/backintime/pull/1351) in the current master branch of that repo and will be released with the next release (`1.3.3`) of _Back In Time_. 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. #### Python 3.10 compatibility and Ubuntu version _Back In Time_ versions older than 1.3.2 do not start with Python >= 3.10. Ubuntu 22.04 LTS ships with Python 3.10 and backintime 1.2.1, but has applied [a patch](https://bugs.launchpad.net/ubuntu/+source/backintime/+bug/1976164/+attachment/5593556/+files/backintime_1.2.1-3_1.2.1-3ubuntu0.1.diff) to make it work. If you want to update to backintime 1.3.2 in Ubuntu, you may use the PPA: see under [`INSTALL/Ubuntu PPA`](#Ubuntu-PPA). Jan 2024 backintime-1.4.3/TRANSLATIONS000066400000000000000000000021371455673541400155120ustar00rootroot00000000000000Bokmål (Norwegian): Hans Fredrik Nordhaug Catalan: Josep Sanchez German: Michael Wiedmann French: Michel Corps , jej@github Indonesian: Andika Triwidada Japanese: Ayako Buhtz Nynorsk (Norwegian) - Hans Fredrik Nordhaug - VL Polish: Paweł Hołuj Portuguese (Brazilian): - Scythemare - Andre Desgualdo Pereira - Yuri Musachio Portuguese: - Yuri Musachio Romanian: Florentina Mușat Russian: Vadim Peretokin Slovak: Tomáš Vadina Slovenian: - Vanja Cvelbar - Liam Starič Spanish: - Francisco Manuel García Claramonte - Mauricio J. Adonis C. Swedish: Niklas Grahn Other languages: - Launchpad translators - backintime-1.4.3/VERSION000066400000000000000000000000061455673541400147070ustar00rootroot000000000000001.4.3 backintime-1.4.3/common/000077500000000000000000000000001455673541400151335ustar00rootroot00000000000000backintime-1.4.3/common/.coveragerc000066400000000000000000000002531455673541400172540ustar00rootroot00000000000000# default options for coverage from coveralls-python [run] omit = */virtualenv/python3* [report] exclude_lines = pragma: no cover if __name__ == '__main__': backintime-1.4.3/common/applicationinstance.py000066400000000000000000000213241455673541400215370ustar00rootroot00000000000000# 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 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) """ 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.4.3/common/askpass.py000066400000000000000000000031761455673541400171610ustar00rootroot00000000000000# 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 try: import gtk except: pass 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.4.3/common/backintime000077500000000000000000000032131455673541400171660ustar00rootroot00000000000000#!/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.4.3/common/backintime-askpass000077500000000000000000000022271455673541400206350ustar00rootroot00000000000000#!/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.4.3/common/backintime.desktop000066400000000000000000000003731455673541400206370ustar00rootroot00000000000000[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.4.3/common/backintime.py000066400000000000000000001256561455673541400176320ustar00rootroot00000000000000# 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 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 from exceptions import MountException from applicationinstance import ApplicationInstance 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 ' + str(config.Config.VERSION), help = "show %(prog)s's version number.") parser.add_argument('--license', action = printLicense, nargs = 0, help = "show %(prog)s's license.") 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 Remove" pattern.' smartRemoveCP = subparsers.add_parser(command, epilog = epilogCommon, help = description, description = description) smartRemoveCP.set_defaults(func = smartRemove) parsers[command] = smartRemoveCP command = 'snapshots-list' nargs = 0 aliases.append((command, nargs)) description = 'Show a list of 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() #parse args args = argParse(None) #add source path to $PATH environ if running from source if tools.runningFromSource(): tools.addSourceToPathEnviron() #warn about sudo if tools.usingSudo() and os.getenv('BIT_SUDO_WARNING_PRINTED', 'false') == 'false': os.putenv('BIT_SUDO_WARNING_PRINTED', 'true') logger.warning("It looks like you're using 'sudo' to start %(app)s. " "This will cause some trouble. Please use either 'sudo -i %(app_name)s' " "or 'pkexec %(app_name)s'." %{'app_name': app_name, 'app': config.Config.APP_NAME}) #call commands if 'func' in dir(args): args.func(args) else: setQuiet(args) printHeader() return getConfig(args, False) def argParse(args): """ Parse arguments given on commandline. Args: args (argparse.Namespace): Namespace that should be enhanced or ``None`` Returns: argparser.Namespace: new parsed Namespace """ def join(args, subArgs): """ Add new arguments to existing Namespace. Args: args (argparse.Namespace): main Namespace that should get new arguments subArgs (argparse.Namespace): second Namespace which have new arguments that should be merged into ``args`` """ for key, value in vars(subArgs).items(): #only add new values if it isn't set already or if there really IS a value if getattr(args, key, None) is None or value: setattr(args, key, value) #first parse the main parser without subparsers #otherwise positional args in subparsers will be to greedy #but only if -h or --help is not involved because otherwise #help will not work for subcommands mainParser = parsers['main'] sub = [] if '-h' not in sys.argv and '--help' not in sys.argv: for i in mainParser._actions: if isinstance(i, argparse._SubParsersAction): #remove subparsers mainParser._remove_action(i) sub.append(i) args, unknownArgs = mainParser.parse_known_args(args) #read subparsers again if sub: [mainParser._add_action(i) for i in sub] #parse it again for unknown args if unknownArgs: subArgs, unknownArgs = mainParser.parse_known_args(unknownArgs) join(args, subArgs) #finally parse only the command parser, otherwise we miss #some arguments from command if unknownArgs and 'command' in args and args.command in parsers: commandParser = parsers[args.command] subArgs, unknownArgs = commandParser.parse_known_args(unknownArgs) join(args, subArgs) if 'debug' in args: logger.DEBUG = args.debug dargs = vars(args) logger.debug('Arguments: %s | unknownArgs: %s' %({arg:dargs[arg] for arg in dargs if dargs[arg]}, unknownArgs)) #report unknown arguments #but not if we run aliasParser next because we will parse again in there if unknownArgs and not ('func' in args and args.func is aliasParser): mainParser.error('Unknown Argument(s): %s' % ', '.join(unknownArgs)) return args def printHeader(): """ Print application name, version and legal notes. """ version = config.Config.VERSION # Git info is now only shown via --diagnostics # ref, hashid = tools.gitRevisionAndHash() # if ref: # version += " git branch '{}' hash '{}'".format(ref, hashid) print('') print('Back In Time') print('Version: ' + version) print('') print('Back In Time comes with ABSOLUTELY NO WARRANTY.') print('This is free software, and you are welcome to redistribute it') print("under certain conditions; type `backintime --license' for details.") print('') class PseudoAliasAction(argparse.Action): """ Translate '--COMMAND' into 'COMMAND' for backwards compatibility. """ def __call__(self, parser, namespace, values, option_string=None): """ Translate '--COMMAND' into 'COMMAND' for backwards compatibility. Args: parser (argparse.ArgumentParser): NotImplemented namespace (argparse.Namespace): Namespace that should get modified values: NotImplemented option_string: NotImplemented """ #TODO: find a more elegant way to solve this dest = self.dest.replace('_', '-') if self.dest == 'b': replace = '-b' alias = 'backup' else: replace = '--%s' % dest alias = dest setattr(namespace, 'func', aliasParser) setattr(namespace, 'replace', replace) setattr(namespace, 'alias', alias) def aliasParser(args): """ Call commands which where given with leading -- for backwards compatibility. Args: args (argparse.Namespace): previously parsed arguments """ if not args.quiet: logger.info("Run command '%(alias)s' instead of argument '%(replace)s' due to backwards compatibility." % {'alias': args.alias, 'replace': args.replace}) argv = [w.replace(args.replace, args.alias) for w in sys.argv[1:]] newArgs = argParse(argv) if 'func' in dir(newArgs): newArgs.func(newArgs) def getConfig(args, check = True): """ Load config and change to profile selected on commandline. Args: args (argparse.Namespace): previously parsed arguments check (bool): if ``True`` check if config is valid Returns: config.Config: current config with requested profile selected Raises: SystemExit: 1 if ``profile`` or ``profile_id`` is no valid profile 2 if ``check`` is ``True`` and config is not configured """ cfg = config.Config(config_path = args.config, data_path = args.share_path) logger.debug('config file: %s' % cfg._LOCAL_CONFIG_PATH) logger.debug('share path: %s' % cfg._LOCAL_DATA_FOLDER) logger.debug('profiles: %s' % ', '.join('%s=%s' % (x, cfg.profileName(x)) for x in cfg.profiles())) if 'profile_id' in args and args.profile_id: if not cfg.setCurrentProfile(args.profile_id): logger.error('Profile-ID not found: %s' % args.profile_id) sys.exit(RETURN_ERR) if 'profile' in args and args.profile: if not cfg.setCurrentProfileByName(args.profile): logger.error('Profile not found: %s' % args.profile) sys.exit(RETURN_ERR) if check and not cfg.isConfigured(): logger.error('%(app)s is not configured!' %{'app': cfg.APP_NAME}) sys.exit(RETURN_NO_CFG) if 'checksum' in args: cfg.forceUseChecksum = args.checksum return cfg def setQuiet(args): """ Redirect :py:data:`sys.stdout` to ``/dev/null`` if ``--quiet`` was set on commandline. Return the original :py:data:`sys.stdout` file object which can be used to print absolute necessary information. Args: args (argparse.Namespace): previously parsed arguments Returns: sys.stdout: default sys.stdout """ force_stdout = sys.stdout if args.quiet: # do not replace with subprocess.DEVNULL - will not work sys.stdout = open(os.devnull, 'w') atexit.register(sys.stdout.close) atexit.register(force_stdout.close) return force_stdout class printLicense(argparse.Action): """ Print custom license """ def __init__(self, *args, **kwargs): super(printLicense, self).__init__(*args, **kwargs) def __call__(self, *args, **kwargs): cfg = config.Config() print(cfg.license()) sys.exit(RETURN_OK) 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-Remove from Terminal. Args: args (argparse.Namespace): previously parsed arguments Raises: SystemExit: 0 if okay 2 if Smart-Remove 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 Remove will remove {} snapshots'.format(len(del_snapshots))) sn.smartRemove(del_snapshots, log = logger.info) _umount(cfg) sys.exit(RETURN_OK) else: logger.error('Smart Remove is not configured.') sys.exit(RETURN_NO_CFG) 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.4.3/common/bash-completion/000077500000000000000000000000001455673541400202175ustar00rootroot00000000000000backintime-1.4.3/common/bash-completion/backintime000066400000000000000000000066371455673541400222640ustar00rootroot00000000000000#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" 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.4.3/common/bcolors.py000066400000000000000000000022131455673541400171460ustar00rootroot00000000000000#!/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.4.3/common/cli.py000066400000000000000000000154711455673541400162640ustar00rootroot00000000000000# -*- 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, termios, struct return [int(x) for x in struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))] except: 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.4.3/common/config-example-local000066400000000000000000000045051455673541400210500ustar00rootroot00000000000000profile1.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.4.3/common/config-example-ssh000066400000000000000000000051741455673541400205560ustar00rootroot00000000000000profile1.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.4.3/common/config.py000066400000000000000000002243131455673541400167570ustar00rootroot00000000000000# 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 shlex try: import pwd except ImportError: import getpass pwd = None # 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 from exceptions import PermissionDeniedByPolicy, \ InvalidChar, \ InvalidCmd, \ LimitExceeded class Config(configfile.ConfigFileWithProfiles): APP_NAME = 'Back In Time' VERSION = '1.4.3' 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): # Note: The main profiles name here is translated using the systems # current locale because the language code in the config file wasn't # read yet. configfile.ConfigFileWithProfiles.__init__(self, _('Main profile')) self._APP_PATH = tools.backintimePath() self._DOC_PATH = os.path.join(tools.sharePath(), 'doc', 'backintime-common') if os.path.exists(os.path.join(self._APP_PATH, 'LICENSE')): self._DOC_PATH = self._APP_PATH self._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) 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(self.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') 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, '{} {}'.format(_('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 user(self): """ portable way to get username cc by-sa 3.0 http://stackoverflow.com/a/19865396/1139841 author: techtonik http://stackoverflow.com/users/239247/techtonik """ if pwd: return pwd.getpwuid(os.geteuid()).pw_name else: return getpass.getuser() 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', self.user(), 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 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 """ 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) # remote port if port: ssh += ['-p', str(self.sshPort(profile_id))] # cipher used to transfer data c = self.sshCipher(profile_id) if cipher and c != 'default': ssh += ['-o', 'Ciphers={}'.format(c)] # 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 = list) # add the command if cmd: ssh += cmd # close quote if quote and cmd: ssh.append("'") 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 = self.user() 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 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 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): if cmd_type == list: if self.sshPrefixEnabled(profile_id): return shlex.split(self.sshPrefix(profile_id)) else: return [] if cmd_type == str: if self.sshPrefixEnabled(profile_id): return self.sshPrefix(profile_id).strip() + ' ' else: return '' 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 appPath(self): return self._APP_PATH def docPath(self): return self._DOC_PATH def appInstanceFile(self): return os.path.join(self._LOCAL_DATA_FOLDER, 'app.lock') def fileId(self, profile_id = None): 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 anacrontab(self, suffix = ''): """ Deprecated since 1.1. Just keep this to delete old anacrontab files """ return os.path.join(self._LOCAL_CONFIG_FOLDER, 'anacrontab' + suffix) def anacrontabFiles(self): """ list existing old anacrontab files """ dirname, basename = os.path.split(self.anacrontab()) for f in os.listdir(dirname): if f.startswith(basename): yield os.path.join(dirname, f) def anacronSpool(self): return os.path.join(self._LOCAL_DATA_FOLDER, 'anacron') def anacronSpoolFile(self, profile_id = None): return os.path.join(self.anacronSpool(), self.anacronJobIdentify(profile_id)) def anacronJobIdentify(self, profile_id = None): if not profile_id: profile_id = self.currentProfile() profile_name = self.profileName(profile_id) return profile_id + '_' + profile_name.replace(' ', '_') def udevRulesPath(self): return os.path.join('/etc/udev/rules.d', '99-backintime-%s.rules' % self.user()) 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 license(self): return tools.readFile(os.path.join(self.docPath(), 'LICENSE'), '') def translations(self): return tools.readFile(os.path.join(self.docPath(), 'TRANSLATIONS'), '') def authors(self): return tools.readFile(os.path.join(self.docPath(), 'AUTHORS'), '') def changelog(self): for i in ('CHANGES', 'changelog'): f = os.path.join(self.docPath(), i) clog = tools.readFile(f, '') if clog: return clog return '' 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 profile is supposed to be run this time """ if self.scheduleMode(profile_id) not in (self.REPEATEDLY, self.UDEV): return True #if crontab wasn't updated since upgrading BIT to version without anacron #we are most likely started by anacron and should run this task without asking. if list(self.anacrontabFiles()): 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 SYSTEM_ENTRY_MESSAGE \ = "#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. """ def setupCron(self): for f in self.anacrontabFiles(): logger.debug("Clearing anacrontab %s" %f, self) os.remove(f) self.setupUdev.clean() oldCrontab = tools.readCrontab() strippedCrontab = self.removeOldCrontab(oldCrontab) newCrontab = self.createNewCrontab(strippedCrontab) if not isinstance(newCrontab, (list, tuple)): return newCrontab #save Udev rules try: if self.setupUdev.isReady and self.setupUdev.save(): logger.debug('Udev rules added successfully', self) except PermissionDeniedByPolicy as e: logger.error(str(e), self) self.notifyError(str(e)) return False if not newCrontab == oldCrontab: if not tools.checkCommand('crontab'): if self.scheduleMode() is self.NONE: return True else: logger.error('crontab not found.', self) self.notifyError(_( "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups.")) return False if not tools.writeCrontab(newCrontab): self.notifyError(_('Failed to write new crontab.')) return False else: logger.debug("Crontab didn't change. Skip writing.") return True def removeOldCrontab(self, crontab): #We have to check if the self.SYSTEM_ENTRY_MESSAGE is in use, #if not then the entries are most likely from Back In Time 0.9.26 #or earlier. if not self.SYSTEM_ENTRY_MESSAGE in crontab: #Then the system entry message has not yet been used in this crontab #therefore we assume all entries are system entries and clear them all. #This is the old behavior logger.debug("Clearing all Back In Time entries", self) return [x for x in crontab if not 'backintime' in x] else: #clear all line peers which have a SYSTEM_ENTRY_MESSAGE followed by #one backintime command line logger.debug("Clearing system Back In Time entries", self) delLines = [] for i, line in enumerate(crontab): if self.SYSTEM_ENTRY_MESSAGE in line and \ len(crontab) > i + 1 and \ 'backintime' in crontab[i + 1]: delLines.extend((i, i + 1)) return [line for i, line in enumerate(crontab) if i not in delLines] def createNewCrontab(self, oldCrontab): newCrontab = oldCrontab[:] if not tools.checkCommand('backintime'): logger.error("Command 'backintime' not found", self) return newCrontab for profile_id in self.profiles(): cronLine = self.cronLine(profile_id) if not isinstance(cronLine, str): return cronLine if cronLine: newCrontab.append(self.SYSTEM_ENTRY_MESSAGE) newCrontab.append(cronLine.replace('{cmd}', self.cronCmd(profile_id))) if newCrontab == oldCrontab: # Leave one self.SYSTEM_ENTRY_MESSAGE in to prevent deleting of manual # entries if there is no automatic entry. newCrontab.append(self.SYSTEM_ENTRY_MESSAGE) newCrontab.append("#Please don't delete these two lines, or all custom backintime " "entries will be deleted next time you call the gui options!") return newCrontab def cronLine(self, profile_id): cron_line = '' profile_name = self.profileName(profile_id) backup_mode = self.scheduleMode(profile_id) logger.debug( 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.cronCmd(profile_id), uuid) except (InvalidChar, InvalidCmd, LimitExceeded) as e: logger.error(str(e), self) self.notifyError(str(e)) return False elif self.WEEK == backup_mode: cron_line = '%s %s * * %s {cmd}' %(minute, hour, weekday) elif self.MONTH == backup_mode: cron_line = '%s %s %s * * {cmd}' %(minute, hour, day) elif self.YEAR == backup_mode: cron_line = '%s %s 1 1 * {cmd}' %(minute, hour) return cron_line def cronCmd(self, profile_id): if not tools.checkCommand('backintime'): logger.error("Command 'backintime' not found", self) return cmd = tools.which('backintime') + ' ' if profile_id != '1': cmd += '--profile-id %s ' % profile_id if not self._LOCAL_CONFIG_PATH is self._DEFAULT_CONFIG_PATH: cmd += '--config %s ' % self._LOCAL_CONFIG_PATH if logger.DEBUG: cmd += '--debug ' cmd += 'backup-job' if self.redirectStdoutInCron(profile_id): cmd += ' >/dev/null' if self.redirectStderrInCron(profile_id): if self.redirectStdoutInCron(profile_id): cmd += ' 2>&1' else: cmd += ' 2>/dev/null' if self.ioniceOnCron(profile_id) and tools.checkCommand('ionice'): cmd = tools.which('ionice') + ' -c2 -n7 ' + cmd 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.4.3/common/configfile.py000066400000000000000000000610321455673541400176140ustar00rootroot00000000000000# 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: all available profile IDs as strings """ return self.strValue('profiles', '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 givin 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.4.3/common/configure000077500000000000000000000246611455673541400170530ustar00rootroot00000000000000#!/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 -s $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\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} 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 "../debian/copyright" "/share/doc/backintime-common" addInstallFile "../AUTHORS" "/share/doc/backintime-common" addInstallFile "../LICENSE" "/share/doc/backintime-common" addInstallFile "../README.md" "/share/doc/backintime-common" addInstallFile "../TRANSLATIONS" "/share/doc/backintime-common" addInstallFile "../VERSION" "/share/doc/backintime-common" addInstallFile "../CHANGES" "/share/doc/backintime-common" 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.4.3/common/create-manpage-backintime-config.py000066400000000000000000000273201455673541400237310ustar00rootroot00000000000000#!/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(), os.path.dirname(sys.argv[0])) CONFIG = os.path.join(PATH, 'config.py') MAN = os.path.join(PATH, 'man/C/backintime-config.1') with open(os.path.join(PATH, '../VERSION'), 'r') as f: VERSION = f.read().strip('\n') SORT = True # True = sort by alphabet; False = sort by line numbering c_list = re.compile(r'.*?self\.(?!set)((?:profile)?)(List)Value ?\( ?[\'"](.*?)[\'"], ?((?:\(.*\)|[^,]*)), ?[\'"]?([^\'",\)]*)[\'"]?') c = re.compile(r'.*?self\.(?!set)((?:profile)?)(.*?)Value ?\( ?[\'"](.*?)[\'"] ?(%?[^,]*?), ?[\'"]?([^\'",\)]*)[\'"]?') 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.4.3/common/diagnostics.py000066400000000000000000000324361455673541400200240ustar00rootroot00000000000000"""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 # config.Config.VERSION Refactor after src-layout migration import tools 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 = {} pwd_struct = pwd.getpwuid(os.getuid()) # === BACK IN TIME === # work-around: Instantiate to get the user-callback folder # (should be singleton) cfg = config.Config() result['backintime'] = { 'name': config.Config.APP_NAME, 'version': config.Config.VERSION, '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), 'running-as-root': pwd_struct.pw_name == 'root', 'user-callback': cfg.takeSnapshotUserCallback(), 'keyring-supported': tools.keyringSupported() } # Git repo bit_root_path = Path(tools.backintimePath("")) git_info = 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'] = { # Kernel & Architecture 'platform': platform.platform(), # OS Version (and maybe name) 'system': '{} {}'.format(platform.system(), platform.version()), # OS Release name (prettier) 'OS': _get_os_release() } # 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 = '{} {} {} {}'.format( 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 = '{} branch: {}'.format(python, branch) rev = platform.python_revision() if rev: python = '{} rev: {}'.format(python, rev) python_executable = Path(sys.executable) # Python interpreter result['python-setup'] = { 'python': python, 'python-executable': str(python_executable), 'python-executable-symlink': python_executable.is_symlink(), } # Real interpreter path if it is used via a symlink if result['python-setup']['python-executable-symlink']: result['python-setup']['python-executable-resolved'] \ = str(python_executable.resolve()) result['python-setup']['sys.path'] = sys.path result['python-setup']['qt'] = _get_qt_information() # === EXTERN TOOL === result['external-programs'] = {} # rsync # 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 result['external-programs']['rsync'] = _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 result['external-programs']['rsync']: # try the old way result['external-programs']['rsync'] = _get_extern_versions( ['rsync', '--version'], r'rsync version (.*) protocol version' ) elif isinstance(result['external-programs']['rsync'], 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 result['external-programs']['rsync'][key] except KeyError: pass # 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_struct.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. """ try: import PyQt5.QtCore import PyQt5.QtGui import PyQt5.QtWidgets except ImportError: return '(Cannot import PyQt5)' # Themes theme_info = {} if tools.checkXServer(): # TODO use tools.is_Qt5_working() when stable qapp = PyQt5.QtWidgets.QApplication([]) theme_info = { 'Theme': PyQt5.QtGui.QIcon.themeName(), 'Theme Search Paths': PyQt5.QtGui.QIcon.themeSearchPaths(), 'Fallback Theme': PyQt5.QtGui.QIcon.fallbackThemeName(), 'Fallback Search Paths': PyQt5.QtGui.QIcon.fallbackSearchPaths() } qapp.quit() return { 'Version': 'PyQt {} / Qt {}'.format(PyQt5.QtCore.PYQT_VERSION_STR, PyQt5.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_git_repository_info(path=None): """Return the current branch and last commit hash. Credits: https://stackoverflow.com/a/51224861/4865723 Args: path (Path): Path with '.git' folder in (default is current working directory). Returns: (dict): Dict with keys "branch" and "hash" if it is a git repo, otherwise an `None`. """ if not path: path = Path.cwd() 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 val.startswith('ref: '): result['branch'] = '/'.join(val.split('/')[2:]).strip() else: result['branch'] = '(detached HEAD)' result['hash'] = val return result # commit hash with (git_folder / 'refs' / 'heads' / result['branch']) \ .open('r') as handle: result['hash'] = handle.read().strip() return result 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.4.3/common/doc-dev/000077500000000000000000000000001455673541400164545ustar00rootroot00000000000000backintime-1.4.3/common/doc-dev/1_doc_maintenance_howto.md000066400000000000000000000127161455673541400235540ustar00rootroot00000000000000# 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) - [Why to use Sphinx to generate the documentation?](#why-to-use-sphinx-to-generate-the-documentation) - [How to build and view the documentation](#how-to-build-and-view-the-documentation) - [How to add new modules to the documentation](#how-to-add-new-modules-to-the-documentation) - [How to write docstrings for Back In Time](#how-to-write-docstrings-for-back-in-time) - [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 the tool Sphinx (https://www.sphinx-doc.org/en/master/) together with the Sphinx-Extensions - autodoc (to automatically generate rst doc files from the python docstrings) https://www.sphinx-doc.org/en/master/man/sphinx-apidoc.html - napoleon (to convert google-style docstrings to reStructuredText "rst" format required for autodoc) https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html - viewcode (to create links to browse the highlighted source code) https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html For a brief introduction to Sphinx for Python see: https://betterprogramming.pub/auto-documenting-a-python-project-using-sphinx-8878f9ddc6e9 For a reference of rst markups see: https://docutils.sourceforge.io/docs/user/rst/quickref.html For a description of the Google coding style for python see: https://google.github.io/styleguide/pyguide.html # Why to use Sphinx to generate the documentation? Sphinx has eg. the advantage to - generate the documentation in different formats (eg. html, PDF or Linux man pages). - report invalid markups contained in the documentation - inject documentation of attributes and methods from parent classes into sub classes (in case of inheritance) # How to build and view the documentation Open a terminal in the "doc-dev" folder and call make html # to generate the HTML documentation make htmlOpen # to open the browser showing the generated HTML pages # How to add new modules to the documentation There are two scenarios here: a) The 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 doc-dev/conf.py file so that autodoc can 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 to 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 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")! # 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` for an extended example](https://www.sphinx-doc.org/en/master/usage/extensions/example_google.html#example-google). # 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` Important: Don't forget to surround the function name with back ticks otherwise Sphinx will not create a cross reference silently! - Specify the python type of an method/function argument: Add the type name (with namespace if not in the same) in parentheses Args: cfg (config.Config): Current configuration - Indicate python keywords (without cross-referencing them) ``True`` ``None`` Surround the keyword with two backticks (it will be shown as code then) # Known issues with documentation generation - 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" March 2023 backintime-1.4.3/common/doc-dev/2_from_weblate_01.png000066400000000000000000003076721455673541400223700ustar00rootroot00000000000000PNG  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. Weblate ["Repository maintenance"](https://translate.codeberg.org/projects/backintime/#repository): 1. Press "Commit". 2. "Lock" the project. 2. git: Start a new branch. 3. Download and integrate Weblate into the git repository via `./update_language_files.py weblate`. 4. Check via `git status` or `git diff`. The `po`-files (not `pot`!) in `common/po` and the file `common/languages.py` should be modified. 5. Commit. 6. Scan `py`-files for modified source strings via `./update_language_files.py source`. 7. Check via `git status` or `git diff`. The file `messages.pot` and all `po`-files should be modified. 8. Commit. 9. Create PR and merge into "dev". 10. 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). # 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.4.3/common/doc-dev/2_to_weblate_01.png000066400000000000000000001143061455673541400220350ustar00rootroot00000000000000PNG  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.4.3/common/doc-dev/2_to_weblate_03.png000066400000000000000000001053261455673541400220410ustar00rootroot00000000000000PNG  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.4.3/common/doc-dev/2_weblate_setup_01.png000066400000000000000000004133711455673541400225570ustar00rootroot00000000000000PNG  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.4.3/common/doc-dev/2_weblate_setup_02.png000066400000000000000000001755741455673541400225720ustar00rootroot00000000000000PNG  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.4.3/common/doc-dev/2_weblate_setup_03.png000066400000000000000000001072121455673541400225530ustar00rootroot00000000000000PNG  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.4.3/common/doc-dev/2_weblate_setup_07.png000066400000000000000000001051361455673541400225620ustar00rootroot00000000000000PNG  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.4.3/common/doc-dev/2_weblate_setup_08.png000066400000000000000000001262731455673541400225700ustar00rootroot00000000000000PNG  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.4.3/common/doc-dev/3_How_to_set_up_openssh_server_for_ssh_unit_tests.md000066400000000000000000000152331455673541400311730ustar00rootroot00000000000000# 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.4.3/common/doc-dev/4_Control_files_usage_(locks_flocks_logs_and_others).md000066400000000000000000001034041455673541400313200ustar00rootroot00000000000000# 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.4.3/common/doc-dev/BiT_release_process.md000066400000000000000000000247641455673541400227270ustar00rootroot00000000000000# 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)). - `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/config.py` - Sphinx config in `common/doc-dev/conf.py` - man pages in `common/man/C/backintime*.1` and `qt/man/C/backintime*.1` - changelog to build a debian package in `debian/changelog` (this will be deprecated once we give up or separate the packaging for distros) - 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" ``` - Optional: Search for all "copyright" strings in the code to update the year and add missing major contributors Eg.: - common/config.py There is also script `updatecopyright.sh` in the project's root folder which updates the copyright dates in all files but this script needs an overhaul to be able to insert new contributors too... - 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 - Copyright: Names and year - 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 ``` - 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. - 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 2023backintime-1.4.3/common/doc-dev/Makefile000066400000000000000000000152601455673541400201200ustar00rootroot00000000000000# 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.4.3/common/doc-dev/README.md000066400000000000000000000010251455673541400177310ustar00rootroot00000000000000# 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.4.3/common/doc-dev/_static/000077500000000000000000000000001455673541400201025ustar00rootroot00000000000000backintime-1.4.3/common/doc-dev/_static/.dummy000066400000000000000000000000001455673541400212240ustar00rootroot00000000000000backintime-1.4.3/common/doc-dev/_templates/000077500000000000000000000000001455673541400206115ustar00rootroot00000000000000backintime-1.4.3/common/doc-dev/_templates/.dummy000066400000000000000000000000001455673541400217330ustar00rootroot00000000000000backintime-1.4.3/common/doc-dev/applicationinstance.rst000066400000000000000000000002241455673541400232340ustar00rootroot00000000000000applicationinstance module ========================== .. automodule:: applicationinstance :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/askpass.rst000066400000000000000000000001601455673541400206500ustar00rootroot00000000000000askpass module ============== .. automodule:: askpass :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/backintime.rst000066400000000000000000000001711455673541400213130ustar00rootroot00000000000000backintime module ================= .. automodule:: backintime :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/bcolors.rst000066400000000000000000000001601455673541400206460ustar00rootroot00000000000000bcolors module ============== .. automodule:: bcolors :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/cli.rst000066400000000000000000000001441455673541400177540ustar00rootroot00000000000000cli module ========== .. automodule:: cli :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/conf.py000066400000000000000000000213431455673541400177560ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # BackInTime documentation build configuration file, created by # sphinx-quickstart on Sat Jan 9 00:04:35 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # 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 config to solve race conditions between config an mount import config # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', 'sphinx_rtd_theme' ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # 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". version = '1.4.3' # The full version, including alpha/beta/rc tags. release = version # '1.3.3-dev' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Intersphinx options -------------------------------------------------- intersphinx_mapping = { 'python': ('https://docs.python.org', None), } # -- 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' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y, %H:%M (%Z)' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # 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'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'backintime', 'Back In Time Development Documentation', ['Germar Reitze'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (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'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. backintime-1.4.3/common/doc-dev/config.rst000066400000000000000000000001551455673541400204540ustar00rootroot00000000000000config module ============= .. automodule:: config :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/configfile.rst000066400000000000000000000001711455673541400213120ustar00rootroot00000000000000configfile module ================= .. automodule:: configfile :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/diagnostics.rst000066400000000000000000000001741455673541400215170ustar00rootroot00000000000000diagnostics module ================== .. automodule:: diagnostics :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/driveinfo.rst000066400000000000000000000001661455673541400211760ustar00rootroot00000000000000driveinfo module ================ .. automodule:: driveinfo :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/encfstools.rst000066400000000000000000000001711455673541400213640ustar00rootroot00000000000000encfstools module ================= .. automodule:: encfstools :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/exceptions.rst000066400000000000000000000001711455673541400213660ustar00rootroot00000000000000exceptions module ================= .. automodule:: exceptions :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/guiapplicationinstance.rst000066400000000000000000000002351455673541400237430ustar00rootroot00000000000000guiapplicationinstance module ============================= .. automodule:: guiapplicationinstance :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/index.rst000066400000000000000000000007331455673541400203200ustar00rootroot00000000000000.. 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 BackInTime's documentation! ====================================== Contents: .. toctree:: :maxdepth: 2 modules.rst plugins/modules.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` backintime-1.4.3/common/doc-dev/logger.rst000066400000000000000000000001551455673541400204660ustar00rootroot00000000000000logger module ============= .. automodule:: logger :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/modules.rst000066400000000000000000000005431455673541400206600ustar00rootroot00000000000000common ====== .. toctree:: :maxdepth: 4 applicationinstance askpass backintime bcolors cli config configfile diagnostics driveinfo encfstools exceptions guiapplicationinstance logger mount password password_ipc pluginmanager progress snapshotlog snapshots sshMaxArg sshtools tools backintime-1.4.3/common/doc-dev/mount.rst000066400000000000000000000001521455673541400203460ustar00rootroot00000000000000mount module ============ .. automodule:: mount :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/password.rst000066400000000000000000000001631455673541400210500ustar00rootroot00000000000000password module =============== .. automodule:: password :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/password_ipc.rst000066400000000000000000000001771455673541400217100ustar00rootroot00000000000000password_ipc module =================== .. automodule:: password_ipc :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/pluginmanager.rst000066400000000000000000000002021455673541400220310ustar00rootroot00000000000000pluginmanager module ==================== .. automodule:: pluginmanager :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/plugins/000077500000000000000000000000001455673541400201355ustar00rootroot00000000000000backintime-1.4.3/common/doc-dev/plugins/modules.rst000066400000000000000000000001051455673541400223330ustar00rootroot00000000000000plugins ======= .. toctree:: :maxdepth: 4 usercallbackplugin backintime-1.4.3/common/doc-dev/plugins/usercallbackplugin.rst000066400000000000000000000002211455673541400245340ustar00rootroot00000000000000usercallbackplugin module ========================= .. automodule:: usercallbackplugin :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/progress.rst000066400000000000000000000001631455673541400210520ustar00rootroot00000000000000progress module =============== .. automodule:: progress :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/snapshotlog.rst000066400000000000000000000001741455673541400215510ustar00rootroot00000000000000snapshotlog module ================== .. automodule:: snapshotlog :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/snapshots.rst000066400000000000000000000001661455673541400212330ustar00rootroot00000000000000snapshots module ================ .. automodule:: snapshots :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/sshMaxArg.rst000066400000000000000000000001661455673541400211060ustar00rootroot00000000000000sshMaxArg module ================ .. automodule:: sshMaxArg :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/sshtools.rst000066400000000000000000000001631455673541400210640ustar00rootroot00000000000000sshtools module =============== .. automodule:: sshtools :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/doc-dev/tools.rst000066400000000000000000000001521455673541400203440ustar00rootroot00000000000000tools module ============ .. automodule:: tools :members: :undoc-members: :show-inheritance: backintime-1.4.3/common/driveinfo.py000066400000000000000000000127561455673541400175050ustar00rootroot00000000000000# 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 configfile import sys import tools #TODO: rewrite and finally USE this class DriveInfo(configfile.ConfigFile): def __init__(self, path): configfile.ConfigFile.__init__(self) self.path = path self.load(self._get_driveinfo_file_()) dirty = False if sys.platform == 'win32': #there is nothing to do pass else: if not self.hasKey('hardlinks'): self.setBoolValue('hardlinks', self._check_hardlinks_()) dirty = True if not self.hasKey('permissions'): self.setBoolValue('permissions', self._check_perms_()) dirty = True if not self.hasKey('usergroup'): self.setBoolValue('usergroup', self._check_usergroup_()) dirty = True if dirty: self.save(self._get_driveinfo_file_()) def support_hardlinks(self): return self.boolValue('hardlinks', False) def support_permissions(self): return self.boolValue('permissions', False) def support_usergroup(self): return self.boolValue('usergroup', False) def _get_driveinfo_file_(self): return os.path.join(self.path, 'driveinfo') def _check_hardlinks_(self): tmp_path = os.path.join(self.path, 'driveinfo.tmp') tools.makeDirs(tmp_path) if not os.path.isdir(tmp_path): return False file1_path = os.path.join(tmp_path, 'file1') file2_path = os.path.join(tmp_path, 'file2') ret_val = False os.system("echo abc > \"%s\"" % file1_path) os.system("ln \"%s\" \"%s\"" % (file1_path, file2_path)) os.system("echo abc > \"%s\"" % file2_path) if os.path.exists(file1_path) and os.path.exists(file2_path): try: info1 = os.stat(file1_path) info2 = os.stat(file2_path) if info1.st_size == info2.st_size: ret_val = True except: pass os.system("rm -rf \"%s\"" % tmp_path) return ret_val def _check_perms_for_file_(self, file_path, mode): ret_val = False os.system("chmod %s \"%s\"" % (mode, file_path)) try: info = "%o" % os.stat(file_path).st_mode info = info[-3 :] if info == mode: ret_val = True except: pass return ret_val def _check_perms_(self): tmp_path = os.path.join(self.path, 'driveinfo.tmp') tools.makeDirs(tmp_path) if not os.path.isdir(tmp_path): return False file_path = os.path.join(tmp_path, 'file') os.system("echo abc > \"%s\"" % file_path) if not os.path.isfile(file_path): return False ret_val = False if self._check_perms_for_file_(file_path, '111'): if self._check_perms_for_file_(file_path, '700'): if self._check_perms_for_file_(file_path, '600'): if self._check_perms_for_file_(file_path, '711'): if self._check_perms_for_file_(file_path, '300'): if self._check_perms_for_file_(file_path, '666'): ret_val = True os.system("rm -rf \"%s\"" % tmp_path) return ret_val def _check_usergroup_(self): tmp_path = os.path.join(self.path, 'driveinfo.tmp') tools.makeDirs(tmp_path) if not os.path.isdir(tmp_path): return False file_path = os.path.join(tmp_path, 'file') os.system("echo abc > \"%s\"" % file_path) if not os.path.isfile(file_path): return False ret_val = False uid = os.getuid() gid = os.getgid() try: info = os.stat(file_path) if info.st_uid == uid and info.st_gid == gid: ret_val = True except: pass if ret_val and uid == 0: #try to change the group import grp #search for another group new_gid = gid new_name = '' for group in grp.getgrall(): if group.gr_gid != gid: new_gid = group.gr_gid new_name = group.gr_name break if new_gid != gid: os.system("chgrp %s \"%s\"" % (new_name, file_path)) try: info = os.stat(file_path) if info.st_gid != new_gid: ret_val = False except: ret_val = False os.system("rm -rf \"%s\"" % tmp_path) return ret_val backintime-1.4.3/common/encfstools.py000066400000000000000000000674551455673541400177050ustar00rootroot00000000000000# 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 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 """ 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 whit 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.4.3/common/exceptions.py000066400000000000000000000034261455673541400176730ustar00rootroot00000000000000# 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.4.3/common/guiapplicationinstance.py000066400000000000000000000051501455673541400222430ustar00rootroot00000000000000# 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.4.3/common/languages.py000066400000000000000000002054711455673541400174640ustar00rootroot00000000000000# Generated at Tue Jan 30 11:25:23 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': 47, 'bg': 92, 'bs': 18, 'ca': 98, 'cs': 65, 'da': 75, 'de': 99, 'el': 43, 'en': 100, 'eo': 58, 'es': 99, 'et': 15, 'eu': 99, 'fa': 82, 'fi': 54, 'fo': 16, 'fr': 92, 'gl': 71, 'he': 63, 'hr': 20, 'hu': 26, 'id': 99, 'is': 75, 'it': 99, 'ja': 99, 'ko': 99, 'lt': 70, 'nb': 99, 'nl': 93, 'nn': 51, 'pl': 65, 'pt': 51, 'pt_BR': 99, 'ro': 99, 'ru': 94, 'sk': 31, 'sl': 99, 'sr': 39, 'sv': 70, 'th': 57, 'tr': 99, 'uk': 99, 'vi': 71, 'zh_CN': 75, 'zh_TW': 15} backintime-1.4.3/common/logger.py000066400000000000000000000136161455673541400167730ustar00rootroot00000000000000# 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: for line in wrapLine(message): syslog.syslog(level, '{}{}: {}'.format( SYSLOG_MESSAGE_PREFIX, _level_names[level], line)) 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) # This function was moved from tools.py to here to solve a circular # import dependency between "tools" and "logger". def wrapLine(msg, size=950, delimiters='\t ', new_line_indicator = 'CONTINUE: '): """ Wrap line ``msg`` into multiple lines with each shorter than ``size``. Try to break the line on ``delimiters``. New lines will start with ``new_line_indicator``. Args: msg (str): string that should get wrapped size (int): maximum length of returned strings delimiters (str): try to break ``msg`` on these characters new_line_indicator (str): start new lines with this string Yields: str: lines with max ``size`` length """ # TODO Use "textwrap.wrap" instead (https://docs.python.org/3/library/textwrap.html) # (which may change the output formatting and may affect unit tests then) # To avoid duplicated argument values in calls this function could # act as a wrapper- if len(new_line_indicator) >= size - 1: new_line_indicator = '' while msg: if len(msg) <= size: yield(msg) break else: line = '' for look in range(size-1, size//2, -1): if msg[look] in delimiters: line, msg = msg[:look+1], new_line_indicator + msg[look+1:] break if not line: line, msg = msg[:size], new_line_indicator + msg[size:] yield(line) backintime-1.4.3/common/man/000077500000000000000000000000001455673541400157065ustar00rootroot00000000000000backintime-1.4.3/common/man/C/000077500000000000000000000000001455673541400160705ustar00rootroot00000000000000backintime-1.4.3/common/man/C/backintime-askpass.1000066400000000000000000000016421455673541400217260ustar00rootroot00000000000000.TH backintime-askpass 1 "Jan 2024" "version 1.4.3" "USER COMMANDS" .SH NAME backintime-askpass \- a simple backup tool for Linux. .PP This is the command line tool for piping passwords into ssh/sshfs and encfs. .SH SYNOPSIS .B backintime-askpass .SH DESCRIPTION Back In Time is a simple backup tool for Linux. This is a helper tool for piping passwords into ssh/sshfs and encfs. Options will will be read from environ variables. It doesn't provide any useful enduser service. .SH ENVIRON .TP ASKPASS_PROFILE_ID Back In Time Profile-ID. .TP ASKPASS_MODE Backup mode (or backend). Take a look at 'man backintime-config' section \fIprofile.snapshots.mode\fR .TP ASKPASS_TEMP Temp FIFO socket used to pipe the password .SH SEE ALSO backintime, backintime-config. .PP Back In Time also has a website: https://github.com/bit-team/backintime .SH AUTHOR This manual page was written by BIT Team(). backintime-1.4.3/common/man/C/backintime-config.1000066400000000000000000000444711455673541400215350ustar00rootroot00000000000000.TH backintime-config 1 "Jan 2024" "version 1.4.3" "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.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 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(). backintime-1.4.3/common/man/C/backintime.1000066400000000000000000000325051455673541400202650ustar00rootroot00000000000000.TH backintime 1 "Jan 2024" "version 1.4.3" "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 There was a paid security audit for EncFS in Feb 2014 which revealed several potential vulnerabilities. .TP From https://defuse.ca/audits/encfs.htm EncFS is probably safe as long as the adversary only gets one copy of the ciphertext and nothing more. EncFS is not safe if the adversary has the opportunity to see two or more snapshots of the ciphertext at different times. EncFS attempts to protect files from malicious modification, but there are serious problems with this feature. .PP This might be a problem with Back In Time snapshots. .SH SEE ALSO backintime-qt, backintime-config. .PP Back In Time also has a website: https://github.com/bit-team/backintime .SH AUTHOR This manual page was written by BIT Team(). backintime-1.4.3/common/mount.py000066400000000000000000001171161455673541400166560ustar00rootroot00000000000000# 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 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 = self.config.user() 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: raise MountException( _("Can't unmount {mountprocess} from {mountpoint}.") .format(mountprocess=self.mountproc, mountpoint=self.currentMountpoint)) 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( _('{} not found. Please install e.g. {}') .format(self.mountproc, 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 {} not empty.') .format(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.4.3/common/password.py000066400000000000000000000261321455673541400173530ustar00rootroot00000000000000# 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. This does even work when run as cronjob and user is logged in. """ if prompt is None: """ Profile {name}: Enter password for {mode} """ prompt = _("Profile '{profile}': Enter password for {mode}: ") \ .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 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.4.3/common/password_ipc.py000066400000000000000000000100431455673541400202000ustar00rootroot00000000000000# 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.4.3/common/pluginmanager.py000066400000000000000000000273561455673541400203530ustar00rootroot00000000000000# 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.4.3/common/plugins/000077500000000000000000000000001455673541400166145ustar00rootroot00000000000000backintime-1.4.3/common/plugins/usercallbackplugin.py000066400000000000000000000110771455673541400230460ustar00rootroot00000000000000# 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") that is located in this folder: $XDG_CONFIG_HOME/backintime/user-callback (by default $XDG_CONFIG_HOME is ~/.config) 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() if not os.path.exists(self.script): return False return True # TODO 09/28/2022: This method should be private (__callback) def callback(self, *args, profileID = None): if profileID is None: profileID = self.config.currentProfile() profileName = self.config.profileName(profileID) cmd = [self.script, profileID, profileName] cmd.extend([str(x) for x in args]) logger.debug('Call user-callback: %s' %' '.join(cmd), self) if self.config.userCallbackNoLogging(): stdout, stderr = None, None else: stdout, stderr = PIPE, PIPE try: callback = Popen(cmd, stdout = stdout, stderr = stderr, universal_newlines = True) output = callback.communicate() if output[0]: logger.info('user-callback returned \'%s\'' %output[0].strip('\n'), self) if output[1]: logger.error('user-callback returned \'%s\'' %output[1].strip('\n'), self) if callback.returncode != 0: logger.warning('user-callback returncode: %s' %callback.returncode, self) raise StopException() except OSError as e: logger.error("Exception when trying to run user callback: %s" % 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.4.3/common/po/000077500000000000000000000000001455673541400155515ustar00rootroot00000000000000backintime-1.4.3/common/po/README.md000066400000000000000000000005001455673541400170230ustar00rootroot00000000000000Translation 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.4.3/common/po/ar.po000066400000000000000000001360011455673541400165140ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-12 13:56+0000\n" "Last-Translator: Maytha8 \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "تحذير" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "الملف الشخصي الرئيسي" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "محلي" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "مفتاح SSH خاص" #: common/config.py:304 msgid "encrypted" msgstr "مشفرة" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "التعمية" #: common/config.py:310 msgid "SSH encrypted" msgstr "تشفير SSH" #: common/config.py:317 msgid "Default" msgstr "إفتراضيّ" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "الملف الشخصي: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "مُجلد اللقطات غير صالح!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "يجب أن تُحدد على الأقل مُجلداً واحداً للنسخ الأحتياطي!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "لا يمكن تضمين مجلد النسخ الاحتياطي." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "لا يمكن تضمين المجلد الفرعي للنسخ الاحتياطي." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "اختيار غير صالح. {path} ليس مُجلداً." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "يجب ألا يكون حقول Host/User/Profile-ID فارغًا." #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "لا يستطيع الكتابة إلى {path}\n" "هل انت مُتأكد بأن لديك حق الكتابة؟" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "نسخ الروابط (احصل علي محتوي العنوان الرمزي)" #: common/config.py:498 msgid "Expert Options" msgstr "خيارات الخبراء" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "لا يستطيع إيجاد crontab.\n" "هل أنت مُتأكد بأن cron مثبت فعلاً ؟\n" "أن لم يكن كذلك فعليك تعطيل جميع النسخ الإحتياطية الأوتوماتيكي." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "فشل كتابة crontab جديد." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "جدول udev لا يعمل مع طريقة {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "الملف الشخصي \"{name}\" موجود أصلاً." #: common/configfile.py:736 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 password" msgstr "يرجى تأكيد كلمة السر" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "كلمة السر ليس مطابق." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" "تحتوي الإصدار 1.7.2 او اقدم من encfs على علة مع خيار --reverse. الرجاء تحديث" " encfs." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "خذ لقطة" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "لا يستطيع تفكيك {mountprocess} من {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "لا يوجد {}. الرجاء تثبيت مثلا {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "نقطة التركيب {} ليست فارغة." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "الملف الشخصي '{profile}': الرجاء دخول كلمة السر لـ{mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "فشل" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "إستعادة الأذونات" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "تم" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "تأجيل النسخ الاحتياطي أثناء وجوده على البطارية" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "لا يستطيع إيجاد مُجلد اللقطات.\n" "إذا كان في قرص قابل للإزالة فيرجى توصيله." #: common/snapshots.py:772 #, 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:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "فشل بأخذ لقطة {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "يُنْهيّ" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "لا يمكن إنشاء مجلد" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "يتم حفظ ملف التكوين…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "يتم حفظ الأذونات…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "وجد بقايا {snapshot_id} ويمكن الاستمرار فيه." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "يتم إزالة مجلد بقايا {snapshot_id} من العملية الاخيرة" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "لا يُمكن إزالة المُجلد" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "يتم اخذ لقطة" #: common/snapshots.py:1254 msgid "Success" msgstr "ناجح" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "نقل جزئي بسبب اختفاء ملفات المصدر (انظر لـ'man rsync')" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "انتهى 'rsync' مع رمز الخروج {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "اطلع على 'man rsync' للمزيد من التفاصيل" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "رموز الخروج السلبية لrsync هم ارقام إشارة, اطلع على 'kill -l' و'man kill'" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "لا شييء تغير, لقطة جديدة ليست ضرورية" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "لا يمكن إعادة تسمية {new_path} إلى {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "إزالة ذكية" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "أزل اللقطات القديمة" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "حاول أبقاء حد أدنى من المساحة فارغة" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "حاول أبقاء حد أدنى من المساحة فارغة" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "الآن" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "تعذر ضم {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "تعذر فتح مفتاح ssh الخاص. إما أن كلمة السر خاطئة أو أنها غير متوفرة لـcron." #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "لا يُمكن إنشاء مُجلد." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, fuzzy, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "رجاء أكد كلمة السر" #: qt/app.py:167 msgid "Shortcuts" msgstr "إختصارات" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "لا يُمكن إيجاد مُجلد اللقطات.\n" "إذا كان على قرص قابل للإزالة من فضلك وَصْلّهُ ومن ثم أضغط موافق." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "خذ لقطة" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "أستخدم تدقيق المجموع لِكشف التغيُّرات." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "حدث قائمة اللقطات" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "خذ لقطة" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "أزل اللقطة" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "عرض سجل اللقطات" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "عرض اخر سجل" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "الملف الشخصي الرئيسي" #: qt/app.py:500 msgid "Shutdown" msgstr "أغلق" #: qt/app.py:502 #, fuzzy msgid "Shut down system after snapshot has finished." msgstr "أطفئ النظام بعد اكتمال أخذ اللقطة." #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "أخرج" #: qt/app.py:512 msgid "Help" msgstr "مُساعدة" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "أحفظ ملف التعديل" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "موقع إلكتروني" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "سجل التغييرات" #: qt/app.py:525 msgid "FAQ" msgstr "الأسئلة الأكثر شيوعاً" #: qt/app.py:528 msgid "Ask a question" msgstr "إسأل سُؤالاً" #: qt/app.py:531 msgid "Report a bug" msgstr "بلّغ عن عِلة" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "الترجمات" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "عنْ" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "إسترجع" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "إسترجع إلى …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "أعلى" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "أظهر الملفات المخفيّة" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "خذ لقطة" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "إسترجع" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "مُساعدة" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "يعمل:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "تم، لا حاجة للنسخ الاحتياطي" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "يعمل" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "خطأ" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 #, fuzzy msgid "Speed" msgstr "السرعة" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "عام" #: qt/app.py:1051 msgid "Root" msgstr "جذر" #: qt/app.py:1052 msgid "Home" msgstr "Home" #: qt/app.py:1067 msgid "Backup folders" msgstr "مجلدات النسخ الاحتياطي" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "اسم اللقطة" #: qt/app.py:1202 #, fuzzy 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "هل أنت متأكد من إستعادة هذه الملفات" msgstr[1] "هل أنت متأكد من إستعادة هذه الملفات" msgstr[2] "هل أنت متأكد من إستعادة هذه الملفات" msgstr[3] "هل أنت متأكد من إستعادة هذه الملفات" msgstr[4] "هل أنت متأكد من إستعادة هذه الملفات" msgstr[5] "هل أنت متأكد من إستعادة هذه الملفات" #: qt/app.py:1370 #, 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] "هل أنت متأكد من إستعادة هذه الملفات" msgstr[3] "هل أنت متأكد من إستعادة هذه الملفات" msgstr[4] "هل أنت متأكد من إستعادة هذه الملفات" msgstr[5] "هل أنت متأكد من إستعادة هذه الملفات" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "لقطات" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "إسترجع" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "إسترجع إلى …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "المؤلفون" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "الترجمات" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "الرخصة" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 #, fuzzy msgid "System default" msgstr "الإفتراضي" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, python-brace-format msgid "" "Hello\n" "You have used Back In Time in the {language} language a few times by now.\n" "The translation of your installed version of Back In Time into {language} is {perc} complete. Regardless of your level of technical expertise, you can contribute to the translation and thus Back In Time itself.\n" "Please visit the {translation_platform_url} if you wish to contribute. For further assistance and questions, please visit the {back_in_time_project_website}.\n" "We apologize for the interruption, and this message will not be shown again. This dialog is available at any time via the help menu.\n" "Your Back In Time Team" msgstr "" "مرحبا\n" "لقد استخدمت تطبيق العودة في الزمن باللغة العربية عدة مرات من قبل.\n" "لقد تم ترجمة {perc} من برنامج العودة في الزمن إلى اللغة العربية. بغض النظر إلى خبرتك التقنية, بإمكانك المساهمة في الترجمة ومن ثم برنامج العودة في الزمن نفسه.\n" "الرجاء زيارة موقع {translation_platform_url} إذا رغبت في المشاركة. للمزيد من الإستفسارات او المساعدة, الرجاء زيارة {back_in_time_project_website}.\n" "نعتذر للمقاطعة, هذه الرسالة لن تعرض مرة أخرى. بإمكانك الإطلاع على هذه الرسالة في أي وقت عبر قائمة المساعدة.\n" "فريق العودة في الزمن" #: qt/languagedialog.py:216 #, fuzzy msgid "translation platform" msgstr "الترجمات" #: qt/languagedialog.py:232 #, fuzzy msgid "Your translation" msgstr "الترجمات" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "ملف شخصي" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "لقطات" #: qt/logviewdialog.py:94 #, fuzzy msgid "Filter" msgstr "مُرَشِح" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "كل" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "تغييرات" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "أخطاء" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "معلومات" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] خطأ، [I] معلومات، [C] غيّر" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "سؤال" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "عرض اخر سجل" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "تشغيل {appname}" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "يعمل…" #: qt/qttools.py:370 msgid "Today" msgstr "اليوم" #: qt/qttools.py:377 msgid "Yesterday" msgstr "البارحة" #: qt/qttools.py:386 msgid "This week" msgstr "هذا الأسبوع" #: qt/qttools.py:393 msgid "Last week" msgstr "الأسبوع الماضي" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "هذه ليست لقطة بلا عرض حي لملفاتك المحلية" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "آخر فحص {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "اعرض السجل كاملا" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "الملف الشخصي الرئيسي" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "تعديل" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "أضف" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "أزل" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "عام" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "الوضع" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "أين سيتم حفظ اللقطات" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "المجلد" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "إعدادات SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "المُضيف" #: qt/settingsdialog.py:204 #, fuzzy msgid "Port" msgstr "المنفذ" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 #, fuzzy msgid "User" msgstr "مستخدم" #: qt/settingsdialog.py:214 msgid "Path" msgstr "المسار" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 #, fuzzy msgid "Private Key" msgstr "مفتاح SSH خاص" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "الكلمة السرية" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "مُتقدم" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "جدول" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "مُعطل" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "في كل اقلاع/اعادة اقلاع" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, 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:363 msgid "Every hour" msgstr "كل ساعة" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "كل ساعتان" msgstr[1] "كل ساعتان" msgstr[2] "كل ساعتان" msgstr[3] "كل ساعتان" msgstr[4] "كل ساعتان" msgstr[5] "كل ساعتان" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "ساعات مخصصة" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "كل يوم" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:375 #, fuzzy msgid "When drive gets connected (udev)" msgstr "عند وصل وسيط تخزين (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "كل أسبوع" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "كل شهر" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "كل يوم" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "يوم" #: qt/settingsdialog.py:394 #, fuzzy msgid "Weekday" msgstr "يوم الأسبوع" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "ساعة" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "ساعات" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "كل" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "ساعات" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "يوم(أيام)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "أسبوع(أسابيع)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "شهور" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "ضمْن" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "ضمْن ملفات ومُجلدات" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "أضف ملف" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "أضف مُجلد" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "أستثني" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "إستثني الأنماط, ملفات أو مُجلدات" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "مُوصى بشدة" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "إستثناء الملفات الأكبر من: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "إزالة-تلقائية" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "أقدم من" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "سنة(سنين)" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "إذا كانت المساحة الفارغة أقل من" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "إذا كانت المساحة الفارغة أقل من" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "إزالة ذكية" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 #, fuzzy msgid "EXPERIMENTAL" msgstr "إختباري" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "أبقي كل اللقطات حتى نهاية" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "يوم (أيّام)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "أبقي لقطة واحدة ليوم واحد حتى النهاية" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "أبقي لقطة واحدة للأسبوع حتى النهاية" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "أسبوع(أسابيع)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "أبقي لقطة واحدة لكل شهر حتى النهاية" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "شهر(أشهر)" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "أبقي لقطة واحدة للسنة وهكذا لكل السنين" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "لا تُزيل اللقطات المُسماّة" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "خيارات" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "فَعْلّ التنبيهات" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "عطلّ اللقطات عندما تعمل بالجهاز على البطارية" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "حالة الطاقة غير متوفرة من النظام" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "أستمر عند الأخطاء (أبقي اللقطات الغير مُكتملة)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "أستخدم تدقيق المجموع لِكشف التغيُّرات" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 #, fuzzy msgid "Log Level" msgstr "مستوي السجل" #: qt/settingsdialog.py:805 msgid "None" msgstr "لا شيء" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "خيارات الخبراء" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "غيّر هذه الخيارات فقط عندما تكون واثق مما تفعله." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "في الجهاز المحلي" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "حِفاظ ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "المُحافظة على الصفات المُوسعة (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "أنسخ الروابط الغير الآمنة (تعمل فقط مع الروابط الأساسية)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "الإفتراضي" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "إستعادة الإعداد" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "ملف شخصي جديد" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "أعِد تسمية الملف الشخصي" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "هل أنت مُتأكد بأنك تُريد حذف ملف الشخصي \"{name}\" ؟" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "إستثني نمط" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "إستثني ملف" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "إستثني مُجلد" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "ضمْن ملف" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "ضمْن مُجلد" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "هل أنت مُتأكد بأنك تُريد تغيير مُجلد اللقطات ؟" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "مفعَّل" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "مُعطَّل" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "استرجاع الإعدادات" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "لم يتم إيجاد الإعداد" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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:66 #, fuzzy msgid "Use %1 and %2 for path parameters" msgstr "استعمل ١٪ و ٢٪ لِمسار الموسطات" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "فحص بعمق (أكثر دقة, ولكن بطيء)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "احذف" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "تحديد الكل" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "مقارنة" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "أذهب إلى" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "خيارات" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "لا تستطيع مُقارنة اللقطة بنفسها." #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "الأمر لم يُعثر عليه" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "هل أنت متأكد من إستعادة هذه الملفات؟" #: qt/snapshotsdialog.py:375 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "هل أنت متأكد من إستعادة هذه الملفات؟" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "لا يمكن إلغاء ذلك!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "تحذير" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s ليس عضوا في مجموعة 'fuse'.\n" #~ " شغل 'sudo adduser %(user)s fuse'. لتطبيق التغييرات سجّل الخروج ثم ادخل.\n" #~ "ألق نظرة على 'man backintime' لمزيد من التعليمات." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "لم يوجد %s في ssh_known_hosts." #, fuzzy #~ msgid "&Snapshot" #~ msgstr "لقطات" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "تغييرات & أخطاء" #~ msgid "Diff" #~ msgstr "فرق" #~ msgid "Diff Options" #~ msgstr "خيارات الاختلافات" #~ msgid "Error:" #~ msgstr "خطأ:" #~ msgid "Every 10 minutes" #~ msgstr "كل ١٠ دقائق" #~ msgid "Every 12 hours" #~ msgstr "كل ١٢ ساعات" #~ msgid "Every 30 minutes" #~ msgstr "كل ٣٠ دقيقة" #~ msgid "Every 4 hours" #~ msgstr "كل ٤ ساعات" #~ msgid "Every 5 minutes" #~ msgstr "كل ٥ دقائق" #~ msgid "Every 6 hours" #~ msgstr "كل ٦ ساعات" #~ msgid "List only different snapshots" #~ msgstr "سرد لقطات المُختلفة فقط" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "الملف الشخصي: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "إسترجع '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "إسترجع '%s' إلى ..." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "إعدادات" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "لقطة: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "أعرض مُحتوى القرص الحالي" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "أعرض اللقطة المُعِدة في {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "بأخطاء" #~ msgid "Working..." #~ msgstr "يعمل..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "لا يُمكنك أن تُضَمْن مُجلد النسخ الإحتياطي !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "لا يُمكنك أن تُضَمْن مُجلد نسخ إحتياطي فرعي !" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "لا يُمكنك إزالة آخر ملف شخصي !" backintime-1.4.3/common/po/bg.po000066400000000000000000001736031455673541400165130ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2023-12-09 16:56+0000\n" "Last-Translator: Salif Mehmed \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.2.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Внимание" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Основен профил" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Местен" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Частен SSH ключ" #: common/config.py:304 msgid "encrypted" msgstr "криптиран" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Криптиране" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH криптиран" #: common/config.py:317 msgid "Default" msgstr "По подразбиране" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Профил: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Папката за моментни архиви не е валидна!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Трябва да изберете поне една директория за архивиране!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Папката за резервно копие не може да бъде включена." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Не може да се включи подпапка за резервно копие." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Невалидна опция. {path} не е папка." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Хост/Потребител/Профил-ID не трябва да бъде празен." #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Записването в {path} е невъзможно\n" "Сигурни ли сте, че имате достъп?" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Копирай връзките към файлове (следвай символните връзки)" #: common/config.py:498 msgid "Expert Options" msgstr "Експертни настройки" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "crontab не може да бъде намерен.\n" "Сигурни ли сте, че cron е инсталиран?\n" "Ако не е, трябва да изключите всички автоматични архивирания." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Неуспех при създаването на нова периодична задача - crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Планираните при монтиране на диск архиви не работят в режим {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Профилът \"{name}\" вече съществува." #: common/configfile.py:736 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 password" msgstr "Моля потвърдете паролата" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Паролата не съвпада." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" "Във версия 1.7.2 и по-ранни версии на encfs има грешка с опцията --reverse. " "Моля, актуализирайте encfs." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Създайте моментен архив" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Не мога да демонтирам {mountprocess} от {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} не е намерен. Моля, инсталирайте например {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Mountpoint {} не е празна." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Профил '{profile}': Въведете парола за {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "Провал" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Възстановете разрешенията" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Готово" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Отлагане на архивирането, докато устройството работи на батерия" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Папката за моментни архиви не може да бъде открита.\n" "Ако е на преносим драйв, моля включете го." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Изчакване %s секунда." msgstr[1] "Изчакване %s секунди." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Неуспех при създаването на моментен архив {snapshot_id} ." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Приключване" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Папката не може да бъде създадена" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Запазване на конфигурационен файл…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Запазване на правата…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Намерен е {snapshot_id}, който може да бъде продължен." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Премахване на остатъчна {snapshot_id} папка от последния курс" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Папката не може да бъде премахната" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Създаване на моментен архив" #: common/snapshots.py:1254 msgid "Success" msgstr "Успешно" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Частично прехвърляне поради изчезнали изходни файлове (вж. 'man rsync')" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' завърши с код за изход {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "За повече информация вижте 'man rsync'" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Отрицателните изходни кодове на rsync са номера на сигнали, вижте 'kill -l' " "и 'man kill'" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Няма промени, не е нужен нов моментен архив" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{new_path} не може да бъде преименуван на {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Умно премахване" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Премахване на старите моментни архиви" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Опит за запазване на минимално свободно пространство" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Опит за запазване на минимум {perc} свободни inode" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Сега" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Неуспех в монтирането на {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-агентът не е намерен. Моля, уверете се, че е инсталиран." #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Не можа да отключи частния ключ на ssh. Грешна парола или паролата не е " "налична за cron." #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Шифърът {cipher} е неуспешен за {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Отдалеченият път съществува, но не е директория." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Отдалеченият път не може да се записва." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Отдалеченият път не е изпълним." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Не може да се създаде отдалечен път." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Отдалеченият хост {host} не поддържа {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Вижте 'man backintime' за допълнителни инструкции" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Проверката на командите на хоста {host} върна непозната грешка" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Отдалеченият хост {host} не поддържа твърди връзки" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "Копирай публичния ssh ключ \"{pubkey}\" в дистанционният хост \"{host}\"" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Моля въведете паролата за \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Препратки" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Тази папка не съществува\n" "в текущата архив." #: qt/app.py:252 msgid "Add to Include" msgstr "Добавете, за да включите" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Добавете, за да изключите" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} не е зададен. Бихте ли искали да възстановите предишна " "конфиурация?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Папката за моментни архиви не може да бъде открита.\n" "Ако е на преносимо устройство, моля включете го и натиснете ОК." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Направете моментен архив" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" "Използвайте времето и размера на модификацията за откриване на промени във " "файла." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Създаване на моментен архив (режим на контролна сума)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Използване на контролни суми за откриване на промени във файловете." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Пауза на моментното копие" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Продължаване на моментното копие" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Спиране на моментното копие" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Обновяване на списъка с архиви" #: qt/app.py:480 msgid "Name snapshot" msgstr "Наименувай архив" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Премахване на архив" #: qt/app.py:488 msgid "View snapshot log" msgstr "Преглед на дневника на архивите" #: qt/app.py:492 msgid "View last log" msgstr "Преглед на последния дневник" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Управление на профили" #: qt/app.py:500 msgid "Shutdown" msgstr "Изключване" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Изключване на системата слад като моментното архивно копие е готово." #: qt/app.py:504 #, fuzzy msgid "Setup language…" msgstr "Език на настройката" #: qt/app.py:508 msgid "Exit" msgstr "Изход" #: qt/app.py:512 msgid "Help" msgstr "Помощ" #: qt/app.py:516 msgid "Profiles config file" msgstr "Файл за конфигуриране на профили" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Уебсайт" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "История на промените" #: qt/app.py:525 msgid "FAQ" msgstr "ЧЗВ" #: qt/app.py:528 msgid "Ask a question" msgstr "Задайте въпрос" #: qt/app.py:531 msgid "Report a bug" msgstr "Докладвайте неизправност" #: qt/app.py:534 msgid "Translation" msgstr "Превод" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Относно" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Възстановяване" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "Възстановете избраните файлове или папки на първоначалното място." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Възстановяване в …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "Възстановете избраните файлове или папки на друго място." #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Възстановете показаната папка и нейното съдържание на първоначалното място." #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Възстановете показаната папка и нейното съдържание на ново място." #: qt/app.py:560 msgid "Up" msgstr "Нагоре" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Показване на скритите файлове" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Сравни архиви" #: qt/app.py:627 msgid "&Backup" msgstr "&Резервно копие" #: qt/app.py:638 msgid "&Restore" msgstr "&Възстановяване" #: qt/app.py:644 msgid "&Help" msgstr "&Помощ" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Ако затворите този прозорец, Back in Time няма да може да изключи вашата система когато моментното копие е завършено.\n" "Наистина ли искате да го затворите?" #: qt/app.py:905 msgid "Working:" msgstr "Изпълнение:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Готово, не е необходимо архивиране" #: qt/app.py:962 msgid "Working" msgstr "Изпълняване" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Грешка" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Изпратено" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Скорост" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Приблизително оставащо време" #: qt/app.py:1050 msgid "Global" msgstr "Глобални" #: qt/app.py:1051 msgid "Root" msgstr "Файлова система" #: qt/app.py:1052 msgid "Home" msgstr "Домашна папка" #: qt/app.py:1067 msgid "Backup folders" msgstr "Резевни папки" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Име на моментния архив" #: qt/app.py:1202 #, fuzzy 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:1294 #, fuzzy, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Създайте резервни копия, завършващи със {suffix} преди\n" "да бъдат презаписани или премахнати локални файлове." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "По-новите версии на файловете ще бъдат преименувани с последващ {suffix} при възстановяването.\n" "Ако не са необходими, могат да бъдат премахнати посредством {cmd}" #: qt/app.py:1314 #, 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:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "Премахнете по-новите файлове от първоначалната директория" #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Възстановяване на избрани файлове или папки до първоначалната дестинация и\n" "изтриване на файлове/папки, които не са в архива.\n" "Това ще доведе до изтриване на файлове/папки, които са били изключени по време на създаването на архива!\n" "Бъдете изключително внимателни!" #: qt/app.py:1361 #, 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] "" "Наистина ли искате да възстановите тези файлове\n" "в новата директория {path}" msgstr[1] "" "Наистина ли искате да възстановите тези файлове\n" "в новата директория {path}" #: qt/app.py:1370 #, fuzzy 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "Сигурни ли сте, че желаете окончателно да премахнете всички по-нови файлове " "в {path}?" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Сигурни ли сте, че искате да премахнете всички по-нови файлове в " "първоначалната директория?" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "ПРЕДУПРЕЖДЕНИЕ: Изтриването на файлове в коренната част на файловата система" " може да разруши цялата ви система!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Моментно архивно копие" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Възстановете {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Възстановете {path} в …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Езиковите настройки влизат в сила само след рестартиране на Back In Time." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Създатели" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Преводачи" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Лизенз за изходния код" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Език на настройката" #: qt/languagedialog.py:87 msgid "System default" msgstr "Система по подразбиране" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Използвайте езика на операционните системи." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Преведено: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "платформа за превод" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Вашият превод" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Последно преглеждане на записа със събития" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Преглеждане на записа със сибития относно моментните копия" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Профил" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Моментни архиви" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Филтър" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Всичко" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Промени" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Грешки" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Информация" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[Г] Грешка, [И] Информация, [П] Промяна" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "декодиране на пътищата" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Копирайте" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Декодиране" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Искате ли да изключите това?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Въпрос" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Преглед на последния запис" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Стартиране на {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Изпълняване…" #: qt/qttools.py:370 msgid "Today" msgstr "Днес" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Вчера" #: qt/qttools.py:386 msgid "This week" msgstr "Тази седмица" #: qt/qttools.py:393 msgid "Last week" msgstr "Миналата седмица" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Това НЕ е моментно копие, а текущ изглед на локалните Ви файлове" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "Последна проверка {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Покажи всички записани събития" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Управление на профили" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Редактиране" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Добавяне" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Премахване" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Общи" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Режим" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Къде да се запазват моментните архиви" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Директория" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH настройки" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Хост" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Порт" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Потребител" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Път" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Шифър" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Частен ключ" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Изберете съществуващ частен ключ (обикновено озаглавен \"id_rsa\")" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" "Създайте нов SSH ключ без парола (не е позволено, ако файл с частен ключ е " "вече избран)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Парола" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Запазете паролата в ключодържателя" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Кеширайте паролата за периодичните задачи (Възможен проблем със сигурността:" " потребител root може да достъпва паролата)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Разширени" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Пълен път за моментното архивно копие" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Разписание" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Изключено" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "При всяко зареждане/рестартиране" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "На всяка {n} минута" msgstr[1] "На всеки {n} минути" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "На всеки час" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "На всеки {n} часа" msgstr[1] "На всеки {n} часа" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Часове по предпочитание" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Ежедневно" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Многократно (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Когато дискът бъде свързан (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Ежеседмично" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Ежемесечно" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Ежегодишно" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Ден" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Работен ден" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Час" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Часа" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Изпълнявайте Back In Time многократно. Това е полезно, когато компютърът не " "работи редовно." #: qt/settingsdialog.py:442 msgid "Every" msgstr "Всеки" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Час (а)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Дни" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Седмици" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Месеци" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Включване" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Включване на файлове и папки" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Добавяне на файл" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Добавяне на папка" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Изключване" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Заместващите знаци ({example1}) ще бъдат игнорирани при режим 'SSH encrypted'.\n" "Разрешени са само единични или двойни звездички ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Изключване на шарки, файлове или папки" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Силно препоръчително" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Добавете по подразбиране" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Изключете файлове, по-големи от: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Изключете файлове, по-големи от стойността в %(prefix)s.\n" "При изключен 'Full rsync mode', това ще засегне само новите файлове,\n" "защото това е преносна опция за rsync, а не изключваща такава.\n" "Така че големите файлове, които са били архивирани преди, ще останат в\n" "моментните копия дори и да са били променени." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&Автоматично премахване" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "По-стари от" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Години" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Ако свободното пространство е по-малко от" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Ако има по-малко inodes от" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Умно премахване" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Изпълнява се във фонов режим на отдалечен хост." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "ЕКСПЕРИМЕНТАЛНО" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Пазете всички временни архиви за последните" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "дни" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Пазете едно моментно копие на ден за последните" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Пазете едно моментно копие на седмица за последните" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "седмица(и)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Пазете едно моментно копие на месец за последните" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "месеца" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Пазете едно моментно копие на година" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Да не се премахват именувани архиви" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Настройки" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Включване на известяването" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Изключване на моментни архиви при работа на батерия" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Енергийното състояние не е налично от системата" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Изпълнявайте по едно моментно копие наведжъж" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Останалите моментни копия ще бъдат блокирани докато текущото такова не приключи.\n" "Това е глобална опция и ще повлияе на всички профили на потребителя.\n" "Но трябва да я изберете също и за останалите потребители." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Архивирайте подменените файлове при възстановяване" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Продължаване при грешки (запазване на непълни моментни архиви)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Използвайте проверка на контролната сума, за да засечете промените" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "Направете ново моментно копие независимо дали има промени или не." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "Ниво на записа" #: qt/settingsdialog.py:805 msgid "None" msgstr "Нищо" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Е&кспертни настройки" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Внимание: Променяйте тези опции само ако наистина знаете какво правите." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Изпълнете 'rsync' посредством '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "като периодична задача" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "върху отдалечен хост" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "когато се изпълнява ръчно моментно копие" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Моля инсталирайте 'nocache', за да активирате тази опция)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "върху настоящата машина" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Пренасочете стандартният изход към /dev/null при периодичните задачи." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" "Пренасочете стандартният изход за грешки към /dev/null за периодични задачи." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Ограничете използваният трафик от rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/сек" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Запазване на ACL (списък за контрол на достъпа)" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Запазване на разширените атрибути (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Копиране на несигурните връзки (работи само с абсолютни връзки)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Опциите трябва да бъдат цитирани, напр. {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Добавете допълнителни опции към rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Префикс, който да се изпълнява преди всяка команда на отдалечения хост.\n" "Променливите трябва да се избягват с \\$FOO.\n" "Това не засяга rsync. Така че, за да добавите префикс\n" "за rsync използвайте \"%(cbRsyncOptions)s\" с\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "по подразбиране" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Добавете префикс към SSH командите" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Проверете дали отдалеченият хост е онлайн" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Внимание: ако е изключено и отдалеченият хост\n" "не е наличен, това може да доведе до някои\n" "непредвидени грешки." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Проверете дали отдалеченият хост поддържа всички необходими команди" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Внимание: ако е изключено и отдалеченият хост\n" "не поддържа всички необходими команди,\n" "това може да довете до неочаквани грешки." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Възстановете конфигурацията" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Променете потребителския скрипт" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Нов профил" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Преименуване на профил" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Сигурни ли сте, че желаете да изтриете профила \"{name}\"?" #: qt/settingsdialog.py:1416 #, 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Не сте избрали частен ключ за SSH.\n" "Бихте ли искали да създадете нова двойка частен/публичен ключ без парола?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Файлът с частния ключ \"{file}\" не съществува." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Бихте ли искали да копирате вашият публичен SSH ключ върху\n" "отдалеченият хост, за да активирате логин без парола?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Достоверността на хоста {host} не може да бъде потвърдена.\n" "\n" "{keytype} отпечатъкът на ключа е:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" "Моля потвърдете този отпечатък! Бихте ли искали да го добавите към " "'known_hosts' файлът с позволени хостове?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Шаблон за изключване" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Изключване на файл" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Изключване на папка" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Включване на файл" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Включване на папка" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Сигурни ли сте, че желаете да промените папката за моментни архиви ?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Неуспех при създаване на нов SSH ключ в {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Пълен път за моментни архивни копия: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "активирано" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "деактивирано" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Възстановете настройките" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Моля, навигирайте до моментния архив , от който искате да възстановите конфигурацията на {appName}. Пътят може да изглежда по следния начин:\n" "{samplePath}\n" "\n" "Ако вашите моментни архиви се намират на отдалечен диск или са криптирани, е необходимо първо да ги монтирате ръчно. Ако използвате режим SSH, също така може да се наложи да използвате автентикация с публичен ключ до отдалечения хост.\n" "Обърнете внимание на \"man backintime\"." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Не е намерена конфигурация" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "Потребителският скрипт няма шебанг (#!/bin/sh) на първия ред." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Използване на %1 и %2 за параметри на пътя" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Само различаващи се моментни архиви" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Покажете само съвпадащи моментни архиви до: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Дълбока проверка (по-точна, но бавна)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Изтриване" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Избиране на всички" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Сравни" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Отиване до" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Настройки" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Не можете да сравнявате моментен архив със самия него." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Командата не е открита" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Наистина ли искате да изтриете {file} от моментния архив {snapshot_id}?" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Наистина ли искате да изтриете {file} от {count} моментни архиви?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Това действие не може да бъде отменено!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ВНИМАНИЕ" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Изключете {path} от бъдещите моментни архиви?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " и добавете вашият потребител към потребителската група 'fuse'" #~ msgid "&Snapshot" #~ msgstr "&Моментни архиви" #~ msgid "&View" #~ msgstr "&Изглед" #~ msgid "..." #~ msgstr "..." #~ msgid "Changes & Errors" #~ msgstr "Промени и грешки" #~ msgid "Config File Help" #~ msgstr "Помощ за конфигурационния файл" #~ msgid "Diff" #~ msgstr "Сравняване" #~ msgid "Diff Options" #~ msgstr "Настройки на сравняването" #~ msgid "Error:" #~ msgstr "Грешка:" #~ msgid "Every 10 minutes" #~ msgstr "На всеки 10 минути" #~ msgid "Every 5 minutes" #~ msgstr "На всеки 5 минути" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Пълният системен бекъп може да създаде само моментно копие, което да бъде възстановено на същият/те физически носител/и със същите дялове като оригинала. Възстановяването на нов диск или на различни дялове ще възпроизведе потенциално неизползваема система.\n" #~ "\n" #~ "Пълният системен бекъп ще презапише някои опции, които може да са били променени. искате ли да продължите?" #~ msgid "List only different snapshots" #~ msgstr "Изброяване само на различаващите се моментни архиви" #~ msgid "Local encrypted" #~ msgstr "Местно криптиране" #~ msgid "Modify for Full System Backup" #~ msgstr "Модифицирайте за пълно системно архивно копие" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Профил: \"{name}\"" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Възстановете избраният файл или папка.\n" #~ "Ако този бутон не е активен, то най-вероятно \"{now}\" е избран в списъка с моментни архивни копия отляво." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Настройки" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Моментен архив: %s" #~ msgid "View the current disk contents" #~ msgstr "Преглед на текущото съдържание на диска" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Преглеждане на моментния архив, създаден на {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "С ГРЕШКИ!" #~ msgid "Working..." #~ msgstr "Изпълнение..." #~ msgid "You can't include backup folder!" #~ msgstr "Не можете да добавите папката с резервни копия!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Не можете да добавите поддиректория на резервното копие!" #~ msgid "You can't remove the last profile!" #~ msgstr "Не можете да премахнете последния профил!" backintime-1.4.3/common/po/bs.po000066400000000000000000001104671455673541400165260ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Upozorenje" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Glavni profil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokalno" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH Privatni kljuc" #: common/config.py:304 msgid "encrypted" msgstr "šifrirano" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Šifriranje" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH šifrirano" #: common/config.py:317 msgid "Default" msgstr "Default" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Folder za slike ekrana nije validan !" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Moraš izabrati najmanje jedan folder za rezervu!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Backup folder nije nemoguće dodati." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Backup sub-folder nije moguće dodati." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opcija nije validna. {path} nije direktorij." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profil-ID ne smije biti prazna." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopiraj linkove (dereferenciraj simbolicke linkove)" #: common/config.py:498 msgid "Expert Options" msgstr "Expert opcije" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Pokušaj pisanja novog crontab-a nije uspio." #: common/config.py:1707 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Zakazani udev ne radi sa modom {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" već postoji." #: common/configfile.py:736 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 password" msgstr "Molimo Vas potvrdite šifru" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Šifre nisu iste." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Napravi sliku" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} nije pronađen. Molimo instalirajte npr. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Mountpoint {} nije prazan." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "" #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "NIJE USPJELO" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Spremi dozvole" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Završeno" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" #: common/snapshots.py:772 #, 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:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "" #: common/snapshots.py:826 msgid "Finalizing" msgstr "Završava" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Nemoguće kreirati direktorij" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Spašava se konfiguracijski fajl…" #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Spremi dozvole ..." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 #, fuzzy msgid "Can't remove folder" msgstr "Nemoguće izbrisati direktorij" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Pametno brisanje" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Sad" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Nemoguće kreirati direktorij." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "" #: qt/app.py:167 msgid "Shortcuts" msgstr "Prečice" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" #: qt/app.py:453 msgid "Take a snapshot" msgstr "" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "" #: qt/app.py:480 msgid "Name snapshot" msgstr "" #: qt/app.py:484 msgid "Remove snapshot" msgstr "" #: qt/app.py:488 msgid "View snapshot log" msgstr "" #: qt/app.py:492 msgid "View last log" msgstr "" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Glavni profil" #: qt/app.py:500 msgid "Shutdown" msgstr "" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Izlaz" #: qt/app.py:512 msgid "Help" msgstr "Pomoć" #: qt/app.py:516 msgid "Profiles config file" msgstr "" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "" #: qt/app.py:528 msgid "Ask a question" msgstr "" #: qt/app.py:531 msgid "Report a bug" msgstr "" #: qt/app.py:534 msgid "Translation" msgstr "" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "O" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Obnovi" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Obnovi …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "Gore" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Prikaži skrivene datoteke" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Napravi sliku" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "Ob&novi" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "&Pomoć" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "" #: qt/app.py:962 msgid "Working" msgstr "" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "Globalno" #: qt/app.py:1051 msgid "Root" msgstr "Korijen" #: qt/app.py:1052 msgid "Home" msgstr "" #: qt/app.py:1067 msgid "Backup folders" msgstr "" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "" #: qt/app.py:1202 #, 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "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:1370 #, fuzzy 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 msgid "Snapshot" msgstr "" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Obnovi {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Obnovi {path}" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "" #: qt/qttools.py:370 msgid "Today" msgstr "Danas" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Jučer" #: qt/qttools.py:386 msgid "This week" msgstr "Ove sedmice" #: qt/qttools.py:393 msgid "Last week" msgstr "Prošle sedmice" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Glavni profil" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "&Opšte" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Isključeno" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, 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:363 msgid "Every hour" msgstr "" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, 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:372 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Svaki dan" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Svake sedmice" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Svaki mjesec" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Svake godine" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Dan(i)" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Svakih" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dan(i)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Sedmica(e)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Uključi" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Dodaje datoteku" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Dodaj direktorij" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Isključi" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "&Automatsko brisanje" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Starije od" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Godina(e)" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Ako je sloboni prostor manji od" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Ako je sloboni prostor manji od" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Pametno brisanje" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "Dan(i)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "Sedmica(e)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "Op&cije" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Uključi obavijesti" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "" #: qt/settingsdialog.py:805 msgid "None" msgstr "" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "Opcije &izvoza" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Uključi direktorij" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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 "Parametri" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Koristite %1 i %2 za parametre putanje" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Idi na" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Opcije" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "" #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "Komanda nije pronađena" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" #: qt/snapshotsdialog.py:375 #, 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:380 msgid "This cannot be revoked!" msgstr "" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #~ msgid "..." #~ msgstr "..." #~ msgid "Every 10 minutes" #~ msgstr "Svakih 10 minuta" #~ msgid "Every 5 minutes" #~ msgstr "Svakih 5 minuta" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #~ msgid "Settings" #~ msgstr "Postavke" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Pogledaj trenutni sadržaj diska" #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Ne možeš dodati folder rezerve !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Ne možeš dodati folder rezerve !" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Ne možeš izbrisati zadnji profil!" backintime-1.4.3/common/po/ca.po000066400000000000000000001450221455673541400165000ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-19 10:47+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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Avís" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Local" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Clau privada SSH" #: common/config.py:304 msgid "encrypted" msgstr "xifrat" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Xifrat" #: common/config.py:310 msgid "SSH encrypted" msgstr "Xifrat amb SSH" #: common/config.py:317 msgid "Default" msgstr "Predeterminat" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "La carpeta d'instantànies no es vàlida!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "No s'hi pot incloure la carpeta de còpia de seguretat." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "No s'hi pot incloure la subcarpeta de la còpia de seguretat." #: common/config.py:448 #, 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:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID no pot estar buit." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Copia enllaços (dereferencia els enllaços simbòlics)" #: common/config.py:498 msgid "Expert Options" msgstr "Opcions avançades" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "No es pot trobar el 'crontab'.\n" "Segur que el 'cron' està instal·lat ?\n" "Si no, hauríeu de desactivar totes les còpies de seguretat automàtiques." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "No s'ha pogut escriure el nou crontab ." #: common/config.py:1707 #, 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:1722 #, 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:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "El perfil \"{name}\" ja existeix." #: common/configfile.py:736 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 password" msgstr "Confirmeu la contrasenya" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "La contrasenya no coincideix." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Fes una instantània" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "No es pot desmuntar {mountprocess} des de {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "No s'ha trobat {}. Si us plau instal·leu p. ex. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "El punt de muntatge {} no és buit." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Perfil '{profile}': Introduïu la contrasenya per {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "S'HA PRODUÏT UN ERROR" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Restaura els permisos" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Fet" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Es posposa la còpia de seguretat mentre s'utilitza la bateria" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Esperant %s segon." msgstr[1] "Esperant %s segons." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "No s'ha pogut fer la instantània {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Està finalitzant" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "No es pot crear la carpeta" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Desa el fitxer de configuració …" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "S'estan desant els permisos …" #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "No es pot eliminar la carpeta" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "S'està fent una instantània" #: common/snapshots.py:1254 msgid "Success" msgstr "Èxit" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, 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:1268 msgid "See 'man rsync' for more details" msgstr "Consulteu 'man rsync' per a més detalls" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "No hi ha canvis, no cal una instantània nova" #: common/snapshots.py:1318 #, 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:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Esborrat intel·ligent" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Elimina les instantànies antigues" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Proveu de mantenir un minim d'espai lliure" #: common/snapshots.py:1737 #, 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:2821 qt/app.py:1616 msgid "Now" msgstr "Ara" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "No s'ha pogut muntar {sshfs}" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "El xifratge {cipher} ha fallat per a {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "El camí remot existeix però no és un directori." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "No es pot escriure a la ruta remota." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "La ruta remota no és executable." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "No es pot crear la ruta remota." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "L'amfitrió remot {host} no admet {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Mireu 'man backintime' per a més instruccions" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "L'amfitrió remot {host} no admet hardlinks" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Confirmeu la contrasenya per \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Dreceres" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Afegeix a la inclusió" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Afegeix a l'exclusió" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} no està configurat. Voleu restaurar una configuració anterior?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "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:453 msgid "Take a snapshot" msgstr "Fes una instantània" #: qt/app.py:455 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:458 msgid "Take a snapshot (checksum mode)" msgstr "Pren una instantània (mode suma de verificació)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Utilitza sumes de verificació per a la detecció de canvis de fitxers." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Pausa el procés d'instantània" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Reprèn el procés d'instantània" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Atura el procés d'instantània" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Refresca la llista d'instantànies" #: qt/app.py:480 msgid "Name snapshot" msgstr "Anomena la instantània" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Esborra la instantània" #: qt/app.py:488 msgid "View snapshot log" msgstr "Visualitza el registre de les instantànies" #: qt/app.py:492 msgid "View last log" msgstr "Visualitza l'últim registre" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Gestiona els perfils…" #: qt/app.py:500 msgid "Shutdown" msgstr "Aturada" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Apaga el sistema després d'acabar la instantània." #: qt/app.py:504 msgid "Setup language…" msgstr "Llenguatge de configuració…" #: qt/app.py:508 msgid "Exit" msgstr "Sortir" #: qt/app.py:512 msgid "Help" msgstr "Ajuda" #: qt/app.py:516 msgid "Profiles config file" msgstr "Fitxer de configuració de perfils" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Lloc web" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Registre de canvis" #: qt/app.py:525 msgid "FAQ" msgstr "PMF" #: qt/app.py:528 msgid "Ask a question" msgstr "Fer una pregunta" #: qt/app.py:531 msgid "Report a bug" msgstr "Informar d'un error" #: qt/app.py:534 msgid "Translation" msgstr "Traducció" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Quant a" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Restaura" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Restaura a …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Amunt" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Mostra els fitxers ocults" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Compara les instantànies…" #: qt/app.py:627 msgid "&Backup" msgstr "&Còpia de seguretat" #: qt/app.py:638 msgid "&Restore" msgstr "&Restaura" #: qt/app.py:644 msgid "&Help" msgstr "&Ajuda" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" 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:905 msgid "Working:" msgstr "S'està treballant:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Fet, no ha estat necessari crear una còpia de seguretat" #: qt/app.py:962 msgid "Working" msgstr "Treballant" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Error" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Enviat" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Velocitat" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "ETA" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Arrel" #: qt/app.py:1052 msgid "Home" msgstr "Inici" #: qt/app.py:1067 msgid "Backup folders" msgstr "Carpetes de còpia de seguretat" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nom de la instantània" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Esborra els fitxers més nous de la carpeta original." #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "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:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "AVÍS: Esborrar fitxers a l'arrel del sistema de fitxers podria trencar tot " "el sistema!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Instantàna" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Restaura {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaura {path} a …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "La configuració de la llengua només tindrà efecte després de reiniciar Back " "In Time." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Autors" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Traduccions" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Llicència" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Llenguatge de configuració" #: qt/languagedialog.py:87 msgid "System default" msgstr "sistema per defecte" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Utilitza el llenguatge del sistema operatiu." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Traduït: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "plataforma de traducció" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "la teva traducció" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Última visualització dels registres" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Visualitza el registre d'instantànies" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Perfil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Instantànies" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtre" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Tots" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Canvis" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Errors" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informació" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E]Error, [I]Informació, [C]Canvia" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "descodifica rutes" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Copia" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Descodifica" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Voleu excloure això?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Pregunta" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Visualitza l'últim registre" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Inicia {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "S'està treballant…" #: qt/qttools.py:370 msgid "Today" msgstr "Avui" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Ahir" #: qt/qttools.py:386 msgid "This week" msgstr "Aquesta setmana" #: qt/qttools.py:393 msgid "Last week" msgstr "La setmana passada" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Última comprovació {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Mostra el registre complet" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Gestiona els perfils" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Edita" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Afegeix" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Elimina" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&General" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Mode" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "A on guardar Instanànies" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Carpeta" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Paràmetres SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Ordinador amfitrió" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Usuari" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Ruta" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Xifratge" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Clau privada" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Contrasenya" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Desa la contrasenya a l'anell de claus" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Avançat" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Ruta completa de la instantània" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Planificació" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Deshabilitat" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "En cada inici/reinici" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "Cada hora" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 msgid "Custom hours" msgstr "Hores personalitzades" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Cada dia" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Repetidament (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Quan la unitat es connecti (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Cada setmana" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Cada mes" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Cada any" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Dia" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Entre setmana" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Hora" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Hores" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Cada" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Hora(es)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dia(es)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Setmana(es)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Mes(os)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Inclou" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Inclou fitxers i carpetes" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Afegeix fitxer" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Afegeix un directori" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Exclou" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Els comodins ({example1}) s'ignoraran amb el mode 'encriptat SSH'.\n" "Només es permeten asteriscs simples o dobles ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Exclou fitxers i carpetes" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Molt recomanat" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Afegeix per defecte" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Exclou els fitxers més grans que: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." 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:616 msgid "&Auto-remove" msgstr "Eliminació &automàtica" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Més antics que" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Any(s)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Si l'espai lliure és inferior a" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Si els inodes lliures són inferiors a" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Esborrat intel·ligent:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Executa en segon pla en una màquina remota." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Mantingues totes les instantànies durant els darrers" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "dia(es)." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Mantingues una instantània al dia durant els darrers" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Mantingues una instantània a la setmana durant les darreres" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "setmana(es)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Mantingues una instantània al mes durant els darrers" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "mes(os)." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Conserva una instantània de cada any." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "No eliminis les instantànies amb nom." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Opcions" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Activa les notificacions" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Desactiva les instantànies treballant en bateria" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "No està disponible l'estat de l'energia" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Executa només una instantània alhora" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "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:772 msgid "Backup replaced files on restore" msgstr "La còpia de seguretat ha substituïts fitxers en restaurar" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continua amb errors (Mantingues instantànies incompletes)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Utilitza el checksum per detectar canvis" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Nivell de registre" #: qt/settingsdialog.py:805 msgid "None" msgstr "Cap" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Opcions d'e&xperts" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Executa 'rsync' amb '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "com a tasca cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "a l'amfitrió remot" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "en prendre una instantània manual" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Si us plau, instal·leu 'nocache' per a activar aquesta opció)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "a la màquina local" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirigeix la stdout a /dev/null a les funcions cron." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirigeix stderr a /dev/null a les funcions cron." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Limita l'ús de l'amplada de banda 'rsync'" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/seg" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Preserva ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Preserva els atributs extesos (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Copia links insegurs (Només funciona amb links absoluts)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Les opcions s'han de citar, p. ex. {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Enganxa les opcions addicionals al 'rsync'" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Prefix 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:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "per defecte" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Afegeix un prefix a les ordres SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Comprova si l'amfitrió remot està connectat" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "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:1071 msgid "Check if remote host supports all necessary commands" msgstr "Comprova si l'amfitrió remot admet totes les ordres necessàries" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "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:1089 msgid "Restore Config" msgstr "Restaura la configuració" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Edita user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Perfil nou" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Reanomena el perfil" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "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:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "El fitxer de clau privada \"{file}\" no existeix." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "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:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "No es pot establir l'autenticitat de l'amfitrió {host}.\n" "\n" "L'empremta digital de la clau {keytype} és:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Exclou el patró" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Exclou el fitxer" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Exclou el directori" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Inclou un fitxer" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Inclou el directori" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Segur que voleu canviar la carpeta d'instantànies?" #: qt/settingsdialog.py:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Ruta completa de la instantània: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "habilitat" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "desactivat" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Restaura configuració" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Si us plau, navegueu a la instantània des de la qual voleu restaurar la configuració de {appName}. El camí pot semblar:\n" "{samplePath}\n" "\n" "Si les vostres instantànies estan en una unitat remota o si estan encriptades, primer haureu de muntar-les manualment. Si utilitzeu Mode SSH, és possible que també necessiteu configurar l'inici de sessió de la clau pública a l'amfitrió remot.\n" "Doneu un cop d'ull a 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "No s'ha trobat cap configuració" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "l'script de user-callback no té línia shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "El Shebang en l'script de user-callback no és executable." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Opcions de comparació de les instantànies" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Ordre" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Paràmetres" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Utilitza %1 i %2 per els paràmetres de les rutes" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Només captures diferents" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Llista només les instantànies iguals a: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Comprovació a fons (més acurada, però lenta)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Suprimeix" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Selecciona-ho tot" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Compara" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Vés a" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Opcions" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "No podeu compara una instantània amb sí mateixa." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "No s'ha trobat l'ordre" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Això no es pot revocar!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "AVÍS" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Exclou {path} de les instantànies futures?" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s no és membre del grup 'fuse'.\n" #~ " Executeu 'sudo adduser %(user)s fuse'. Per aplicar els canvis cal que sortiu i torneu a entrar a la sessió.\n" #~ "Mireu a 'man backintime' per a més instruccions." #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Instantànies" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Canvis i Errors" #~ msgid "Diff" #~ msgstr "Diff" #~ msgid "Diff Options" #~ msgstr "Opcions de diff" #~ msgid "Error:" #~ msgstr "S'ha produït un error:" #~ msgid "Every 10 minutes" #~ msgstr "Cada 10 minuts" #~ msgid "Every 12 hours" #~ msgstr "Cada 12 hores" #~ msgid "Every 30 minutes" #~ msgstr "Cada 30 minuts" #~ msgid "Every 4 hours" #~ msgstr "Cada 4 hores" #~ msgid "Every 5 minutes" #~ msgstr "Cada 5 minuts" #~ msgid "Every 6 hours" #~ msgstr "Cada 6 hores" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "La còpia de seguretat completa del sistema només pot crear una instantània que es restaurarà al mateix disc físic o discs físics amb els mateixes particions que des de l'origen; la restauració a discs físics nous o els mateixos discs amb diferents particions produirà un sistema potencialment trencat i inutilitzable.\n" #~ "\n" #~ "La còpia de seguretat completa del sistema substituirà alguns paràmetres que poden haver estat personalitzats. Continuar?" #~ msgid "List only different snapshots" #~ msgstr "LLista només les instantànies diferents" #~ msgid "Local encrypted" #~ msgstr "Xifrat local" #~ msgid "Modify for Full System Backup" #~ msgstr "Modifica per a la còpia de seguretat completa del sistema" #, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Perfil: {name}" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Restaura '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Restaura '%s' a..." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Configuració" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Instantània: %s" #~ msgid "View the current disk contents" #~ msgstr "Mostra el contingut actual del disc" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Visualitza la instantània creada a {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "AMB ERRORS !" #~ msgid "Working..." #~ msgstr "S'està treballant..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "No podeu incloure la carpeta de còpies de seguretat!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "No podeu incloure una subcarpeta de còpies de seguretat!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "No es pot esborrar el darrer perfil !" backintime-1.4.3/common/po/cs.po000066400000000000000000001622121455673541400165220ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Varování" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Hlavní profil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Na tomto počítači" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Soukromou část SSH klíče" #: common/config.py:304 #, fuzzy msgid "encrypted" msgstr "Na jiném stroji (skrze SSH tunel) – v šifrované podobě" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Šifrování" #: common/config.py:310 msgid "SSH encrypted" msgstr "Na jiném stroji (skrze SSH tunel) – v šifrované podobě" #: common/config.py:317 msgid "Default" msgstr "Výchozí" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: „{name}“" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Složka zachycených stavů není platná!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Vyberte alespoň jednu složku, kterou zálohovat!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} není složka." #: common/config.py:457 #, fuzzy 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:467 common/config.py:514 #, fuzzy, 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 zde oprávnění k zápisu?" #: common/config.py:484 #, 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:493 #, fuzzy, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopírovat až cíle odkazů (obejde symbolické odkazy)" #: common/config.py:498 msgid "Expert Options" msgstr "Pokročilé předvolby" #: common/config.py:502 #, 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:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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í." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Nepodařilo se zapsat novou úlohu (crontab)." #: common/config.py:1707 #, fuzzy, 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:1722 #, fuzzy, 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:1733 #, fuzzy, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil „{name}“ už existuje." #: common/configfile.py:736 #, fuzzy msgid "The last profile cannot be removed." msgstr "Toto nelze vzít zpět." #: common/encfstools.py:92 #, fuzzy, python-brace-format msgid "Can't mount '{command}'" msgstr "Nedaří se připojit (mount) „{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 password" msgstr "Potvrďte heslo" #: common/encfstools.py:160 #, fuzzy msgid "Password doesn't match." msgstr "Zadání hesla se neshodují." #: common/encfstools.py:178 #, fuzzy msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" "Implementace encfs verze 1.7.2 a starší obsahují chybu v předvolbě --reverse" " . Přejděte na novější verzi encfs." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Pořídit zachycený stav" #: common/mount.py:608 #, fuzzy, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Nedaří se odpojit (umount) {mountprocess} z {mountpoint}." #: common/mount.py:695 #, fuzzy msgid "{} not found. Please install e.g. {}" msgstr "" "%(proc)s nebylo nalezeno. Nainstalujte např. pomocí %(install_command)s" #: common/mount.py:716 #, fuzzy msgid "Mountpoint {} not empty." msgstr "" "Přípojný bod (složka) %s není prázdný (obsahuje soubory a/nebo složky)." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil „{profile}“: Zadejte heslo pro {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "NEZDAŘILO SE" #: common/snapshots.py:539 common/snapshots.py:601 #, fuzzy msgid "Restore permissions" msgstr "Obnovit přístupová práva" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Dokončeno" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Zálohování odloženo po dobu napájení z akumulátoru" #: common/snapshots.py:770 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:772 #, 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:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Pořízení zachyceného stavu {snapshot_id} se nezdařilo." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Dokončování" #: common/snapshots.py:949 #, fuzzy msgid "Can't create folder" msgstr "Nedaří se vytvořit složku" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Ukládání souboru s nastaveními…" #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Ukládání přístupových práv…" #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Složku se nepodařilo odstranit" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Pořizování zachyceného stavu" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Nic se nezměnilo, nový zachycený stav není třeba" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Nepodařilo se přejmenovat {new_path} na {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Postupné odstranění" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Odstranění starých zachycených stavů" #: common/snapshots.py:1698 #, fuzzy msgid "Trying to keep min free space" msgstr "Pokus o zachování neobsazeného prostoru, jak je nastaveno" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" "Pokus o zachování alespoň {perc} volných pozic pro soubory a složky (inode)" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Nyní" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs} se nedaří připojit (mount)" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 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:506 #, fuzzy, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Použití šifry {cipher} se na stroji {host}." #: common/sshtools.py:653 #, fuzzy msgid "Remote path exists but is not a directory." msgstr "Umístění na protistraně existuje, ale není to složka." #: common/sshtools.py:658 #, fuzzy msgid "Remote path is not writable." msgstr "Do vzdáleného umístění nelze zapisovat." #: common/sshtools.py:663 #, fuzzy 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:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Umístění na protistraně se nedaří vytvořit." #: common/sshtools.py:948 #, fuzzy, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Protějšek, stroj {host} nepodporuje {command}" #: common/sshtools.py:952 common/sshtools.py:961 #, fuzzy msgid "Look at 'man backintime' for further instructions" msgstr "Další pokyny naleznete v manuálové stránce aplikace (man backintime)" #: common/sshtools.py:956 #, fuzzy, 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:977 #, fuzzy, 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:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Vyplňte heslo pro „{user}“" #: qt/app.py:167 msgid "Shortcuts" msgstr "Klávesové zkratky" #: qt/app.py:187 #, fuzzy msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Tato složka v aktuálně označeném\n" "zachyceném stavu neexistuje." #: qt/app.py:252 msgid "Add to Include" msgstr "Přidat do seznamu zálohovaných" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Přidat do seznamu vynechaných" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "Aplikace {appName} postrádá nastavení. Chcete obnovit ta dřívější?" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "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:453 #, fuzzy msgid "Take a snapshot" msgstr "Pořídit zachycený stav" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 #, fuzzy msgid "Take a snapshot (checksum mode)" msgstr "Pořídit zachycený stav a opatřit ho kontrolními součty" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "" "Zda se soubory, určené k zálohování změnily zjišťovat pomocí jejich " "kontrolního součtu (přesnější)." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Pozastavit pořizování zachyceného stavu" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Pokračovat v pořizování zachyceného stavu" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Ukončit toto pořizování zachyceného stavu" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Znovu načíst seznam zachycených stavů" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Pořídit zachycený stav" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Odstranit zachycený stav" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Zobrazit záznam událostí při pořizování zachyceného stavu" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Zobrazit záznam nedávných událostí" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Hlavní profil" #: qt/app.py:500 msgid "Shutdown" msgstr "Vypnout počítač po dokončení úlohy" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Po pořízení zachyceného stavu vypnout počítač." #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Ukončit aplikaci" #: qt/app.py:512 msgid "Help" msgstr "Nápověda" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Ukládání souboru s nastaveními" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Webové stránky projektu" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Co je v aplikaci nového" #: qt/app.py:525 msgid "FAQ" msgstr "Často kladené dotazy (FAQ)" #: qt/app.py:528 msgid "Ask a question" msgstr "Položit dotaz" #: qt/app.py:531 msgid "Report a bug" msgstr "Nahlásit chybu / dát podnět" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "Překladatelé" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "O aplikaci" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Obnovit" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Obnovit do…" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Přejít do nadřazené složky" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Zobrazovat skryté soubory" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Pořídit zachycený stav" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "&Obnovit" #: qt/app.py:644 msgid "&Help" msgstr "&Nápověda" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" 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:905 msgid "Working:" msgstr "Zpracovává se:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Dokončeno, záloha není potřebná" #: qt/app.py:962 msgid "Working" msgstr "Zpracovává se" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Chyba" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Odesláno" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Rychlost" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Odhad. zbývající čas" #: qt/app.py:1050 msgid "Global" msgstr "Celkové" #: qt/app.py:1051 msgid "Root" msgstr "Kořen" #: qt/app.py:1052 msgid "Home" msgstr "Domovská složka" #: qt/app.py:1067 msgid "Backup folders" msgstr "Složky pro zálohu" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nazvat zachycený stav" #: qt/app.py:1202 #, fuzzy 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ý stav" msgstr[2] "Opravdu chcete odstranit zachycený stav" #: qt/app.py:1294 #, fuzzy, 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:1300 qt/settingsdialog.py:774 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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/app.py:1314 #, fuzzy 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:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "Odebrat novější soubory v původní složce" #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "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!\n" "Buďte velmi opatrní!!!" #: qt/app.py:1361 #, 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] "" "Opravdu chcete obnovit tyto soubory\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:1370 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Opravdu chcete obnovit tyto soubory" msgstr[1] "Opravdu chcete obnovit tyto soubory" msgstr[2] "Opravdu chcete obnovit tyto soubory" #: qt/app.py:1385 #, 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:1388 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:1393 #, fuzzy msgid "" "WARNING: Deleting files in filesystem root could break your whole 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:1623 msgid "Snapshot" msgstr "Zachycený stav" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Obnovit {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Obnovit {path} do…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Vývojáři" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Překladatelé" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licence" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 #, fuzzy msgid "System default" msgstr "výchozí" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 #, fuzzy msgid "translation platform" msgstr "Překladatelé" #: qt/languagedialog.py:232 #, fuzzy msgid "Your translation" msgstr "Překladatelé" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Zobrazení nedávných událostí" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Zobrazení událostí při pořizování zachyceného stavu" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Zachycené stavy" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtr" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Vše" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Změny" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Chyby" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Informace" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Chyba (Error), [I] Informace, [C] Změna (Change)" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "dekódovat popisy umístění" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopírovat" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Dekódovat" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Opravdu toto chcete vynechat?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Dotaz" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Zobrazit záznam nedávných událostí" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Spustit {appname}" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Zpracovává se" #: qt/qttools.py:370 msgid "Today" msgstr "Dnes" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Včera" #: qt/qttools.py:386 msgid "This week" msgstr "Tento týden" #: qt/qttools.py:393 msgid "Last week" msgstr "Minulý týden" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Minulá kontrola {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Zobrazit kompletní záznam událostí" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Hlavní profil" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Přejmenovat" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Přidat" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Odebrat" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Obecné" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Ukládání záloh" #: qt/settingsdialog.py:152 #, 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} š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)." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Kam ukládat zachycené stavy" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Složka" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Nastavení pro SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Stroj" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Uživatel" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Umístění" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Šifra" #: qt/settingsdialog.py:226 #, fuzzy msgid "Private Key" msgstr "Soukromá část klíče" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Heslo pro" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Uložit heslo do klíčenky desktopového prostředí" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Pokročilé" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Úplný popis umístění zachyceného stavu" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Automatické zálohování" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Vypnuto" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Při každém startu/restartu počítače" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Každých {n} minut" msgstr[1] "Každých {n} minut" msgstr[2] "Každých {n} minut" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Každou hodinu" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Každých {n} hodin" msgstr[1] "Každých {n} hodin" msgstr[2] "Každých {n} hodin" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Vlastní interval" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Každý den" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Opakovaně (jako anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Při připojení (flash)disku (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Každý týden" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Každý měsíc" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Každý rok" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Den" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Den v týdnu" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "V hodinu" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "V hodiny" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Každých" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Hodin" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dnů" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Týdnů" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Měsíců" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Zahrnout" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Zahrnout soubory a složky" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Přidat soubor" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Přidat složku" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "Vyn&echat" #: qt/settingsdialog.py:528 #, fuzzy, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Zástupná vyjádření ({example1}) budou v režimu ukládání záloh „Na jiném stroji (skrze SSH tunel) – šifrováno“ přehlížena.\n" "V tomto případě je možné použít pouze zápis s oddělenými hvězdičkami ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Vynechat zástupně vyjádřené, soubory a složky" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "Důrazně doporučeno" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Přidat výchozí" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Vynechat soubory větší než: " #: qt/settingsdialog.py:594 #, fuzzy, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." 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:616 msgid "&Auto-remove" msgstr "&Automatické odstranění" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Starší než" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Let" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Pokud zbývá volného místa méně než" #: qt/settingsdialog.py:664 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:678 #, fuzzy msgid "Smart remove:" msgstr "Postupné odstranění" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "Na protistraně spustit na pozadí." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EXPERIMENTÁLNÍ" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Ponechávat všechny zachycené stavy za uplynulé" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "dny" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Ponechávat jeden zachycený stav za den z uplynulých" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Ponechávat jeden zachycený stav za týden za uplynulých" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "týdnů" #: qt/settingsdialog.py:717 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:721 #, fuzzy msgid "month(s)." msgstr "měsíců" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Ponechat jeden zachycený stav co rok za všechna leta" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Neodstraňovat ty zachycené stavy, které uživatel nějak nazval" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "Př&edvolby" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "" "Zobrazovat oznámení (prostřednictvím centra oznámení desktopového prostředí)" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Vypnout pořizování zachycených stavů při provozu na akumulátor" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Údaje o stavu napájení nejsou dispozici" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Pořizovat pouze jeden zachycený stav naráz" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "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:772 msgid "Backup replaced files on restore" msgstr "Zazálohovat soubory, které by byly při procesu obnovování nahrazeny" #: qt/settingsdialog.py:785 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:789 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:793 #, fuzzy 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:800 msgid "Log Level" msgstr "Podrobnost zaznamenávání událostí (log)" #: qt/settingsdialog.py:805 msgid "None" msgstr "Nic" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "&Pokročilé předvolby" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Tyto předvolby měňte pouze v případě, že opravdu víte co děláte." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Spustit nástroj rsync s parametrem '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "když jako naplánovaná úloha (cron)" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "při zálohování na jiný stroj" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "při ručně spuštěném pořízení zachyceného stavu" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(pokud chcete zapnout tuto možnost, nainstalujte „nocache“)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "při ukládání záloh přímo na zálohovaném počítači" #: qt/settingsdialog.py:894 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:903 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:915 msgid "Limit rsync bandwidth usage" msgstr "Omezit využití přenosové kapacity, zabrané nástrojem rsync, na" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Zálohovat včetně seznamů řízení přístupu (ACL)" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Zálohovat včetně rozšířených atributů (xattr)" #: qt/settingsdialog.py:991 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:1024 #, fuzzy, 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:1027 msgid "Paste additional options to rsync" msgstr "Dodatečné parametry, se kterými spouštět nástroj rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Čím uvést každý z příkazů, spouštěným na stroji na protějšku.\n" "Proměnné je třeba zbavit jejich významu pomocí zpětného lomítka (příklad: \\$NECO).\n" "Toto se nedotýká nástroje rsync. Pro ten použijte předvolbu „%(cbRsyncOptions)s“ s\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "výchozí" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Před příkazy přes SSH přidávat" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Zkontrolovat zda je stroj na protistraně dostupný na síti" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "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:1071 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:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "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:1089 msgid "Restore Config" msgstr "Obnovit nastavení" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Upravit uživatelsky definované akce" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nový profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Přejmenovat profil" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Opravdu chcete profil „{name}“ smazat?" #: qt/settingsdialog.py:1416 #, 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 "" "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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "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:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Soubor „{file}“ se soukromou částí klíče neexistuje." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "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:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Nepodvrženost stroje „{host}“ se nepodařilo ověřit.\n" "\n" "{keytype} otisk klíče je:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Zástupné vyjádření vynechaného" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Vynechat soubor" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Vynechat složku" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Zahrnout soubor" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Zahrnout složku" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Opravdu změnit složku se zachycenými stavy?" #: qt/settingsdialog.py:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Úplný popis umístění zachyceného stavu: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "zapnuto" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "vypnuto" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Obnovit dřívější nastavení" #: qt/settingsdialog.py:2125 #, fuzzy, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Přejděte k zachycenému stavu, ze kterého chcete obnovit nastavení pro {appName}. Popis umístění může vypadat nějak takto: \n" "{samplePath}\n" "\n" "Pokud se zachycené stavy nacházejí na síťovém úložišti nebo jsou šifrované, bude třeba toto umístění nejprve ručně připojit. V případě, že jsou zálohy ukládány na vzdálený stroj prostřednictvím SSH tunelu, je také třeba zajistit přihlašování k tomuto stroji pomocí klíče.\n" "Další pokyny naleznete v manuálové stránce aplikace (man backintime)." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Nebyla nalezena žádná nastavení" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" "Skript s uživatelsky definovanými akcemi postrádá řádek určující jeho " "interpret (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" "Soubor se skriptem uživatelsky definovaných akcí nemá nastavený příznak " "spustitelné." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Příkaz" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Parametry" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Popisy umístění vyjádřete parametry %1 a %2" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Pouze odlišující se zachycené stavy" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Vypsat pouze zachycené stavy, shodné s: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Hloubková kontrola (přesnější, ale pomalejší)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Smazat" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Vybrat vše" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Přejít na" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Předv&olby" #: qt/snapshotsdialog.py:330 #, fuzzy 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:338 msgid "Command not found" msgstr "Příkaz nenalezen" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Opravdu chcete vymazat „{file}“ ze zachyceného stavu „{snapshot_id}“?" #: qt/snapshotsdialog.py:375 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Opravdu chcete vymazat „{file}“ z(e) {count} zachycených stavů?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Toto nelze vzít zpět!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "VAROVÁNÍ" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Vynechat „{path}“ z budoucích zachycených stavů?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " a přidejte svůj uživatelský účet do systémové skupiny „fuse“" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "„%s“ je symbolický odkaz. Odkazovaný cíl nebude zazálohován, pokud jeho umístění výslovně nezahrnete.\n" #~ "Zahrnout tedy cíl symbolického odkazu namísto odkazu samotného?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Tento záznam událostí byl dekódován pomocí automatického vyhledávacího vzorce\n" #~ "### Pokud některá umístění nejsou dekódována, můžete je dekódovat ručně, pomocí:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s není členem skupiny fuse.\n" #~ " Spusťte příkaz: sudo adduser %(user)s fuse . Aby se změny projevily, bude nutné se odhlásit a znovu přihlásit.\n" #~ "Další pokyny naleznete v manuálové stránce aplikace (man backintime)." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s nebyl nalezen v souboru ssh_known_hosts." #~ msgid "&Snapshot" #~ msgstr "Zachycený &stav" #~ msgid "&View" #~ msgstr "&Zobrazit" #~ msgid "..." #~ msgstr "…" #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Změny a chyby" #~ msgid "Config File Help" #~ msgstr "Nápověda k souboru s nastaveními" #~ msgid "Create a new SSH key without Password." #~ msgstr "Vytvořte nový SSH klíč, nechráněný heslem." #~ msgid "Diff" #~ msgstr "Porovnat" #~ msgid "Diff Options" #~ msgstr "Předvolby porovnávání (diff)" #~ msgid "Error:" #~ msgstr "Chyba:" #~ msgid "Every 10 minutes" #~ msgstr "Každých 10 minut" #~ msgid "Every 12 hours" #~ msgstr "Každých 12 hodin" #~ msgid "Every 30 minutes" #~ msgstr "Každých 30 minut" #~ msgid "Every 4 hours" #~ msgstr "Každé 4 hodiny" #~ msgid "Every 5 minutes" #~ msgstr "Každých 5 minut" #~ msgid "Every 6 hours" #~ msgstr "Každých 6 hodin" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Zachycený stav, pořízený pomocí funkce kompletní záloha systému, je s jistotou možné obnovit pouze na stejnou jednotku datového úložiště, ze které byl pořízen a to ještě za předpokladu, že rozvržení oddílů na ní bude stejné, jaké je v zachyceném stavu. Pokus o obnovení na jednotku lišící se kapacitou a/nebo rozvržením oddílů může vyústit v rozbitý a tím nepoužitelný systém.\n" #~ "\n" #~ "Úprava profilu pro účely kompletní zálohy systému může vést ke změně některých nastavení, které jste v něm mohli udělat. Přesto pokračovat?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Vyskytly se dva stejné otisky v hash_id %s. Zvyšte celkovou hodnotu " #~ "parametru hash_collision a zkuste to znovu." #~ msgid "Key File" #~ msgstr "Soubor s klíčem" #~ msgid "List only different snapshots" #~ msgstr "Vypsat pouze odlišné zachycené stavy" #~ msgid "Local encrypted" #~ msgstr "Na tomto počítači – v šifrované podobě" #~ msgid "Modify for Full System Backup" #~ msgstr "Upravit pro účely kompletní zálohy systému" #~ msgid "Mountprocess lock timeout" #~ msgstr "Časový limit zámku procesu připojování (mount)" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "Ověření uživatele %(user)s na stroji %(host)s pomocí ssh klíče se nezdařilo." #~ " Další pokyny naleznete v manuálové stránce aplikace (man backintime)." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "" #~ "Ověření dostupnosti stroje (ping) %s nebylo úspěšné. Stroj je buď vypnutý, " #~ "nebo je zadaná nesprávná IP adresa." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: „{name}“" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Obnovit „%s“" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Obnovit „%s“ na…" #, fuzzy, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Obnovit označený soubor či složku.\n" #~ "Pokud je toto tlačítko nedostupné (vyšedlé), je to nejspíš tím, že v seznamu zachycených stavů (vlevo) je označena položka „{now}“." #~ msgid "Run 'ionice':" #~ msgstr "Spustit s nižší prioritou na vst./výst. (ionice):" #~ msgid "Run 'nice':" #~ msgstr "Spustit s nižší prioritou (nice):" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Spustit nástroj rsync s parametrem nocache:" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Spustit nástroj rsync s parametrem nocache:" #~ msgid "SSH" #~ msgstr "Na jiném stroji (skrze SSH tunel)" #~ msgid "Settings" #~ msgstr "Nastavení" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Zachycený stav: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Zobrazit stávající data" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Zobrazit zachycený stav pořízený k {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "S CHYBAMI!" #~ msgid "Working..." #~ msgstr "Zpracování…" #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "" #~ "Složku se zálohami nelze zahrnout mezi zálohované položky (zacyklení)!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "" #~ "Podsložku složky se zálohami nelze zahrnout mezi zálohované položky " #~ "(zacyklení)!" #~ msgid "You can't remove the last profile!" #~ msgstr "Nelze odebrat poslední zbývající profil!" backintime-1.4.3/common/po/da.po000066400000000000000000001405571455673541400165110ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-17 12:56+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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Advarsel" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "hovedprofil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokal" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH privat nøgle" #: common/config.py:304 #, fuzzy msgid "encrypted" msgstr "SSH krypteret" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Kryptering" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH krypteret" #: common/config.py:317 msgid "Default" msgstr "Standard" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 #, fuzzy msgid "Snapshots folder is not valid!" msgstr "Snapshots mappe er ikke gyldig !" #: common/config.py:361 #, fuzzy 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:378 msgid "Backup folder cannot be included." msgstr "Backup-mappe kan ikke inkluderes." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Backup-undermappe kan ikke inkluderes." #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} er ikke en mappe." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Maskine/Bruger/Profil-ID kan ikke være tom." #: common/config.py:467 common/config.py:514 #, fuzzy, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopier links (følg symbolske links)" #: common/config.py:498 msgid "Expert Options" msgstr "Avancerede indstillinger" #: common/config.py:502 #, 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:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Kan ikke finde crontab.\n" "Er du sikker på, at cron er installeret ?\n" "Hvis ikke, skal du deaktivere alle automatiske sikkerhedskopieringer." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Kunne ikke skrive ny crontab." #: common/config.py:1707 #, 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:1722 #, fuzzy, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Skedulere udev virker ikke med indstilling {mode}" #: common/config.py:1733 #, fuzzy, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" findes allerede." #: common/configfile.py:736 msgid "The last profile cannot be removed." msgstr "Den sidste profil kan ikke slettes." #: common/encfstools.py:92 #, fuzzy, 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 #, fuzzy 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 password" msgstr "Bekræft venligst kodeord" #: common/encfstools.py:160 #, fuzzy msgid "Password doesn't match." msgstr "Kodeord passer ikke." #: common/encfstools.py:178 #, 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Tag et tilstands-billede" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan ikke fjerne {mountprocess} fra {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} ikke fundet. Installér venligst for eksempel {}" #: common/mount.py:716 #, fuzzy msgid "Mountpoint {} not empty." msgstr "tilsluttelsespunkt %s ikke tomt." #: common/password.py:240 #, fuzzy, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}': Indtast kodeord for {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "MISLYKKEDES" #: common/snapshots.py:539 common/snapshots.py:601 #, fuzzy msgid "Restore permissions" msgstr "Gendan tilladelser" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Færdig" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Venter med backup under batteridrift" #: common/snapshots.py:770 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Kan ikke finde tilstands-billede-mappen.\n" "Hvis den er på et flytbart drev indsæt venligst dette." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Venter %s sekund." msgstr[1] "Venter %s sekunder." #: common/snapshots.py:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Kunne ikke tage tilstands-billede {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Afslutter" #: common/snapshots.py:949 #, fuzzy msgid "Can't create folder" msgstr "Kan ikke oprette mappen" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Gem config fil …" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Gem tilladelse …" #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 #, fuzzy msgid "Can't remove folder" msgstr "Kan ikke fjerne mappen" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Tag et tilstands-billede" #: common/snapshots.py:1254 msgid "Success" msgstr "Succes" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' afsluttede med slutværdien {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Kig i 'man rsync' for flere detaljer" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Ingen ændringer, så et nyt tilstands-billede er ikke nødvendigt" #: common/snapshots.py:1318 #, fuzzy, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Kan ikke omdøbe {new_path} til {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Smart fjern" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Fjern gamle tilstands-billeder" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Prøv at holde minimum fri plads" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Prøv at beholde minimum {perc} inoder fri" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Nu" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan ikke tilslutte {sshfs}" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Chiffer {cipher} fungerede ikke for {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Stien findes i den anden ende, men er ikke en mappe." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Stien i den anden ende er ikke skrivbar." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Stien i den anden ende er ikke eksekverbar." #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Kan ikke oprette mappen." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Den anden maskine {host} understøtter ikke {command}" #: common/sshtools.py:952 common/sshtools.py:961 #, fuzzy msgid "Look at 'man backintime' for further instructions" msgstr "Se 'man backintime' for yderlig informationer" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Den anden maskine {host} understøtter ikke hardlinks" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "Kopiér offentligt ssh-nøgle \"{pubkey}\" til vært \"{host}\"" #: common/sshtools.py:1062 #, fuzzy, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Bekræft venligst kodeord" #: qt/app.py:167 msgid "Shortcuts" msgstr "Genveje" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Tilføj til Inkludér" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Tilføj til Spring over" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} er ikke konfigureret. Vil du rulle tilbage til en tidligere " "opsætning?" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Kan ikke finde tilstands-billede-mappen.\n" "Hvis den er på et flytbar drev, indsæt venligst drevet og tryk OK." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "Tag et tilstands-billede" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "Brug ændringstid og størrelse til at opdage filændringer." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Opret et tilstands-billede (checksum modus)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Brug checksummer til at opdage filændringer." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Sæt tilstands-afbildning på pause" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Genoptag tilstands-afbildning" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Stop tilstands-afbildning" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Hvor skal tilstands-billeder gemmes" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Tag et tilstands-billede" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Fjern tilstands-billede" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Tag et tilstands-billede" #: qt/app.py:492 msgid "View last log" msgstr "Vis seneste log" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "hovedprofil" #: qt/app.py:500 msgid "Shutdown" msgstr "Luk ned" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Luk ned når tilstands-billedet er færdiggjort." #: qt/app.py:504 msgid "Setup language…" msgstr "Sæt sprog op…" #: qt/app.py:508 msgid "Exit" msgstr "Afslut" #: qt/app.py:512 msgid "Help" msgstr "Hjælp" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Gem config fil" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Websted" #: qt/app.py:522 qt/app.py:1261 #, fuzzy msgid "Changelog" msgstr "Ændringer" #: qt/app.py:525 msgid "FAQ" msgstr "OSS" #: qt/app.py:528 msgid "Ask a question" msgstr "Stil et spørgsmål" #: qt/app.py:531 msgid "Report a bug" msgstr "Rapporter en fejl" #: qt/app.py:534 msgid "Translation" msgstr "Oversættelse" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Om" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Gendan" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Gendan" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Op" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Vis skjulte filer" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Tag et tilstands-billede" #: qt/app.py:627 msgid "&Backup" msgstr "&Sikkerhedskopiér" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "&Gendan" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "&Hjælp" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Hvis du lukker dette vindue så kan Back In Time ikke lukke din maskine ned når tilstands-billedet er færdigt.\n" "Vil du virkelig lukke?" #: qt/app.py:905 msgid "Working:" msgstr "Arbejder:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Færdig, ingen sikkerhedskopiering nødvendig" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Arbejder" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Fejl" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Sendt" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Hastighed" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Forventet afslutning" #: qt/app.py:1050 msgid "Global" msgstr "Globalt" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Hjemmemappe" #: qt/app.py:1067 msgid "Backup folders" msgstr "Sikkerhedskopi-mapper" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Tilstands-billede-navn" #: qt/app.py:1202 #, fuzzy 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-billedet" #: qt/app.py:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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.\n" "Hvis du ikke har brug for dem længere, kan de fjernes med {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Fjern nyere elementer i oprindelsesmappen." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Gendan valgte filer eller mapper på den oprindelige plads og\n" "slet filer eller mapper som ikke er i tilstands-billedet.\n" "Vær ekstremt forsigtig for dette vil\n" "slette filer og mapper som blev\n" "sprunget over da tilstands-billedet blev dannet." #: qt/app.py:1361 #, 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] "Kunne ikke tage tilstands-billede {snapshot_id} !!!" msgstr[1] "Kunne ikke tage tilstands-billede {snapshot_id} !!!" #: qt/app.py:1370 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Ønsker du at udelukke dette?" msgstr[1] "Ønsker du at udelukke dette?" #: qt/app.py:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "ADVARSEL: Hvis du sletter filer i filsystemets rod risikerer du at ødelægge " "hele systemet!" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Tilstamds-billeder" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Gendan {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Gendan {path}" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "Sprogopsætningen ændres først efter genstart af Back In Time." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Forfattere" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Oversættelser" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licens" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Sæt sprog op" #: qt/languagedialog.py:87 msgid "System default" msgstr "Systemstandard" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Brug styresystemets sprog." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Oversat: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "oversættelses platform" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Din oversættelse" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Se Sidste Log" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Se Snapshot Log" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Tilstands-billeder" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Alles" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Ændringer" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Fejl" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Information" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Fejl, [I] Information, [C] Ændre" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "afkod paths" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopier" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Afkode" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Ønsker du at udelukke dette?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Spørgsmål" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Se Sidste Log" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Kør {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Arbejder…" #: qt/qttools.py:370 msgid "Today" msgstr "I dag" #: qt/qttools.py:377 msgid "Yesterday" msgstr "I går" #: qt/qttools.py:386 msgid "This week" msgstr "Denne uge" #: qt/qttools.py:393 msgid "Last week" msgstr "Sidste uge" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Sidste check {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Vis Fulde Log" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "hovedprofil" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Redigér" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Tilføj" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Fjern" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Generelt" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Tilstand" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Hvor skal tilstands-billeder gemmes" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Folder" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH Indstillinger" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Vært" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Bruger" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Sti" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Cipher" #: qt/settingsdialog.py:226 #, fuzzy msgid "Private Key" msgstr "SSH privat nøgle" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Kodeord" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Gem kodeord i nøglering" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Avanceret" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Fuld sti til tilstands-billeder" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Planlæg" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Deaktiveret" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Ved hver opstart/genstart" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Hver {n} minutter" msgstr[1] "Hver {n} minutter" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Hver time" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Hver {n} time" msgstr[1] "Hver {n} time" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Angiv interval" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Hver dag" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Gentag (anacron)" #: qt/settingsdialog.py:375 #, fuzzy msgid "When drive gets connected (udev)" msgstr "Når drev bliver tilsluttet (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Hver uge" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Hver måned" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Hvert år" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Dag(e)" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Ugedag" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Time" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Timer" #: qt/settingsdialog.py:435 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:442 #, fuzzy msgid "Every" msgstr "Hver" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Time(r)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dag(e)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Uge(r)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Måned(er)" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Inkludér" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Inkludér filer og mapper" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Tilføj fil" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Tilføj mappe" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Ekskludér" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Wildcards ({example1}) bliver ignoreret ved 'SSH encrypted'.\n" "Kun en eller to asterisker er tilladt ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Ekskludéringmønstre, filer eller mapper" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Højest anbefalet" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Tilføj standard" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Ekskludér filer større end: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Ekskludér filer større end værdien i %(prefix)s.\n" "Med 'Full rsync mode' slået fra påvirker dette kun nye filer\n" "da dette er en overførselsesparameter for rsync, ikke en ekskluderings parameter.\n" "Så store filer der tidligere er overført forbliver i tilstands-billederne\n" "selv hvis de er ændret." #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "&Auto-fjern" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Ældre end" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "År" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Hvis fri plads er mindre end" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Hvis fri plads er mindre end" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Smart fjern" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Kør i bagrunden på fjernværten." #: qt/settingsdialog.py:690 #, fuzzy msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Behold alle tilstands-billeder for de seneste" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "Dag(e)." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Behold et tilstands-billede per dag for de seneste" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Behold et tilstands-billede per uge for de seneste" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "Uge(r)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Behold et tilstands-billede per måned for de seneste" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Behold et tilstands-billede per år for alle år." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Fjern ikke navngivne tilstands-billeder." #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "I&ndstillinger" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Aktivér bekendtgørelser" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "De-aktivér tilstands-billeder ved batteri-drift" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Strømstatus ikke tilgængelig fra system" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Dan kun et tilstands-billede af gangen" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "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.\n" "Dette er et globalt valg. Så det påvirker alle profiler for denne bruger.\n" "Men du bliver nød til at aktivere det for alle andre brugere også." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Sikkerhedskopiering erstattede filer ved genddannelse" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Fortsæt ved fejl (behold ufuldstændige tilstands-billeder)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Brug checksummer til at finde ændringer" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Logniveau" #: qt/settingsdialog.py:805 msgid "None" msgstr "Ingen" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "A&vancerede indstillinger" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Kør 'rsync' med '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "som et cronjob" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "på en fjernvært" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "når et manuelt tilstands-billede dannes" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Installér venlist 'nocache' for at kunne vælge denne mulighed)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "på lokal maskine" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Send stdout til /dev/null i cronjobs." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Send stderr til /dev/null i cronjobs." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Begræns rsync's båndbreddeforbrug" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Behold ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Behold udvidede attributter (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopier usikre links (virker kun med absolutte links)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Værdier skal i citationstegn, for eksempel {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Indsæt yderligere parametre til rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "standard" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Foranstil alle SSH kommandoer" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Undersøg om fjernvært er på nettet" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Advarsel: hvis dette er slået fra og fjernværten\n" "ikke er tilgængelig, risikerer du at få\n" "nogle besynderlige fejl." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Undersøg om fjernværten understøtter alle nødvendige kommandoer" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Advarsel: hvis dette er slået fra og fjernværten\n" "ikke understøtter alle nødvendige kommandoer\n" "risikerer du at få nogle besynderlige fejl." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Gendan opsætning" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Redigér bruger-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Ny profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Omdøb profil" #: qt/settingsdialog.py:1142 #, fuzzy, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Du har ikke valgt en privat nøglefil til SSH.\n" "Vil du danne et nyt offentligt/private nøglepar uden kodeord?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Den private nøglefil \"{file}\" mangler." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Vil du kopiere din offentlige SSH-nøgle til\n" "fjernværten for at gøre login uden kodeord mulig?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Autenticiteten af værten {host} can ikke afgøres.\n" "\n" "{keytype} nøglefingeraftryk er:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Ekskludér mønster" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Ekskludér fil" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Ekskludér mappe" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Inkludér fil" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Inkludér mapper" #: qt/settingsdialog.py:1930 #, fuzzy 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:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Fuld sti til tilstands-billeder: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "slået til" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "slået fra" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Genddan opsætning" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Ingen opsætning fundet" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Valgmuligheder om sammenligning af tilstands-billeder" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command" msgstr "Kommando" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters" msgstr "Parametre" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Brug %1 og %2 som sti-parametre" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Kun tilstands-billeder der er forskellige" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Vis kun tilstands-billeder der er ens med: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Grundig sammenligning (mere præcis, men langsom)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Slet" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Vælg alle" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Sammenlign" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Gå til" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Indstillinger" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Du kan ikke sammenligne et tilstands-billede med sig selv." #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "Kommando ikke fundet" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Kunne ikke tage tilstands-billede {snapshot_id} !!!" #: qt/snapshotsdialog.py:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Dette kan ikke tilbagekaldes!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ADVARSEL" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Ekskludér {path} fra tilstands-billeder fremover?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Denne log er blevet afkodet med automatisk søge mønster\n" #~ "### Hvis nogle stier ikke er afkodet kan du manuelt afkode dem med:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s er ikke medlem af gruppe 'fuse'.\n" #~ " Kør 'sudo adduser %(user)s fuse'. Bekræft ændringerne ved at logge ud og ind igen.\n" #~ "Se 'man backintime' for yderlig information." #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Tilstamds-billeder" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Diff" #~ msgstr "Diff" #~ msgid "Diff Options" #~ msgstr "Diff-indstillinger" #~ msgid "Error:" #~ msgstr "Fejl:" #~ msgid "Every 10 minutes" #~ msgstr "Hvert 10. minut" #~ msgid "Every 12 hours" #~ msgstr "Hver 12. time" #~ msgid "Every 30 minutes" #~ msgstr "Hvert 30. minut" #~ msgid "Every 4 hours" #~ msgstr "Hver 4. time" #~ msgid "Every 5 minutes" #~ msgstr "Hver 5 minutter" #~ msgid "Every 6 hours" #~ msgstr "Hver 6. time" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Hash kollision skete i hash_id %s. Forøg den globale værdi hash_collision og" #~ " forsøg igen." #~ msgid "Local encrypted" #~ msgstr "Lokal krypteret" #~ msgid "Modify for Full System Backup" #~ msgstr "Modificer til Fuld System Backup" #~ msgid "Mountprocess lock timeout" #~ msgstr "Lås af tilslutningsproces udløbet" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Indstillinger" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Tilstands-billede: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Se nuværende disk-indhold" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Se tilstands-billedet lavet ved {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "MED FEJL!" #~ msgid "Working..." #~ msgstr "Arbejder..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Du kan ikke inkludere sikkerhedskopierings-mappen !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Du kan ikke inkludere en sikkerhedskopierings-mappe !" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Du kan ikke fjerne den sidste profil !" backintime-1.4.3/common/po/de.po000066400000000000000000001547551455673541400165220ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-24 11:34+0000\n" "Last-Translator: zvavybir \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Warnung" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Hauptprofil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokal" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Privater SSH Schlüssel" #: common/config.py:304 msgid "encrypted" msgstr "verschlüsselt" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Verschlüsselung" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH verschlüsselt" #: common/config.py:317 msgid "Default" msgstr "Standard" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: »{name}«" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Schnappschussordner ist ungültig!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Mindestens ein Ordner muss zum Sichern ausgewählt werden!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Ein Sicherungsordner kann nicht einbezogen werden." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Ein Unter-Sicherungsordner kann nicht einbezogen werden." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ungültige Option. {path} ist kein Verzeichnis." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profil-ID darf nicht leer sein." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Verknüpfungen kopieren (symbolische Verknüpfungen zurückverfolgen)" #: common/config.py:498 msgid "Expert Options" msgstr "Experten-Einstellungen" #: common/config.py:502 #, 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 harten Verknüpfungen. Bitte benutzen Sie stattdessen den " "SSH-Modus." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Crontab wurde nicht gefunden.\n" "Sind Sie sicher, dass cron installiert ist?\n" "Wenn nicht, sollten Sie alle automatischen Sicherungen deaktivieren." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Schreiben einer neuen crontab ist fehlgeschlagen." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Udev-Zeitplan funktioniert nicht mit Modus {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil »{name}« bereits vorhanden." #: common/configfile.py:736 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 password" msgstr "Passwort bitte bestätigen" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Passwort stimmt nicht überein." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Schnappschuss erstellen" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kann {mountprocess} nicht von {mountpoint} aushängen." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} nicht gefunden. Bitte installieren z.B. mit {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Einhängepunkt {} ist nicht leer." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil »{profile}«: Bitte Passwort für {mode} eingeben: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "FEHLGESCHLAGEN" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Berechtigungen wiederherstellen" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Fertig" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Aufschieben der Sicherung im Akkubetrieb" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Warte %s Sekunde." msgstr[1] "Warte %s Sekunden." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Schnappschuss {snapshot_id} konnte nicht erstellt werden." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Finalisiere" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Verzeichnis kann nicht erstellt werden" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Konfigurationsdatei wird gespeichert …" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Zugriffsrechte werden gespeichert …" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" "Schnappschuss »{snapshot_id}« gefunden, welcher fortgesetzt werden kann." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Schnappschuss »{snapshot_id}« vom letzten Durchgang wird entfernt" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Ordner kann nicht entfernt werden" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Schnappschuss wird erstellt" #: common/snapshots.py:1254 msgid "Success" msgstr "Erfolgreich" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Partieller Transfer aufgrund von verschwundenen Quelldateien (siehe 'man " "rsync')" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' beendet mit Code {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Siehe 'man rsync' für weitere Details" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Keine Änderungen, es ist kein neuer Schnappschuss notwendig" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Umbenennen von {new_path} in {path} fehlgeschlagen" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Cleveres Löschen" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Alte Schnappschüsse werden entfernt" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Das Minimum an freiem Speicher wird freigehalten" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Es wird versucht mindestens {perc} freie Inodes zu behalten" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Jetzt" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs} kann nicht eingebunden werden" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Verschlüsselungsverfahren {cipher} für {host} fehlgeschlagen." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" "Der Pfad existiert auf dem entfernten Rechner, ist aber kein Verzeichnis." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Der Pfad auf dem entfernten Rechner ist nicht beschreibbar." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Der Pfad auf dem entfernten Rechner ist nicht ausführbar." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Erstellen des Pfades auf dem entfernten Rechner ist fehlgeschlagen." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Der entfernte Rechner {host} unterstützt {command} nicht" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Weitere Anweisungen unter »man backintime«" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Der entfernte Rechner {host} unterstützt keine Hardlinks" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Bitte Passwort für »{user}« eingeben" #: qt/app.py:167 msgid "Shortcuts" msgstr "Verknüpfungen" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Zum Einbeziehen hinzufügen" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Zum Ausschließen hinzufügen" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} ist noch nicht konfiguriert. Möchten Sie frühere Einstellungen " "wiederherstellen?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Schnappschussordner wurde nicht gefunden.\n" "Wenn er auf einem Wechsellaufwerk ist, bitte dieses verbinden und dann OK drücken." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Schnappschuss erstellen" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "Nutzt Änderungszeit und Größe um Änderungen an Dateien zu erkennen." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Schnappschuss erstellen (Prüfsummen Modus)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Prüfsumme benutzen, um Änderungen von Dateien zu erkennen." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Schnappschuss anhalten" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Schnappschuss fortsetzen" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Schnappschuss stoppen" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Schnappschussliste auffrischen" #: qt/app.py:480 msgid "Name snapshot" msgstr "Schnappschuss benennen" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Schnappschuss entfernen" #: qt/app.py:488 msgid "View snapshot log" msgstr "Schnappschussprotokoll ansehen" #: qt/app.py:492 msgid "View last log" msgstr "Letztes Protokoll ansehen" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Profile verwalten…" #: qt/app.py:500 msgid "Shutdown" msgstr "Herunterfahren" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Rechner herunterfahren, nachdem der Schnappschuss erstellt wurde." #: qt/app.py:504 msgid "Setup language…" msgstr "Sprache einstellen…" #: qt/app.py:508 msgid "Exit" msgstr "Beenden" #: qt/app.py:512 msgid "Help" msgstr "Hilfe" #: qt/app.py:516 msgid "Profiles config file" msgstr "Profile-Konfigurationsdatei" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Internetseite" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Änderungsprotokoll" #: qt/app.py:525 msgid "FAQ" msgstr "Häufige Fragen (FAQ)" #: qt/app.py:528 msgid "Ask a question" msgstr "Eine Frage stellen" #: qt/app.py:531 msgid "Report a bug" msgstr "Fehlerbericht einsenden" #: qt/app.py:534 msgid "Translation" msgstr "Übersetzung" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Über" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Wiederherstellen" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "Ausgewählte Dateien oder Ordner am Originalort wiederherstellen." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Wiederherstellen zu …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" "Ausgewählte Dateien oder Ordner an einer neuen Position wiederherstellen." #: qt/app.py:552 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:557 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:560 msgid "Up" msgstr "Hoch" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Versteckte Dateien anzeigen" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Schnappschüsse vergleichen…" #: qt/app.py:627 msgid "&Backup" msgstr "&Backup" #: qt/app.py:638 msgid "&Restore" msgstr "&Wiederherstellen" #: qt/app.py:644 msgid "&Help" msgstr "&Hilfe" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Wenn Sie das Fenster schließen, kann »Back In Time« den Rechner nicht herunterfahren wenn die Sicherung beendet ist.\n" "Wirklich schließen?" #: qt/app.py:905 msgid "Working:" msgstr "In Bearbeitung:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Fertig, keine Sicherung notwendig" #: qt/app.py:962 msgid "Working" msgstr "In Bearbeitung" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Fehler" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Gesendet" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Geschwindigkeit" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Verbleibend" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Wurzelverzeichnis" #: qt/app.py:1052 msgid "Home" msgstr "Persönlicher Ordner" #: qt/app.py:1067 msgid "Backup folders" msgstr "Sicherungsordner" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Schnappschussname" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "Neuere Dateiversionen werden mit einem angehängten {suffix} vor dem Wiederherstellen umbenannt.\n" "Wenn sie nicht mehr benötigt werden, können Sie sie mit {cmd} entfernen" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Neuere Elemente im Originalordner entfernen." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Ausgewählte Dateien oder Ordner am ursprünglichen Ort wiederherstellen und\n" "Dateien/Ordner löschen, die nicht in dem Schnappschuss enthalten sind.\n" "Seien Sie sehr vorsichtig,\n" "den damit werden Dateien und Ordner gelöscht,\n" "die während des Erstellen des Schnappschusses ausgeschlossen waren." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "WARNUNG: Das Löschen von Dateien im Wurzelverzeichnis kann das komplette " "System zerstören!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Schnappschuss" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Stelle {path} wieder her" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Stelle {path} wieder her nach …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Die Spracheinstellungen wirken erst, nach dem Back In Time erneut gestartet " "wurde." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Autoren" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Übersetzungen" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Lizenz" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Sprache einstellen" #: qt/languagedialog.py:87 msgid "System default" msgstr "Standardeinstellung des Systems" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Nutze Sprache des Betriebssystems." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Übersetzt: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "Übersetzungsplattform" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Ihre Übersetzung" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Letzte Protokollansicht" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Schnappschussprotokollansicht" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Schnappschüsse" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Alles" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Änderungen" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Fehler" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informationen" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Fehler, [I] Information, [C] Änderung" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "Pfade entschlüsseln" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopieren" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Dekodieren" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Wollen Sie das ausschließen?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Frage" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Letztes Protokoll ansehen" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Starte {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "In Bearbeitung…" #: qt/qttools.py:370 msgid "Today" msgstr "Heute" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Gestern" #: qt/qttools.py:386 msgid "This week" msgstr "Diese Woche" #: qt/qttools.py:393 msgid "Last week" msgstr "Letzte Woche" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Letzte Überprüfung {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Komplettes Protokoll anzeigen" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Profile verwalten" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Bearbeiten" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Hinzufügen" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Entfernen" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Allgemein" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Modus" #: qt/settingsdialog.py:152 #, 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«." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Wo Schnappschüsse gespeichert werden" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Ordner" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH-Einstellungen" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Host" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "User" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Pfad" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Chiffre" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Privater Schlüssel" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Passwort" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Passwort im Schlüsselbund speichern" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Erweitert" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Vollständiger Schnappschusspfad" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Zeitplan" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Deaktiviert" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Bei jedem Hochfahren/Neustart" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Jede Minute" msgstr[1] "Alle {n} Minuten" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Jede Stunde" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Jede Stunde" msgstr[1] "Alle {n} Stunden" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "Benutzerdefinierte Stunden" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Täglich" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Wiederholend (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Sobald das Laufwerk angeschlossen wird (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Wöchentlich" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Monatlich" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Jährlich" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Tag" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Wochentag" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Stunde" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Stunden" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Alle" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Stunde(n)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Tag(e)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Woche(n)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Monat(e)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Einbeziehen" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Dateien und Ordner einbeziehen" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Datei hinzufügen" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Verzeichnis hinzufügen" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "Aus&schließen" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Platzhalter ({example1}) werden im Modus »SSH verschlüsselt« ignoriert.\n" "Nur einzelne oder doppelte Sternchen sind erlaubt ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Ausschlussmuster, Dateien oder Ordner" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Dringend empfohlen" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Voreinstellungen hinzufügen" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Dateien ausschließen, die größer sind als: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Schließt Dateien aus, die größer als der Wert in %(prefix)s sind.\n" "Wenn »Voller rsync-Modus« deaktiviert ist, wird das nur neue Dateien betreffen,\n" "weil das für rsync eine Transferoption ist, keine Ausschlussoption.\n" "Das heißt, dass größere Dateien, die bereits zuvor gesichert wurden,\n" "auch in neuen Schnappschüssen gesichert werden, selbst wenn sie verändert wurden." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "Automatisch ent&fernen" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Älter als" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Jahr(e)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Falls freier Speicher kleiner ist als" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Falls weniger Inodes frei sind als" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Intelligentes Löschen:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Auf entferntem Rechner im Hintergrund ausführen." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EXPERIMENTELL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Alle Schnappschüsse behalten, der letzten" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "Tag(e)." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Einen Schnappschuss pro Tag behalten, der letzten" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Einen Schnappschuss pro Woche behalten, der letzten" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "Woche(n)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Einen Schnappschuss pro Monat behalten, der letzten" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "Monat(e)." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Einen Schnappschuss pro Jahr behalten, für alle Jahre." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Benannte Schnappschüsse nicht entfernen." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Optionen" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Benachrichtigungen aktivieren" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Schnappschüsse im Akkubetrieb deaktivieren" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Energiestatus des Systems nicht verfügbar" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Nur einen Schnappschuss zur selben Zeit ausführen" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Andere Schnappschüsse werden blockiert, bis der aktuelle Schnappschuss fertiggestellt ist.\n" "Das ist eine globale Option, die alle Profile dieses Benutzers betreffen wird.\n" "Die Option muss aber auch für alle anderen Benutzer aktiviert werden." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Beim Wiederherstellen Dateien ersetzen" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Bei Fehlern fortfahren (unvollständige Schnappschüsse behalten)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Prüfsumme benutzen, um Änderungen zu erkennen" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "Neuen Schnappschuss unabhängig von Änderungen erstellen." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "Protokollierungsstufe" #: qt/settingsdialog.py:805 msgid "None" msgstr "Nichts" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "E&xperten-Einstellungen" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Starte »rsync« mit »{cmd}«:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "als cron-job" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "auf entfernten Rechnern" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "wenn ein manueller Schnappschuss erstellt wird" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Bitte 'nocache' installieren, um diese Option freizuschalten)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "auf dem lokalen Rechner" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "stdout in cronjobs nach /dev/null umleiten." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "stderr in cronjobs nach /dev/null umleiten." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "rsync-Bandbreite drosseln" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "ACLs bewahren" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Erweiterte Attribute (xattr) bewahren" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Unsichere Verknüpfungen kopieren (funktioniert nur mit absoluten " "Verknüpfungen)" #: qt/settingsdialog.py:1024 #, 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:1027 msgid "Paste additional options to rsync" msgstr "Weitere Optionen zu rsync hinzufügen" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Präfix der vor jedem Befehl auf dem entfernten Rechner ausgeführt wird.\n" "Variablen müssen mit \\$FOO maskiert werden.\n" "Das betrifft nicht rsync. Um einen Präfix für rsync hinzuzufügen\n" "muss »%(cbRsyncOptions)s« mit\n" "%(rsync_options_value)s genutzt werden.\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "Vorgabe" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Präfix zum SSH-Befehl hinzufügen" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Überprüfen, ob der entfernte Rechner am Netz ist" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Achtung: Wenn dies deaktiviert ist und der entfernte Rechner\n" "nicht verfügbar ist, kann es zu einigen verwirrenden\n" "Fehlern kommen." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" "Prüfen, ob der entfernte Rechner alle erforderlichen Befehle unterstützt" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Achtung: Wenn dies deaktiviert ist und der entfernte Rechner\n" "nicht alle erforderlichen Befehle unterstützt, kann es zu\n" "einigen verwirrenden Fehlern kommen." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Konfiguration wiederherstellen" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "User-callback bearbeiten" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Neues Profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Profil umbenennen" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Sie haben keine private Schlüsseldatei für SSH ausgewählt.\n" "Möchten Sie ein neues öffentlich/privates Schlüsselpaar ohne Passwort erstellen?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Die private Schlüsseldatei »{file}« ist nicht vorhanden." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Möchten Sie Ihren öffentlichen SSH-Schlüssel auf den entfernten\n" "Rechner kopieren, um die passwortlose Anmeldung zu aktivieren?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Die Echtheit des Rechners »{host}« kann nicht festgestellt werden.\n" "\n" "{keytype} Schlüsselfingerabdruck ist:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Ausschlussmuster" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Datei ausschließen" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Ordner ausschließen" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Datei einbeziehen" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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 eine symbolische Verknüpfung. Das verknüpfte Ziel wird nicht mit gesichert, wenn Sie es nicht auch hinzufügen.\n" "Möchten Sie das Ziel der Verknüpfung stattdessen hinzufügen?" #: qt/settingsdialog.py:1893 msgid "Include folder" msgstr "Ordner einbeziehen" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Sind Sie sicher, dass Sie den Schnappschussordner ändern wollen?" #: qt/settingsdialog.py:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Vollständiger Schnappschusspfad: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "Aktiviert" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "deaktiviert" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Einstellungen wiederherstellen" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Bitte ein Schnappschuss auswählen, von dem Sie die Einstellungen für {appName} wiederherstellen möchten. Der Pfad kann so aussehen: \n" "{samplePath}\n" "\n" "Wenn sich der Schnappschuss auf einer externen Festplatte befindet oder verschlüsselt ist, muss er vorher manuell eingehängt werden. Wenn Sie den SSH-Modus verwenden, müssen Sie außerdem die öffentliche Schlüsselanmeldung zum entfernten Rechner einrichten.\n" "Weitere Informationen unter »man backintime«." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Keine Konfiguration gefunden" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "User-callback Skript hat keine Shebang-Zeile (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang im user-callback ist nicht ausführbar." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "%1 und %2 als Pfadparameter verwenden" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Nur unterschiedliche Schnappschüsse" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Nur gleiche Schnappschüsse auflisten zu: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Gründliche Prüfung (genauer, aber langsamer)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Löschen" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Alles auswählen" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Vergleichen" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Gehen zu" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Optionen" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Sie können einen Schnappschuss nicht mit sich selbst vergleichen." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Befehl nicht gefunden" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Soll »{file}« wirklich in dem Schnappschuss »{snapshot_id}« gelöscht werden?" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Soll »{file}« wirklich in {count} Schnappschüssen gelöscht werden?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Das kann nicht rückgängig gemacht werden!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "WARNUNG" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "»{path}« von zukünftigen Schnappschüssen ausschließen?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " und Ihren Benutzer zur Gruppe »fuse« hinzufügen" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "»%s« ist eine symbolische Verknüpfung. Das verknüpfte Ziel wird nicht mit gesichert, wenn Sie es nicht auch hinzufügen.\n" #~ "Möchten Sie das Ziel der Verknüpfung stattdessen hinzufügen?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Dieses Protokoll wurde mit automatischen Suchmustern entschlüsselt.\n" #~ "### Wenn einige Dateinamen nicht entschlüsselt wurden, können sie manuell entschlüsselt werden mit:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s ist kein Mitglied der Gruppe »fuse«.\n" #~ "Bitte »sudo adduser %(user)s fuse« ausführen. Zum Anwenden der Änderungen bitte einmal ab- und wieder anmelden.\n" #~ "Weitere Informationen unter »man backintime«." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s nicht in ssh_known_hosts gefunden." #~ msgid "&Snapshot" #~ msgstr "Schnappschuss" #~ msgid "&View" #~ msgstr "&Ansicht" #~ msgid "..." #~ msgstr "…" #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Änderungen & Fehler" #~ msgid "Config File Help" #~ msgstr "Konfigurationsdateihilfe" #~ msgid "Create a new SSH key without Password." #~ msgstr "Einen neuen SSH-Schlüssel ohne Passwort erstellen." #~ msgid "Diff" #~ msgstr "Unterschiede" #~ msgid "Diff Options" #~ msgstr "Vergleichsoptionen" #~ msgid "Error:" #~ msgstr "Fehler:" #~ msgid "Every 10 minutes" #~ msgstr "Alle 10 Minuten" #~ msgid "Every 12 hours" #~ msgstr "Alle 12 Stunden" #~ msgid "Every 30 minutes" #~ msgstr "Alle 30 Minuten" #~ msgid "Every 4 hours" #~ msgstr "Alle 4 Stunden" #~ msgid "Every 5 minutes" #~ msgstr "Alle 5 Minuten" #~ msgid "Every 6 hours" #~ msgstr "Alle 6 Stunden" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Die vollständige Systemsicherung kann nur auf der selben physikalischen Platte, mit den selben Festplattenpartitionen, wie er von der Quelle, einen Schnappschuss erstellen. Das Wiederherstellen auf einer neuen physischen Festplatte oder der selben Platte mit anderen Partitionen, wird möglicherweise ein defektes und unbrauchbares System erstellen.\n" #~ "\n" #~ "Die vollständige Systemsicherung wird einige Einstellungen überschreiben, die möglicherweise angepasst wurden. Fortfahren?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Prüfsummenkollision in hash_id %s. Der globale Wert für hash_collision wird " #~ "heraufgesetzt und erneut versucht." #~ msgid "Key File" #~ msgstr "Schlüsseldatei" #~ msgid "List only different snapshots" #~ msgstr "Nur unterschiedliche Schnappschüsse auflisten" #~ msgid "Local encrypted" #~ msgstr "Lokal verschlüsselt" #~ msgid "Modify for Full System Backup" #~ msgstr "Für vollständige Systemsicherung verändern" #~ msgid "Mountprocess lock timeout" #~ msgstr "Zeitüberschreitung beim Einhängen" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "Passwortlose Anmeldung von %(user)s@%(host)s fehlgeschlagen. Weitere " #~ "Informationen unter »man backintime«." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "" #~ "Ping %s fehlgeschlagen. Rechner ist ausgeschaltet oder hat eine falsche " #~ "Adresse." #, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: »{name}«" #, python-format #~ msgid "Restore '%s'" #~ msgstr "»%s« wiederherstellen" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "»%s« wiederherstellen zu …" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Stellt die ausgewählte Datei oder den Ordner wieder her.\n" #~ "Wenn dieser Knopf deaktiviert ist, liegt das meist daran, dass »{now}« in der linken Schnappschussliste aktiviert ist." #~ msgid "Run 'ionice':" #~ msgstr "»ionice« starten:" #~ msgid "Run 'nice':" #~ msgstr "»nice« starten:" #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Starte »rsync« mit »nocache«:" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "»rsync« mit »nocache« starten:" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Einstellungen" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Schnappschuss: %s" #~ msgid "View the current disk contents" #~ msgstr "Den aktuellen Festplatteninhalt anzeigen" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Den Schnappschuss vom {timestamp} anzeigen" #~ msgid "WITH ERRORS !" #~ msgstr "MIT FEHLERN!" #~ msgid "Working..." #~ msgstr "Wird bearbeitet …" #~ msgid "You can't include backup folder!" #~ msgstr "Sicherungsordner kann nicht eingeschlossen werden!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Sicherungsunterordner kann nicht eingeschlossen werden!" #~ msgid "You can't remove the last profile!" #~ msgstr "Das letzte Profil kann nicht entfernt werden!" backintime-1.4.3/common/po/el.po000066400000000000000000001334241455673541400165200ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-17 12:56+0000\n" "Last-Translator: hpanago \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Προειδοποίηση" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Κύριο Προφίλ" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Τοπικό" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Ιδιωτικό κλειδί SSH" #: common/config.py:304 msgid "encrypted" msgstr "κρυπτογραφημένο" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Κρυπτογράφηση" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH κρυπτογραφημένο" #: common/config.py:317 msgid "Default" msgstr "Προεπιλογή" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Προφίλ: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Ο φάκελος στιγμιοτύπων δεν είναι έγκυρος!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "" "Πρέπει να επιλέξετε τουλάχιστον ένα φάκελο για να προστεθεί στο αντίγραφο " "ασφαλείας!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Ο φάκελος του αντιγράφου ασφαλείας δεν μπορεί να συμπεριληφθεί." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Ο υποφάκελος του αντιγράφου δεν μπορεί να εμπεριέχεται." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Μην έγκυρη επιλογή. Το {path} δεν είναι φάκελος." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Το Host/User/Profile-ID δεν πρέπει να είναι κενό." #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Αδύνατη η εγγραφή στο: {path}\n" "Είστε σίγουροι ότι έχετε δικαίωμα εγγραφής;" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Αντιγραφή συνδέσμων (αφαίρεση αναφοράς των συμβολικών συνδέσμων)" #: common/config.py:498 msgid "Expert Options" msgstr "Προχωρημένες Ρυθμίσεις" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Δε βρέθηκε το crontab.\n" "Είστε σίγουροι ότι το cron είναι εγκατεστημένο;\n" "Αν όχι, θα πρέπει να απενεργοποιήσετε όλα τα αυτοματοποιημένα backup." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Αποτυχία εγγραφής καινούργιου crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Ο χρονοπρογραμματισμός με udev δεν δουλεύει με τη λειτουργία {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Το προφίλ \"{name}\" υπάρχει ήδη." #: common/configfile.py:736 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 password" msgstr "Παρακαλώ επιβεβαιώστε τον κωδικό" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Ο κωδικός δεν ταιριάζει." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" "Οι εκδόσεις του encfs από την έκδοση 1.7.2 και πριν, περιέχουν ένα σφάλμα " "στην επιλογή --reverse. Παρακαλώ αναβαθμίστε το encfs." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Λήψη στιγμιότυπου" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Αδυναμία αποπροσάρτησης {mountprocess} από το {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "Δεν βρέθηκε το {}. Παρακαλώ εγκαταστήστε, π.χ. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Το σημείο προσάρτησης {} δεν είναι κενό." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Προφίλ '{profile}': Εισαγωγή κωδικού για {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "ΑΠΟΤΥΧΙΑ" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Επαναφορά δικαιωμάτων" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Ολοκληρώθηκε" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Αναβολή του backup όσο το σύστημα βρίσκεται σε μπαταρία" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Δε βρέθηκε ο φάκελος στιγμιοτύπων.\n" "Εάν βρίσκεται σε αφαιρούμενο δίσκο, παρακαλώ συνδέστε τον." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Αναμονή %s δευτερόλεπτο." msgstr[1] "Αναμονή %s δευτερόλεπτα." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Αποτυχία λήψης στιγμιότυπου {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Οριστικοποίηση" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Αδυναμία δημιουργίας φακέλου" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Αποθήκευση αρχείου ρυθμίσεων…" #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Αποθήκευση δικαιωμάτων…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Βρέθηκε παλαιότερο {snapshot_id} το οποίο μπορεί να συνεχιστεί." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" "Αφαίρεση προηγούμενου {snapshot_id} φακέλου από την τελευταία εκτέλεση" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Αδυναμία αφαίρεσης φακέλου" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Λήψη στιγμιότυπου" #: common/snapshots.py:1254 msgid "Success" msgstr "Επιτυχία" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Μερική μεταφορά λόγω εξαφανισμένων αρχείων πηγής (δείτε 'man rsync')" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "Η διεργασία 'rsync' τελείωσε με κωδικό {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Δείτε το 'man rsync' για περισσότερες λεπτομέρειες" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Οι αρνητικοί αριθμοί ως κωδικοί εξόδου του rsync είναι αριθμοί σήματος, " "δείτε 'kill -l' και 'man kill'" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Δεν άλλαξε τίποτα, δεν είναι απαραίτητη η λήψη νέου στιγμιότυπου" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Αδυναμία μετονομασίας {new_path} σε {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Έξυπνη αφαίρεση" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Αφαίρεση παλαιών στιγμιοτύπων" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Προσπάθεια διατήρησης ελάχιστου ελεύθερου χώρου" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" "Προσπαάθεια διατήρησης ελάχιστου ποσοστού {perc} ελεύθερων συστημάτων " "αρχείου (inodes)" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Τώρα" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Αδυναμία προσάρτησης {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" "το ssh-agent δε βρέθηκε. Παρακαλώ βεβαιωθείτε ότι είναι εγκατεστημένο." #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Δεν ήταν δυνατό το ξεκλείδωμα του ιδιωτικού κλειδιού SSH. Λάθος κωδικός ή " "δεν υπάρχει διαθέσιμος κωδικός για cron." #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Το κρυπτογράφημα {cipher} απέτυχε για τον {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Η απομακρυσμένη διαδρομή υπάρχει αλλά δεν είναι φάκελος." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Η απομακρυσμένη διαδρομή δεν είναι εγγράψιμη." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Η απομακρυσμένη διαδρομή δεν είναι εκτελέσιμη." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Αδυναμία δημιουργίας απομακρυσμένης διαδρομής." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" "Ο απομακρυσμένος διακομιστής {host} δεν υποστηρίζει την εντολή {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Βλέπε 'man backintime' για περισσότερες οδηγίες" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Οι εντολές ελέγχου στον διακομιστή {host} επέστρεψαν ένα άγνωστο σφάλμα" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "O απομακρυσμένος διακομιστής {host} δεν υποστηρίζει hardlinks" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" "Αντιγράψτε το δημόσιο κλειδί SSH \"{pubkey}\" στον απομακρυσμένο διακομιστή " "\"{host}\"" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Παρακαλώ επιβεβαιώστε τον κωδικό του {user}" #: qt/app.py:167 msgid "Shortcuts" msgstr "Συντομεύσεις" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Αυτός ο φάκελος δεν υπάρχει\n" "στο επιλεγμένο στιγμιότυπο." #: qt/app.py:252 msgid "Add to Include" msgstr "Προσθήκη στα Περιλαμβανόμενα" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Προσθήκη στα Αποκλειόμενα" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "Η εφαρμογή {appName} δεν είναι ρυθμισμένη. Θα θέλατε να επαναφέρετε κάποιες " "προηγούμενες ρυθμίσεις;" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Δε βρέθηκε ο φάκελος στιγμιοτύπων.\n" "Εάν βρίσκεται σε αφαιρούμενη συσκευή, παρακαλώ συνδέστε την." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "Λήψη στιγμιοτύπου" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Λήψη στιγμιοτύπου" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Λήψη στιγμιοτύπου" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Λήψη στιγμιοτύπου" #: qt/app.py:492 msgid "View last log" msgstr "Προβολή τελευταίας καταγραφής" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Διαχείριση προφιλ…" #: qt/app.py:500 msgid "Shutdown" msgstr "Τερματισμός" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Έξοδος" #: qt/app.py:512 msgid "Help" msgstr "Βοήθεια" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Αποθήκευση αρχείου ρυθμίσεων" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Ιστοσελίδα" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Αρχείο αλλαγών" #: qt/app.py:525 msgid "FAQ" msgstr "Συχνές Ερωτήσεις" #: qt/app.py:528 msgid "Ask a question" msgstr "" #: qt/app.py:531 msgid "Report a bug" msgstr "Αναφορά προβλήματος" #: qt/app.py:534 msgid "Translation" msgstr "Μετάφραση" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Σχετικά" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Επαναφορά" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Επαναφορά στο…" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Προβολή κρυφών αρχείων" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Σύγκριση στιγμιοτύπων…" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "" #: qt/app.py:644 msgid "&Help" msgstr "" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "" #: qt/app.py:962 msgid "Working" msgstr "" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Σφάλμα" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Ταχύτητα" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "" #: qt/app.py:1051 msgid "Root" msgstr "" #: qt/app.py:1052 msgid "Home" msgstr "" #: qt/app.py:1067 msgid "Backup folders" msgstr "" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "" #: qt/app.py:1202 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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:1370 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Λήψη στιγμιοτύπου" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Μεταφράσεις" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Άδεια Χρήσης" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "πλατφόρμα μετάφρασης" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Προφίλ" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Φίλτρο" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Όλα" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Αλλαγές" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Σφάλματα" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "" #: qt/qttools.py:370 msgid "Today" msgstr "" #: qt/qttools.py:377 msgid "Yesterday" msgstr "" #: qt/qttools.py:386 msgid "This week" msgstr "" #: qt/qttools.py:393 msgid "Last week" msgstr "" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Κύριο Προφίλ" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 msgid "&General" msgstr "" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Λειτουργία" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Χρήστης" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Κρυπτογράφημα" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Χρονοπρογραμματισμός" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Απενεργοποιημένο" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Σε κάθε εκκίνηση/επανεκκίνηση" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Κάθε {n} λεπτό" msgstr[1] "Κάθε {n} λεπτά" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Κάθε ώρα" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Κάθε {n} ώρα" msgstr[1] "Κάθε {n} ώρες" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Ώρες που μπορείτε να τις προσαρμόσετε" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Καθημερινά" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Επανειλημμένως" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Όταν συνδέεται δίσκος (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Κάθε εβδομάδα" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Κάθε μήνα" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Κάθε χρόνο" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Ημέρα(ες)" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Κάθε" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Ώρα(ες)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Ημέρα(ες)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Εβδομάδα(ες)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Μήνας(ες)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Έτος(η)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "Ημέρα(ες)." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "Εβδομάδα(ες)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "Μήνας(ες)." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Μην αφαιρέσετε επώνυμα στιγμιότυπα." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "" #: qt/settingsdialog.py:805 msgid "None" msgstr "" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/δευτερόλεπτο" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Επαναφορά παραμετροποίησης" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Νέο προφίλ" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Μετονομασία προφίλ" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Εξαίρεση αρχείου" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Συμπερίληψη αρχείου" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "ενεργό" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "ανενεργό" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Επαναφορά ρυθμίσεων" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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:66 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Διαγραφή" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Δεν μπορείτε να συγκρίνετε ένα στιγμιότυπο με τον εαυτό του." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Η εντολή δεν βρέθηκε" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Θέλετε σίγουρα να διαγράψετε το αρχείο {file} που υπάρχει στο στιγμιότυπο " "{snapshot_id};" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" "Είστε σίγουροι ότι θέλετε να διαγράψετε το {file} από {count} στιγμιότυπα;" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Αυτό δεν μπορεί να αναιρεθεί!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ΠΡΟΣΟΧΗ" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Λήψη στιγμιοτύπου" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Every 10 minutes" #~ msgstr "Κάθε 10 λεπτά" #~ msgid "Every 12 hours" #~ msgstr "Κάθε 12 ώρες" #~ msgid "Every 30 minutes" #~ msgstr "Κάθε 30 λεπτά" #~ msgid "Every 4 hours" #~ msgstr "Κάθε 4 ώρες" #~ msgid "Every 5 minutes" #~ msgstr "Κάθε 5 λεπτά" #~ msgid "Every 6 hours" #~ msgstr "Κάθε 6 ώρες" #~ msgid "Local encrypted" #~ msgstr "Τοπικό κρυπτογραφημένο" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Προφίλ: \"{name}\"" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "WITH ERRORS !" #~ msgstr "ΜΕ ΛΑΘΗ !" #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Δεν μπορείτε να συμπεριλάβετε φάκελο backup !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Δεν μπορείτε να συμπεριλάβετε υποφάκελο backup !" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Αδύνατη η αφαίρεση του τελευταίου προφίλ !" backintime-1.4.3/common/po/eo.po000066400000000000000000001215661455673541400165270ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-27 19:46+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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Averto" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Ĉefa profilo" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Loka" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "privata ŝlosilo de ssh" #: common/config.py:304 msgid "encrypted" msgstr "kripta" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "kriptante" #: common/config.py:310 msgid "SSH encrypted" msgstr "kriptita SSH" #: common/config.py:317 msgid "Default" msgstr "defaŭlto" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profilo: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Dosiero por ekrankopio ne estas valida!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Vi devas elekti almenaŭ unun dosierujon por sekurkopio!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Savkopi-dosierujon ne eblis inkluzivi." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Savkopi-subdosierujon ne eblis inkluzivi." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Nevalida opcio. {path} ne estas dosierujo." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "" "Gastiga komputilo/Uzanto/Identigilo de profilo ne devas esti malplena." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopii lingilojn (forigi simbolajn lingilojn)" #: common/config.py:498 msgid "Expert Options" msgstr "Spertaj Agordoj" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Malsukcesis skribi novan crontab-on." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "udev-planado ne funkcias je reĝimo {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilo \"{name}\" jam ekzistas." #: common/configfile.py:736 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 password" msgstr "Bonvolu konfirmi pasvorton" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Pasvorto ne kongruas." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Krei momentaĵon" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Ne povas demeti {mountprocess} disde {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} ne estas trovita. Bonvolu instali ekz. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Surmetingo {} ne estas malplena." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profilo'{profile}': Enmeti pasvorton por {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "Malpravis" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Farite" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Atendas baterion antaŭ komenci savkopii" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Atendi %s sekundon." msgstr[1] "Atendi %s sekundojn." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "" #: common/snapshots.py:826 msgid "Finalizing" msgstr "Finanta" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Ne povas krei dosieron" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Konservante agordan dosieron…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Ne povas forigi dosieron" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Faras ekrankopion" #: common/snapshots.py:1254 msgid "Success" msgstr "Sukceso" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Nenio estas ŝanĝita, nova momentaĵo ne estas necesa" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Ne povas renomi {new_path} al {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Inteligenta forigo" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Malnovaj momentaĵoj estas forigitaj" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Provas konservi la minimumo da neokupita diskospaco" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Nun" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Ĉifro {cipher} malsukcesas por {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Fora vojo ekzistas, sed ne estas dosierujo." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Fora vojo ne estas skribebla." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Fora vojo ne estas plenumebla." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Ne povis krei foran vojon." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Kontrolkomando en gastiganto {host} revenigis nekonatan eraron" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Fora gastiganto {host} ne subtenas senperan ligilon" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Bonvolu enigi pasvorton por uzanto \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Klavkombinoj" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Aldoni al la inkluzivitoj" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Aldoni al la ekskluzivitoj" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "{appName} ne estas agordita. Ĉu vi volas restaŭri antaŭan agordon?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Ne povas trovi momentaĵdosierujon.\n" "Se ĝi estas je demetebla disko, bonvolu konekti ĝin kaj post tio premi OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Fari momentaĵon" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "" #: qt/app.py:480 msgid "Name snapshot" msgstr "" #: qt/app.py:484 msgid "Remove snapshot" msgstr "" #: qt/app.py:488 msgid "View snapshot log" msgstr "Vidi momentaĵprotokolon" #: qt/app.py:492 msgid "View last log" msgstr "Vidi lastan protokolon" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Ĉefa profilo" #: qt/app.py:500 msgid "Shutdown" msgstr "Sistemfermo" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Eliri" #: qt/app.py:512 msgid "Help" msgstr "Helpo" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Konservante agordan dosieron" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Retejo" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "Oftaj demandoj" #: qt/app.py:528 msgid "Ask a question" msgstr "Demandi" #: qt/app.py:531 msgid "Report a bug" msgstr "Raporti cimon" #: qt/app.py:534 msgid "Translation" msgstr "Traduko" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Pri" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Restaŭri" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Restaŭri al…" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Supren" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Montri kaŝitajn dosierojn" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Kompari momentaĵojn…" #: qt/app.py:627 msgid "&Backup" msgstr "&Savkopio" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "&Restaŭri" #: qt/app.py:644 msgid "&Help" msgstr "&Helpo" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Se vi fermi tiun fenestron, Back in Time ne eblos malŝalti vian sistemon.\n" "Ĉu vi vere volas fermi ĝin?" #: qt/app.py:905 msgid "Working:" msgstr "Faranta:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Farita, savkopio ne estas bezonata" #: qt/app.py:962 msgid "Working" msgstr "Laborante" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Eraro" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Sendita" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Rapideco" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "Tutmonda" #: qt/app.py:1051 msgid "Root" msgstr "Ĉefuzanto" #: qt/app.py:1052 msgid "Home" msgstr "Hejmo" #: qt/app.py:1067 msgid "Backup folders" msgstr "Savkopiodosierujoj" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nomo de momentaĵo" #: qt/app.py:1202 #, fuzzy 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 ĉiujn pli novajn dosierojn en {path}?" msgstr[1] "" "Ĉu vi certas ke vi volas forigi ĉiujn pli novajn dosierojn en {path}?" #: qt/app.py:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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.\n" "Se vi ne plu bezonas ilin, vi povas forigi ilin je {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "" "Ĉu vi vere volas restaŭri ĉi tiu(j)n dosiero(j)n\n" "en la nova dosierujo {path}" msgstr[1] "" "Ĉu vi vere volas restaŭri ĉi tiu(j)n dosiero(j)n\n" "en la nova dosierujo {path}" #: qt/app.py:1370 #, fuzzy 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 tiu(j)n dosiero(j)n" msgstr[1] "Ĉu vi vere volas restaŭri ĉi tiu(j)n dosiero(j)n" #: qt/app.py:1385 #, 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:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 msgid "Snapshot" msgstr "" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Restaŭri {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Restaŭri {path} al…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profilo" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtrilo" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Ĉiuj" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Ŝanĝoj" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Eraroj" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Informoj" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "Malĉifri vojojn" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopii" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Malĉifri" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Ĉu vi volas ekskludi tion?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Demando" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Vidi lastan protokolon" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Komencu {appname}" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Laborante…" #: qt/qttools.py:370 msgid "Today" msgstr "Hodiaŭ" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Hieraŭ" #: qt/qttools.py:386 msgid "This week" msgstr "Nuna semajno" #: qt/qttools.py:393 msgid "Last week" msgstr "Lasta semajno" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Ĉefa profilo" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Redakti" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Forigi" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Ĝenerale" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Maniero" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Kien savi la momentaĵojn" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Dosierujo" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Agordo de SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Gastiganto" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Pordo" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Uzanto" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Dosierindiko" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Ĉifro" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Privata Ŝlosilo" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Pasvorto" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Savi pasvorton je ŝlosilaro" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Malŝaltita" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Je ĉiu startigo/restartigo" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Je ĉiuj {n} minutoj" msgstr[1] "Je ĉiuj {n} minutoj" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Ĉiu horon" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Ĉiu {n} horojn" msgstr[1] "Ĉiu {n} horojn" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Ĉiutage" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Ripete (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Kiam diskingo estas kontektate (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Ĉiusemajne" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Ĉiumonate" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Ĉiujare" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Tago" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Horo" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Horoj" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Ĉiu" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Horo(j)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Tago(j)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Semajno(j)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Monato(j)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Inkluzivi" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Aldoni dosieron" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Aldoni dosierujon" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Ekskluzivi" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Jaro(j)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "Tago(j)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "Semajno(j)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "Monato(j)" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Opcioj" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Aktivigi sciigojn" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "" #: qt/settingsdialog.py:805 msgid "None" msgstr "Neniu" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "&Specialaj Opcioj" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Startas 'rsync' je '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "kiel cron laboro" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "je la fora gastiganto" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "kiam fari manan momentaĵon" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Bonvolu instali 'nocache' por ŝalti tion opcion)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "je loka komputilo" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Alidirekti stdout al /dev/null en cron laboro." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Alidirekti stderr al /dev/null en cron laboro." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Trafiklimigi rsync-on" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Konservi ACL-ojn" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Konservi etenditajn atributojn (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopii danĝerajn ligilojn (sole funkcias je absolutaj ligiloj)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Ĉu vi certas ke vi volas forigi ĉiujn pli novajn dosierojn en \"{name}\"?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Vi ne elektis private ŝlosilan dosieron por SSH.\n" "Ĉu vi volas generi novan paron de publika/privatan ŝlosilojn sen pasvorto?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Private ŝlosila dosiero {file} ne ekzistas." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Ĉu vi volas kopii vian publikan SSH-ŝlosilon al la\n" "fora gastiganto por ebligi ensaluton sen pasvorto?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Ekskludi ŝablonon" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Ekskludi dosieron" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Ekskludi dosierujon" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Inkludi dosieron" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Inkludi dosierujon" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Ĉu vi certas ke vi volas forigi ĉiujn pli novajn dosierojn en {path}?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Malsukcesi krei novan SSH-ŝlosilon en {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Plena momentaĵvojo: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "ŝaltita" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "malŝaltita" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Restaŭri agordon" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Komando" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "parametroj" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Forigi" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Elekti ĉion" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Iri al" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Opcioj" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "" #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "La komando ne estas trovita" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" "Ĉu vi vere volas restaŭri ĉi tiu(j)n dosiero(j)n\n" "en la nova dosierujo {path}" #: qt/snapshotsdialog.py:375 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Ĉu vi vere volas restaŭri ĉi tiu(j)n dosiero(j)n" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Ĉi tio ne povas esti nuligita!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "AVERTO" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #~ msgid "Error:" #~ msgstr "Eraro:" #~ msgid "Every 10 minutes" #~ msgstr "Je ĉiuj 10 minutoj" #~ msgid "Every 5 minutes" #~ msgstr "Je ĉiuj 5 minutoj" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profilo: \"{name}\"" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Agordoj" #~ msgid "Working..." #~ msgstr "Laborante..." #~ msgid "You can't include backup folder!" #~ msgstr "Vi ne povas inkluzivi sekurkopian dosierujon!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Vi ne povas inkluzivi sekurkopian dosieron!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Vi ne povas forigi la lastan profilon!" backintime-1.4.3/common/po/es.po000066400000000000000000001437631455673541400165360ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-30 00:57+0000\n" "Last-Translator: gallegonovato \n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Aviso" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Local" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Clave privada SSH" #: common/config.py:304 msgid "encrypted" msgstr "Encriptado" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Encriptación" #: common/config.py:310 msgid "SSH encrypted" msgstr "Encriptado SSH" #: common/config.py:317 msgid "Default" msgstr "Por defecto" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "¡La carpeta de instantáneas no es válida!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "No se puede incluir la carpeta para la copia de seguridad." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "No se puede incluir la subcarpeta para la copia de seguridad." #: common/config.py:448 #, 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:457 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:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Copiar vínculos (descarta vínculos simbólicos)" #: common/config.py:498 msgid "Expert Options" msgstr "Opciones avanzadas" #: common/config.py:502 #, 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 para {path} es un recurso montado mediante" " sshfs. sshfs no admite enlaces duros. Por favor, utilice el modo 'SSH' en " "su lugar." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "No se pudo encontrar crontab.\n" "¿Está seguro de que cron está instalado?\n" "Si no, debería desactivar todas las copias de seguridad automáticas." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Error al escribir nuevo crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Programar udev no funciona con el modo {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "El perfil \"{name}\" ya existe." #: common/configfile.py:736 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 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:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Tomar instantánea" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "No se puede desmontar {mountprocess} de {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} no encontrado. Por favor, instale por ejemplo {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Punto de montaje {} no disponible." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Perfil '{profile}': Ingrese clave para {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "Falló" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Restaurar permisos" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Finalizado" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Aplazando la copia de seguridad mientras se usa la batería" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Esperando %s segundo." msgstr[1] "Esperando %s segundos." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Error al tomar la instantánea {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Terminando" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "No se puede crear carpeta" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Guardando archivo de configuración…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Salvando permisos…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Encontrada {snapshot_id} sobrante que puede continuarse." #: common/snapshots.py:1169 #, 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:1179 msgid "Can't remove folder" msgstr "No se puede eliminar la carpeta" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Tomar instantánea" #: common/snapshots.py:1254 msgid "Success" msgstr "Exitoso" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' finalizó con código de salida {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Consulte 'man rsync' para más detalles" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Nada ha cambiado, no es necesaria una nueva instantánea" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "No se puede renombrar {new_path} a {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Borrado inteligente" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Eliminando instantáneas antiguas" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Intentar mantener el espacio libre mínimo" #: common/snapshots.py:1737 #, 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:2821 qt/app.py:1616 msgid "Now" msgstr "Ahora" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "No se puede montar {sshfs}" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "El cifrado {cipher} falló para {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "La ruta remota existe pero no es un directorio." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "La ruta remota no admite escritura." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "La ruta remota no es ejecutable." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "No se ha podido crear la ruta remota." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "El host {host} remoto no admite {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Consulte 'man backintime' para obtener instrucciones adicionales" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "El host remoto {host} no admite enlaces duros" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "Copiar clave pública ssh \"{pubkey}\" al servidor remoto \"{host}\"" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Por favor, introduzca la contraseña para \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Accesos directos" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Agregar a incluir" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Añadir a Excluidos" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName}no está configurado. ¿Desea restaurar una configuración anterior?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "No se puede encontrar la carpeta de instantáneas. \n" "Si está en una unidad removible, por favor conéctela y luego presione OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Tomar una instantánea" #: qt/app.py:455 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:458 msgid "Take a snapshot (checksum mode)" msgstr "Tomar una instantánea (en modo suma de verificación)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Utiliza sumas de verificación para detectar cambios en los archivos." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Pausar el proceso de instantánea" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Reanudar el proceso de instantáneas" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Parar el proceso de instantánea" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Recargar la lista de instantáneas" #: qt/app.py:480 msgid "Name snapshot" msgstr "Renombrar instantánea" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Eliminar instantánea" #: qt/app.py:488 msgid "View snapshot log" msgstr "Ver registro de instantánea" #: qt/app.py:492 msgid "View last log" msgstr "Ver último registro" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Perfil principal…" #: qt/app.py:500 msgid "Shutdown" msgstr "Apagar" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Apagar el sistema al terminar instantánea." #: qt/app.py:504 msgid "Setup language…" msgstr "Configurar idioma…" #: qt/app.py:508 msgid "Exit" msgstr "Salir" #: qt/app.py:512 msgid "Help" msgstr "Ayuda" #: qt/app.py:516 msgid "Profiles config file" msgstr "Archivo de configuración de perfiles" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Sitio web" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Registro de cambios" #: qt/app.py:525 msgid "FAQ" msgstr "PREGUNTAS FRECUENTES" #: qt/app.py:528 msgid "Ask a question" msgstr "Haz preguntas" #: qt/app.py:531 msgid "Report a bug" msgstr "Reportar fallos" #: qt/app.py:534 msgid "Translation" msgstr "Traducción" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Acerca de" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Restaurar" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Recuperar en …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Arriba" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Mostrar archivos ocultos" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Comparar instantáneas…" #: qt/app.py:627 msgid "&Backup" msgstr "&Copia de seguridad" #: qt/app.py:638 msgid "&Restore" msgstr "&Restaurar" #: qt/app.py:644 msgid "&Help" msgstr "&Ayuda" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Si cierra esta ventana, Back In Time no podrá apagar el sistema cuando finalice la instantánea.\n" "¿Realmente desea cerrar?" #: qt/app.py:905 msgid "Working:" msgstr "Trabajando:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Hecho, no se necesita una copia de respaldo" #: qt/app.py:962 msgid "Working" msgstr "En curso" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Error" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Enviado" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Velocidad" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Restante" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Administrador" #: qt/app.py:1052 msgid "Home" msgstr "Carpeta Personal" #: qt/app.py:1067 msgid "Backup folders" msgstr "Carpeta de copias de seguridad" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nombre de la instantánea" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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.\n" "Si ya no los necesita, puede eliminarlos con {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Elimina los elementos más nuevos de la carpeta original." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Restaurar archivos o carpetas seleccionados al destino original y\n" "elimine archivos o carpetas que no estén en la instantánea.\n" "Tenga mucho cuidado porque esto\n" "eliminar archivos y carpetas que fueron\n" "excluidos durante la toma de la instantánea." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "ADVERTENCIA: ¡La eliminación de archivos en la raíz del sistema de archivos " "podría romper todo el sistema!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Instantánea" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Restaurar {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurar {path} en …" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Autores" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Traducciones" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licencia" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Configurar el idioma" #: qt/languagedialog.py:87 msgid "System default" msgstr "Predeterminado del sistema" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Utilizar el lenguaje de los sistemas operativos." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Traducido: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "plataforma de traduccion" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "La traducción" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Ver el último registro" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Ver registro de instantáneas" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Perfil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Instantáneas" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtro" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Todo" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Cambios" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Errores" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Información" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Error, [I] Información, [C] Cambiar" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "decodificar rutas" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Copia" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Decodifca" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "¿Quiere excluir esto?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Pregunta" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Ver último registro" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Iniciar {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Trabajando…" #: qt/qttools.py:370 msgid "Today" msgstr "Hoy" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Ayer" #: qt/qttools.py:386 msgid "This week" msgstr "Esta semana" #: qt/qttools.py:393 msgid "Last week" msgstr "Última semana" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Última comprobación {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Mostrar el registro completo" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Administrar perfiles" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Editar" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Añadir" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Quitar" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&General" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Modo" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "¿Dónde guardar las instantáneas?" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Carpeta" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Ajustes de SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Host" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Puerto" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Usuario" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Ruta" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Cipher" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Clave privada" #: qt/settingsdialog.py:236 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:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" "Crear una nueva clave SSH sin contraseña (no se permite si ya se ha " "seleccionado un archivo de clave privada)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Contraseña" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Guardar la contraseña en el llavero" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Avanzado" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Ruta completa de la instantánea" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Tareas programadas" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Desactivado" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "En cada arranque/reinicio" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "Cada hora" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 msgid "Custom hours" msgstr "Horario personalizado" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Todos los días" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Repetidamente (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Cuando la unidad se conecta (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Semanalmente" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Mensualmente" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Anualmente" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Día" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Día laborable" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Hora" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Horas" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Cada" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Hora(s)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Día(s)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Semana(s)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Mes(es)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Incluir" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Incluir archivos y carpetas" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Añadir archivo" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Añadir carpeta" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Excluir" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Los comodines ({example1}) serán ignorados con el modo 'SSH encriptado'.\n" "Sólo se permiten asteriscos simples o dobles ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Excluir patrones, archivos o carpetas" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Muy recomendable" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Agregar valor predeterminado" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Excluir archivos mayores que: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Excluir archivos mayores que el valor en %(prefix)s.\n" "Con 'Modo rsync completo' desactivado esto sólo afectará a los archivos nuevos\n" "porque para rsync esta es una opción de transferencia, no de exclusión.\n" "Así que los archivos grandes que han sido respaldados antes permanecerán en las instantáneas\n" "aunque hayan cambiado." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&Eliminar automáticamente" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Mayor que" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Año(s)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Si el espacio libre es inferior a" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Si los inodos libres son menores que" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Borrado inteligente:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Se ejecuta en segundo plano en el host remoto." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Mantener todas las instantáneas hasta la última" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "día(s)." #: qt/settingsdialog.py:703 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:710 msgid "Keep one snapshot per week for the last" msgstr "Conserve una instantánea por semana para la última semana" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "semana(s)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Conserve una instantánea por mes para el último mes" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "mes(es)." #: qt/settingsdialog.py:724 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:733 msgid "Don't remove named snapshots." msgstr "No elimine las instantáneas con nombre." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Opciones" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Activar notificaciones" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Desactivar instantáneas cuando se esté con batería" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "El estado de la energía no está disponible en el sistema" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Ejecutar sólo una instantánea a la vez" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "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.\n" "Esta es una opción global. Así que afectará a todos los perfiles de este usuario.\n" "Pero es necesario activar esto para todos los demás usuarios, también." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Copia de seguridad de los archivos sustituidos al restaurar" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuar si hay errores (mantener imágenes incompletas)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Utilizar la suma de verificación (checksum) para detectar cambios" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Nivel del registro" #: qt/settingsdialog.py:805 msgid "None" msgstr "Ninguno" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "A&justes avanzados" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Ejecute 'rsync' con '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "como tarea cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "en un host remoto" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "al tomar una instantánea manualmente" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Instale 'nocache' para activar esta opción)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "en un equipo local" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirigir stdout a /dev/null en cronjobs." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirigir stderr a /dev/null en cronjobs." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Limitar ancho de banda de rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "kbit/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Preservar ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Preservar atributos extendidos (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Copiar vínculos incompletos (funciona solo con vínculos absolutos)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Las opciones deben entrecomillarse, por ejemplo {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Pegar opciones adicionales a rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Prefijo para ejecutar antes de cada comando en el host remoto.\n" "Las variables deben escaparse con \\$FOO.\n" "Esto no toca rsync. Así que para añadir un prefijo\n" "para rsync use \"%(cbRsyncOptions)s\" con\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "por defecto" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Añadir prefijo a comandos SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Comprobar si el host remoto está en línea" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Advertencia: si se desactiva y el host remoto\n" "no está disponible, pueden producirse\n" "errores extraños." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Compruebe si el host remoto admite todos los comandos necesarios" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Advertencia: si está desactivado y el host remoto\n" "no soporta todos los comandos necesarios\n" "esto podría conducir a algunos errores extraños." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Restablecer configuración" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Editar user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nuevo perfil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Renombrar perfil" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "No ha elegido un archivo de clave privada para SSH.\n" "¿Desea generar un nuevo par de claves pública/privada sin contraseña?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "El archivo de clave privada \"{file}\" no existe." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "¿Desea copiar su clave pública SSH a la\n" "host remoto para habilitar el inicio de sesión sin contraseña?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "No se puede establecer la autenticidad del host {host}.\n" "\n" "La huella digital de la clave {keytype} es:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" "Por favor, verifique esta huella digital. ¿Quiere añadirla a su archivo " "'known_hosts'?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Excluir patrón" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Excluir archivo" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Excluir carpeta" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Incluir archivo" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Incluir carpeta" #: qt/settingsdialog.py:1930 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:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Ruta completa de la instantánea: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "activado" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "desactivado" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Restablecer ajustes" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Por favor, navegue hasta la instantánea desde la que desea restaurar la configuración de {appName}. La ruta puede parecerse a\n" "{samplePath}\n" "\n" "Si sus instantáneas están en una unidad remota o si están encriptadas, necesitará montarlas manualmente primero. Si utiliza Modo SSH también puede necesitar configurar el inicio de sesión con una clave pública en el host remoto.\n" "Echa un vistazo a 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "No se ha encontrado ninguna configuración" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "El script user-callback no tiene línea shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang en el script user-callback no es ejecutable." #: 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 "Orden" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Parámetros" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Usar %1 y %2 para los parámetros de ruta" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Sólo instantáneas diferentes" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Lista sólo instantáneas iguales a: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Comprobación rigurosa (más precisa, pero lenta)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Borrar" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Seleccionar todo" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Comparar" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Ir a" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Opciones" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "No se puede comparar una misma instantánea." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Orden no encontrada" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "¡Esto no se puede revocar!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ADVERTENCIA" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "¿Excluir {path} de futuras instantáneas?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "Este log ha sido decodificado con busqueda automática de patrón.\n" #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s no se ha encontrado en ssh_known_hosts" #~ msgid "&Snapshot" #~ msgstr "&Instantáneas" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Cambios & Errores" #~ msgid "Diff" #~ msgstr "Diferencias" #~ msgid "Diff Options" #~ msgstr "Opciones al mostrar diferencias (Diff)" #~ msgid "Error:" #~ msgstr "Error:" #~ msgid "Every 10 minutes" #~ msgstr "Cada diez minutos" #~ msgid "Every 12 hours" #~ msgstr "Cada 12 horas" #~ msgid "Every 30 minutes" #~ msgstr "Cada 30 minutos" #~ msgid "Every 4 hours" #~ msgstr "Cada 4 horas" #~ msgid "Every 5 minutes" #~ msgstr "Cada cinco minutos" #~ msgid "Every 6 hours" #~ msgstr "Cada 6 horas" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Una colisión Hash ha ocurrido en hash_id%s. Incremente el valor global " #~ "hash_collision y trate de nuevo." #~ msgid "List only different snapshots" #~ msgstr "Listar sólo instantáneas diferentes" #~ msgid "Local encrypted" #~ msgstr "Encriptado local" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Perfil: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Recuperar '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Recuperar '%s' en" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Preferencias" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Instantánea: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Ver el contenido actual del disco" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Ver la instantánea hecha el {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "¡CON ERRORES!" #~ msgid "Working..." #~ msgstr "Trabajando..." #~ msgid "You can't include backup folder!" #~ msgstr "¡No puede incluir una carpeta de copias de seguridad!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "¡No puede incluir una subcarpeta de copias de seguridad!" #~ msgid "You can't remove the last profile!" #~ msgstr "¡No puede eliminar el último perfil!" backintime-1.4.3/common/po/et.po000066400000000000000000001110221455673541400165160ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Peamine profiil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "" #: common/config.py:304 msgid "encrypted" msgstr "" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "" #: common/config.py:310 msgid "SSH encrypted" msgstr "" #: common/config.py:317 msgid "Default" msgstr "" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profiil: \"{name}\"" #: common/config.py:349 #, fuzzy msgid "Snapshots folder is not valid!" msgstr "Tõmmise kataloog ei ole kehtiv!" #: common/config.py:361 #, fuzzy msgid "You must select at least one folder to back up!" msgstr "Sa pead valima vähemalt ühe kataloogi tagavarakoopia tegemiseks!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} ei ole kataloog." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "" #: common/config.py:467 common/config.py:514 #, fuzzy, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "" #: common/config.py:498 msgid "Expert Options" msgstr "" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "" #: common/config.py:1707 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profiil \"{name}\" on juba olemas." #: common/configfile.py:736 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 password" msgstr "" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Võta tõmmis" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "" #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "EBAÕNNESTUS" #: common/snapshots.py:539 common/snapshots.py:601 #, fuzzy msgid "Restore permissions" msgstr "Taastamise õigused" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Valmis" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:770 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Ei leia tagavarakoopia kataloogi.\n" "Kui see on eemaldatav seade palun ühenda see." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "" msgstr[1] "" #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "" #: common/snapshots.py:826 msgid "Finalizing" msgstr "Lõpetan" #: common/snapshots.py:949 #, fuzzy msgid "Can't create folder" msgstr "Ei saa luua kataloogi" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Salvesta seade fail ..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Salvesta õigused ..." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 #, fuzzy msgid "Can't remove folder" msgstr "Ei saa eemaldada kataloogi" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Võta tõmmis" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Tark eemaldamine" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Eemalda vanad tõmmised" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Nüüd" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Ei saa luua kataloogi." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "" #: qt/app.py:167 msgid "Shortcuts" msgstr "Otseteed" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Ei leia tõmmiste kataloogi.\n" "Kui see on eemaldataval seadmel palun ühenda see ja vajuta OK." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "Võta tõmmis" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Uuenda tõmmiste nimekirja" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Võta tõmmis" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Eemalda Tõmmis" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Vaata Tõmmise Logi" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Vaata Viimast Logi" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Peamine profiil" #: qt/app.py:500 msgid "Shutdown" msgstr "" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Välju" #: qt/app.py:512 msgid "Help" msgstr "Abi" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Salvesta seade fail" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Veebileht" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "" #: qt/app.py:528 msgid "Ask a question" msgstr "" #: qt/app.py:531 msgid "Report a bug" msgstr "" #: qt/app.py:534 msgid "Translation" msgstr "" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Programmist" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Taasta" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Taasata …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "Üles" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Näita peidetud faile" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Võta tõmmis" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "&Taasta" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "&Abi" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "Töötan:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Töötan" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "Üleüldine" #: qt/app.py:1051 msgid "Root" msgstr "" #: qt/app.py:1052 msgid "Home" msgstr "Kodukataloog" #: qt/app.py:1067 msgid "Backup folders" msgstr "Tagavarakoopia kataloogid" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Tõmmise Nimi" #: qt/app.py:1202 #, fuzzy msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Oled kindel, et tahad eemalada tõmmist" msgstr[1] "Oled kindel, et tahad eemalada tõmmist" #: qt/app.py:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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:1370 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Oled kindel, et tahad eemalada tõmmist" msgstr[1] "Oled kindel, et tahad eemalada tõmmist" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Tõmmis" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Taasta {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Taasata ..." #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profiil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Tõmmis" #: qt/logviewdialog.py:94 #, fuzzy msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Kõik" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Muutused" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Veateated" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Teave" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Vaata Viimast Logi" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Töötan…" #: qt/qttools.py:370 msgid "Today" msgstr "Täna" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Eile" #: qt/qttools.py:386 msgid "This week" msgstr "See nädal" #: qt/qttools.py:393 msgid "Last week" msgstr "Eelmine nädal" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Peamine profiil" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Muutmine" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "Ü&ldine" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Kuhu salvestada tõmmis" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "Server" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 #, fuzzy msgid "User" msgstr "Kasutaja" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Põhjalikum" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Välja lülitatud" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Igal käivitamisel/taaskäivitamisel" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Iga {n} minuti tagant" msgstr[1] "Iga {n} minuti tagant" #: qt/settingsdialog.py:363 #, fuzzy msgid "Every hour" msgstr "Iga tund" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Iga {n} tunni tagant" msgstr[1] "Iga {n} tunni tagant" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Iga päev" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Iga nädal" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Iga kuu" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Iga aasta" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Päev" #: qt/settingsdialog.py:394 #, fuzzy msgid "Weekday" msgstr "Nädalapäev" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "Tund" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "Tund" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Iga" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Päev(a)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Nädal(at)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Kaasa" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Kaasa failid ja kataloogid" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Lisa fail" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Lisa kaust" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Jäta välja" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Jäta välja mustrid, failid või kataloogid" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "Rangelt soovitatud" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "&Automaatselt eemalda" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Vanem kui" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Aasta(t)" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Kui vabaruumi on vähem kui" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Kui vabaruumi on vähem kui" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Tark eemaldamine" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "päev(ad)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "nädal(ad)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "kuu(d)" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Eemalda Tõmmis" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "&Valikud" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Luba teated" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 #, fuzzy msgid "Log Level" msgstr "Logitase" #: qt/settingsdialog.py:805 msgid "None" msgstr "Pole" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "Ek&sperdi valikud" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Uus profiil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Muuda profiili nime" #: qt/settingsdialog.py:1142 #, fuzzy, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Jäta välja fail" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Jäta välja kataloog" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Kaasa fail" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Kaasa katloog" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Oled kindel, et tahad muuta tõmmiste kataloogi?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command" msgstr "Käsk" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters" msgstr "Parameetrid" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Mine" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Valikud" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Sa ei saa võrrelda tõmmist iseendaga." #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "Käsku ei leitud" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Tõmmis" #~ msgid "..." #~ msgstr "..." #~ msgid "Changes & Errors" #~ msgstr "Muudatused & Veateated" #~ msgid "Diff" #~ msgstr "Erinevus" #~ msgid "Error:" #~ msgstr "Veateade:" #~ msgid "Every 10 minutes" #~ msgstr "Iga 10 minuti tagant" #~ msgid "Every 5 minutes" #~ msgstr "Iga 5 minuti tagant" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profiil: \"{name}\"" #~ msgid "Settings" #~ msgstr "Seaded" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Tõmmis: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Vaata praeguse ketta sisu" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Vaata tõmmist, mis on tehtud {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "VIGATEGA!" #~ msgid "Working..." #~ msgstr "Töötan..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Sa ei saa kaasata tagavarakoopia kataloogi ennast!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Sa ei saa kaasata tagavarakoopia kataloogis olevaid katalooge!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Sa ei saa eemaldada viimast profiili!" backintime-1.4.3/common/po/eu.po000066400000000000000000001505111455673541400165250ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-21 15:15+0000\n" "Last-Translator: alexgabi \n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Abisua" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Profil nagusia" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokala" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH gako pribatua" #: common/config.py:304 msgid "encrypted" msgstr "zifratua" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Zifraketa" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH zifratua" #: common/config.py:317 msgid "Default" msgstr "Lehenetsia" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profila: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Babeskopien karpeta ez da baliozkoa!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Gutxienez karpeta bat hautatu behar duzu babeskopiarako!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Ezin da babeskopia karpeta sartu." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Babeskopia azpikarpeta ezin da sartu." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Aukera baliogabea. {path} ez da karpeta bat." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Ostalari/Erabiltzaile/Profila-ID ez da hutsik egon behar." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopiatu estekak (erreferentzia kendu esteka sinbolikoei)" #: common/config.py:498 msgid "Expert Options" msgstr "Aukera aurreratuak" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Ezin da aurkitu crontab.\n" "Seguru zaude crontab instalatuta dagoela?\n" "Crontab ez baduzu babeskopia automatiko guztiak ezgaitu beharko zenuke." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Huts egin du crontab berria idazten." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Programatutako udev-a ez dabil {mode} moduarekin" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\" profila badago honezkero." #: common/configfile.py:736 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 password" msgstr "Egiaztatu pasahitza mesedez" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Pasahitzak ez datoz bat." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Egin babeskopia" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Ezin da {mountprocess} desmuntatu {mountpoint}-tik." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} ez da aurkitu. Mesedez instalatu adibidez {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "{} muntatze-puntua ez dago hutsik." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "'{profile}' profila: Sartu pasahitza honentzat {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "HUTS EGIN DU" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Leheneratu baimenak" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Eginda" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Atzeratu babeskopia bateriaz dabilen bitartean" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Itxaroten segundo %s ." msgstr[1] "Itxaroten %s segundo." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Huts egin du {snapshot_id} babeskopia egiten." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Amaitzen" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Ezin da karpeta sortu" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Gorde ezarpenen fitxategia…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Baimenak gordetzen…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "'{snapshot_id}' jarraitu daitekeen soberakina aurkitu du." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Azken exekuziotik '{snapshot_id}' karpeta soberakina kentzen" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Ezin da karpeta ezabatu" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Babeskopia egiten" #: common/snapshots.py:1254 msgid "Success" msgstr "Ongi" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Transferentzia partziala desagertutako iturburu-fitxategiengatik (ikus 'man " "rsync')" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "\"rsync\" amaitu da irteera-kode honekin {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Ikus 'man rsync' xehetasun gehiagorako" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Ez dago aldaketarik. Ez da babeskopia berririk egin behar" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Ezin jarri {new_path} izena honi {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Ezabatze automatikoa" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Babeskopia zaharrak ezabatzen" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Saiatu espazio minimoa mantentzen" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Saiatu gutxienez {perc} nodo libre mantentzen" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Orain" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Ezin da {sshfs} muntatu" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent ez da aurkitu. Ziurtatu instalatuta dagoela." #: common/sshtools.py:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "{cipher} zifraketak huts egin du {host}rentzat." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Urrutiko bide-izena badago baina ez da direktorio bat." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Urrutiko bide-izenak ez dauka idazteko baimenik." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Urrutiko bide-izena ez da exekutagarria." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Ezin da sortu urrutiko bide-izena." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Urruneko {host} ostalariak ez du {command} onartzen" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Begiratu 'man backintime' azalpenak ikusteko" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "{host} ostalariaren egiaztatzeko komandoek errore ezezaguna itzuli dute" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "{host} urrutiko ostalariak ez ditu esteka gogorrak onartzen" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Egiaztatu \"{user}\"-en pasahitza, mesedez" #: qt/app.py:167 msgid "Shortcuts" msgstr "Lasterbideak" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Gehitu sartu beharrekoetan" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Gehitu kanpoan utzi beharrekoetan" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} ez dago konfiguratua. Nahi duzu leheneratu aurreko konfiguraziora?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Ezin da aurkitu babeskopien karpeta.\n" "Unitate eramangarri batean badago konekta ezazu eta sakatu Ados." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Egin babeskopia" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" "Erabili denbora eta tamaina aldaketak fitxategi-aldaketak hautemateko." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Egin babeskopia (kontrol baturarekin)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Erabili kontrol-batura aldaketak detektatzeko." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Pausatu babeskopia prozesua" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Laburtu babeskopia prozesua" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Gelditu babeskopia prozesua" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Freskatu babeskopien zerrenda" #: qt/app.py:480 msgid "Name snapshot" msgstr "Izendatu babeskopia" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Kendu babeskopia" #: qt/app.py:488 msgid "View snapshot log" msgstr "Ikusi babeskopiaren erregistroa" #: qt/app.py:492 msgid "View last log" msgstr "Ikusi azken erregistroa" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Kudeatu profilak…" #: qt/app.py:500 msgid "Shutdown" msgstr "Itzali" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Itzali sistema babeskopia bukatu ondoren." #: qt/app.py:504 msgid "Setup language…" msgstr "Konfiguratu hizkuntza…" #: qt/app.py:508 msgid "Exit" msgstr "Irten" #: qt/app.py:512 msgid "Help" msgstr "Laguntza" #: qt/app.py:516 msgid "Profiles config file" msgstr "Profilen konfigurazio fitxategia" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Webgunea" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Aldaketen egunkaria" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ" #: qt/app.py:528 msgid "Ask a question" msgstr "Zerbait galdetu" #: qt/app.py:531 msgid "Report a bug" msgstr "Akats baten berri eman" #: qt/app.py:534 msgid "Translation" msgstr "Itzulpena" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Honi buruz" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Berreskuratu" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" "Leheneratu hautatutako fitxategiak eta karpetak jatorrizko kokalekura." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Leheneratu hona …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "Leheneratu hautatutako fitxategiak eta karpetak kokaleku berrira." #: qt/app.py:552 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:557 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:560 msgid "Up" msgstr "Gora" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Erakutsi ezkututko fitxategiak" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Konparatu babeskopiak…" #: qt/app.py:627 msgid "&Backup" msgstr "&Babeskopia" #: qt/app.py:638 msgid "&Restore" msgstr "&Leheneratu" #: qt/app.py:644 msgid "&Help" msgstr "L&aguntza" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Leiho hau ixten baduzu Back In Time-k ezin izango du zure sistema itzali babeskopia bukatzean.\n" "Seguru zaude itxi nahi duzula?" #: qt/app.py:905 msgid "Working:" msgstr "Lanean:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Egina, ez da babeskopia egin behar" #: qt/app.py:962 msgid "Working" msgstr "Lanean" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Errorea" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Bidalita" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Abiadura" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "ETA" #: qt/app.py:1050 msgid "Global" msgstr "Orokorra" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Hasiera" #: qt/app.py:1067 msgid "Backup folders" msgstr "Babeskopia-karpetak" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Babeskopiaren izena" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "Berriagoak diren fitxategiak berrizendatuko dira '{suffix}' kokapenarekin leheneratu baino lehen.\n" "Ez badituzu gehiago behar ezabatu ditzakezu '{cmd}'-rekin" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Ezabatu jatorrizko karpetako fitxategirik berrienak." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Leheneratu hautatutako fitxategiak edo karpetak jatorrizko kokalekura eta\n" "ezabatu babeskopian ez dauden fitxategi edo karpetak.\n" "Argi ibili, honek babeskopia\n" "egitean baztertutako fitxategi\n" "eta karpetak ezabatuko ditu." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "KONTUZ: fitxategi-sistemaren erroko fitxategiak ezabatzeak sistema osoa " "hondatu dezake!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Babeskopia" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Berreskuratu {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Leheneratu {path} hona…" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Egileak" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Itzulpenak" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Lizentzia" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Konfiguratu hizkuntza" #: qt/languagedialog.py:87 msgid "System default" msgstr "Sistemak lehenetsia" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Erabili sistema eragileen hizkuntza." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Itzulia: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "Itzulpenetarako plataforma" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Zure Itzulpena" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Azken erregistroaren ikuspegia" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Babeskopiaren erregistro ikuspegia" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profila" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Babeskopiak" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Iragazi" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Guztiak" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Aldaketak" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Akatsak" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informazioa" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Errorea, [I] Informazioa, [C] Aldaketa" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "dekodetu bide-izenak" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopiatu" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Dekodetu" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Nahi duzu hau kanpoan utzi?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Galdera" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Ikusi azken erregistroa" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Hasi {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Lanean…" #: qt/qttools.py:370 msgid "Today" msgstr "Gaur" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Atzo" #: qt/qttools.py:386 msgid "This week" msgstr "Aste honetan" #: qt/qttools.py:393 msgid "Last week" msgstr "Joan den astean" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Azken kontrola {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Erakutsi erregistro osoa" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Kudeatu profilak" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Editatu" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Gehitu" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Kendu" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Orokorra" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Modua" #: qt/settingsdialog.py:152 #, 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." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Non gorde babeskopiak" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Karpeta" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH ezarpenak" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Ostalaria" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Ataka" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Erabiltzailea" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Bide-izena" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Zifratu" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Gako pribatua" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Pasahitza" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Gorde pasahitza" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Aurreratua" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Babeskopiaren bide osoa" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Programaketa" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Ezgaituta" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Abiatze/berrabiaratze guztietan" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "Orduro" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "{n} orduero" msgstr[1] "{n} orduero" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "Ordu pertsonalizatuak" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Egunero" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Behin eta berriz (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Unitatea konektatzean (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Astero" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Hilero" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Urtero" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Eguna" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Asteguna" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Ordua" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Orduak" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Bakoitza" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Ordu" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Egun(ak)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "aste" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Hilabete" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Sartu" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "sartu fitxategiak eta karpetak" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Gehitu fitxategia" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Gehitu karpeta" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Kanpoan utzi" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Komodin karaktereak ({example1}) ez dira ezagutuko 'SSH encrypted' moduan.\n" "Bakarrik erabil daitezke banatutako asteriskoak ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Baztertu txantiloi, fitxategi edo karpeta hauek" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Oso gomendagarria" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Gehitu lehenetsia" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Utzi kanpoan hau baino handiagoak diren fitxategiak: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Utzi kanpoan hau baino handiagoak diren fitxategiak %(prefix)s\n" "'rsync osoko modua' desgaituta badago horrek fitxategi berriei soilik eragingo die\n" "zeren eta rsync-entzat hori transferentzia aukera bat da, ez baztertze aukera bat.\n" "Beraz babeskopia lehendik egina zituzten fitxategi handiak mantenduko dira irudian\n" "nahiz eta aldaketak jasan." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&Ezabaketa automatikoa" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Hau baino zaharragoa" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "urtez" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Toki librea hau baino txikiagoa bada" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Baldin eta nodo libreak hauek baino gutxiago badira" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Ezabatze adimendua:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Exekutatu urrutiko ostalariaren atzeko planoan." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "ESPERIMENTALA" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Mantendu babeskopia guztiak" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "eguna(k)." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Mantendu eguneko babeskopia bana azkenerako" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Mantendu asteko babeskopia bana azkenerako" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "astea(k)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Mantendu hilabeteko babeskopia bana azkenerako" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "hilabeta(k)." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Mantendu babeskopia bat urteko urte guztietan." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Ez ezabatu izena duten babeskopiak." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Aukerak" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Gaitu notifikazioak" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Ezgaitu babeskopiak egitea bateriaz funtzionatzean" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Energia egoera ez dago eskurtagarri sistemarentzat" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Egin babeskopia bat aldiko" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Beste babeskopiak blokeatuko dira oraingo babeskopia egiten den bitartean.\n" "Hau aukera globala da, beraz, erabiltzaile honen profil guztiei eragiten die.\n" "Baina beste erabiltzaileentzat aktibatu behar da ere." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Egin berreskurapenean ordeztutako fitxategien babeskopia" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Jarraitu erroreak egon arren (mantendu babeskopia osatu gabea)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Erabili kontrol-batura aldaketak detektatzeko" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Erregistro maila" #: qt/settingsdialog.py:805 msgid "None" msgstr "Ezer Ez" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "A&ukera aurreratuak" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Exekutatu 'rsync' '{cmd}'-rekin:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "programatutako ataza bezala" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "urrutiko ostalarian" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "eskuzko babeskopia egitean" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Mesedez, instalatu 'nocache' aukera hau gaitzeko)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "ordenagailuan bertan" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Birzuzendu stdout /dev/null-era cronjob-etan." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Birzuzendu stderr /dev/null-era cronjob-etan." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Mugatu rsync-en banda-zabaleraren erabilera" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/seg" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Mantendu ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Mantendu atributu hedatuak (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopiatu esteka inseguruak (egin bakarrik esteka absolutuekin)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Aukerak kakotxen artean idatzi, esate baterako {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Itsatsi aukera gehigarriak rsync-eri" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Ezarri exekutatzeko urrutiko ostalariko edozein komandoaren aurretik.\n" "Aldagaiak \\$FOO bidez alde egin behar dute.\n" "Honek ez dio eragiten rsync-i. Beraz gehitu aurrizki bat\n" "rsync erabili dezan \"%(cbRsyncOptions)s\"\n" "%(rsync_options_value)s-rekin\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "lehenetsia" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Gehitu aurrizkia SSH komandoei" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Markatu urrutiko ostalaria linean badago" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Kontuz: desgaitzen baduzu eta urrutiko ostalaria\n" "ezin bada erabili, ustekabeko erroreak sor\n" "daitezke." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" "Markatu urrutiko ostalariak behar diren komando guztiak onartzen baditu" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Kontuz: desgaitzen baduzu eta urrutiko ostalariak\n" "behar diren komando guztiak onartzen ez baditu,\n" "ustekabeko erroreak sor daitezke ." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Leheneratu konfigurazioa" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Editutatu erabiltzailearen atzeradeia" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Profil berria" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Berrizendatu profila" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Ez duzu aukeratu SSHrako gako fitxategi pribatua.\n" "Nahi al duzu pasahitzik gabeko gako publiko/pribatu parea sortzea?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "\"{file}\" gako pribatuko fitxategirik ez dago." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Nahi al duzu kopiatzea zure SSH gako publikoa\n" "urrutiko ostalarira pasahitzik gabe sartu ahal izateko?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Ezin da egiaztatu \"{host}\" ostalariaren benetakotasuna.\n" "\n" "{keytype} hatz-marka gakoa da:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Baztertu txantiloia" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Baztertu fitxategia" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Baztertu karpeta" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Sartu fitxategia" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Sartu karpeta" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Ziur zaude aldatu nahi duzula babeskopien karpeta?" #: qt/settingsdialog.py:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Babeskopiaren bide osoa: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "gaituta" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "ezgaituta" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Ezarpenak berreskuratu" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Nabigatu berreskuratu nahi duzun {appName} -ren ezarpenak dauden babeskopiara. Bide-izenak honelako itxura izan behar luke:\n" "{samplePath}\n" "\n" "Zure babeskopiak urrutiko unitate batean badaude edo zifratuta badaude, aldez aurretik eskuz muntatu behar dituzu. SSH modua erabiltzen baduzu gako publikoa ezarri beharko duzu urrutiko ostalarian sartzeko.\n" "Eman begirada bat 'man backintime' oharrei." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Ez da ezarpenik topatu" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" "Erabiltzailearen atzeradeia-scriptak ez dauka shebang (#!/bin/sh) lerrorik." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" "Erabiltzalearen atzera-deiaren scriptaren shebang ez da exekutagarria." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Irudiak alderatzeko aukerak" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Komandoa" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Parametroak" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Erabili %1 eta %2 bide-izenen parametroetarako" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Babeskopia desberdinak soilik" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Zerrendatu bakarri berdinak diren babeskopiak hona: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Egiaztatze sakona (zehaztasun handiagoa, baina motela)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Ezabatu" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Hautatu denak" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Konparatu" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Joan hona" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Aukera" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Ezin duzu alderatu babeskopia bat bere buruarekin." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Ez da komandoa aurkitu" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Honek ez dauka atzera bueltarik!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ABISUA" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Baztertu {path} hurrengo babeskopietan?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " eta gehitu zure erabiltzailea 'fuse' taldera" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "\"%s\" esteka sinboliko bat da. Estekatutako objektuaren babeskopia ez da egingo are eta bera ere gehitu arte.\n" #~ "Nahi duzu gehitu esteka sinbolikoen helburu-objektuak?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Erregistro hau automatikoki bilatutako txantiloi baten bidez dekodetu da\n" #~ "### Baldin eta bide-izenen bat ez badago dekodetua eskuz dekodetu dezakezu honekin\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s ez da 'fuse' taldeko partaidea.\n" #~ " Exekutatu 'sudo adduser %(user)s fuse'. Aldaketak aplikatzeko atera zaitez eta sartu berriro.\n" #~ "Begira ezazu 'man backintime' azalpen zabalagoa izateko." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "Ez da aurkitu %s ssh-known-host ostalarian." #~ msgid "&Snapshot" #~ msgstr "&Babeskopia" #~ msgid "&View" #~ msgstr "&Ikusi" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Aldaketa eta erroreak" #~ msgid "Config File Help" #~ msgstr "Config fitxategiaren laguntza" #~ msgid "Create a new SSH key without Password." #~ msgstr "Sortu pasahitzik gabeko SSH gako berri bat." #~ msgid "Diff" #~ msgstr "Dif" #~ msgid "Diff Options" #~ msgstr "Diff aukerak" #~ msgid "Error:" #~ msgstr "Errorea:" #~ msgid "Every 10 minutes" #~ msgstr "10 minuturo" #~ msgid "Every 12 hours" #~ msgstr "12 ordutan behin" #~ msgid "Every 30 minutes" #~ msgstr "30 minuturo" #~ msgid "Every 4 hours" #~ msgstr "4 ordutan behin" #~ msgid "Every 5 minutes" #~ msgstr "5 minuturo" #~ msgid "Every 6 hours" #~ msgstr "6 ordutan behin" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Sistemaren babeskopia osoa bakarrik leheneratu daiteke disko fisiko bere(et)an eta jatorrizko partiketarekin; jatorrizkoa ez diren diskoetan edo partiketetan leheneratzeak sor dezake hautsitako edo egonkorra ez den sistema bat.\n" #~ "\n" #~ "Sistemaren babeskopia osoak pertsonalizatutako hainbat ezarpen gainidatz dezake. Jarraitu nahi duzu?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Hash talka gertatu da hemen: hash_id %s. Handitu hash_collision balioa eta " #~ "saiatu berriz." #~ msgid "Key File" #~ msgstr "Gako fitxategia" #~ msgid "List only different snapshots" #~ msgstr "Zerrendatu bakarri desberdinak diren babeskopiak" #~ msgid "Local encrypted" #~ msgstr "Lokala zifratua" #~ msgid "Modify for Full System Backup" #~ msgstr "Aldatu Full System Backup-entzat" #~ msgid "Mountprocess lock timeout" #~ msgstr "Muntatze prozedura blokeatuta iraungitzean" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "Huts egin du %(user)s@%(host)s-en pasahitzik gabeko autentifikazioa. " #~ "Begiratu 'man backintime' azalpenak ikusteko." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "Huts egin du %s ping. Ostalaria erorita dago edo helbidea okerra da." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profila: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Berreskuratu '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Berreskuratu '%s' hona..." #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Leheneratu hautatutako fitxategi edo karpetak.\n" #~ "Botoi hau grisa badago gehienetan da \"{now}\" hautatua dagoelako ezkerreko babeskopien zerrendan." #~ msgid "Run 'ionice':" #~ msgstr "Exekutatu 'ionice'" #~ msgid "Run 'nice':" #~ msgstr "Exekutatu 'nice':" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Exekutatu 'rsync' 'nocache'-rekin" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Exekutatu 'rsync' 'nocache'-rekin" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Ezarpenak" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Babeskopia: %s" #~ msgid "View the current disk contents" #~ msgstr "Ikusi diskoaren uneko edukia" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Ikusi {timestamp}-an egindako babeskopia" #~ msgid "WITH ERRORS !" #~ msgstr "ERROREAK DAUDE!" #~ msgid "Working..." #~ msgstr "Lanean..." #~ msgid "You can't include backup folder!" #~ msgstr "Ezin duzu babeskopiaren karpeta sartu babeskopian!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Ezin duzu babeskopiaren azpi-karpetak sartu babeskopian!" #~ msgid "You can't remove the last profile!" #~ msgstr "Ezin duzu ezabatu azken profila!" backintime-1.4.3/common/po/fa.po000066400000000000000000001613741455673541400165130ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-27 20:01+0000\n" "Last-Translator: alialmasi \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.3.1\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "هشدار" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "پروفایل اصلی" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "محلی" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "کلید خصوصی اس‌اس‌اچ" #: common/config.py:304 msgid "encrypted" msgstr "رمزگذاری شده" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "رمزنگاری" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH رمزنگاری شده" #: common/config.py:317 msgid "Default" msgstr "پیش‌فرض" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "پروفایل: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "پوشه اسنپ‌شات معتبر نیست!" #: common/config.py:361 #, fuzzy msgid "You must select at least one folder to back up!" msgstr "شما حداقل برای پشتیبان‌گیری باید یک پوشه را انتخاب کنید!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "پوشه پشتیبان نمی‌تواند گنجانده شود" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "زیر پوشه پشتیبان نمی‌تواند گنجانده شود" #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "گزینه نادرست. {path} یک پوشه نیست." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "آی‌دی هاست/کاربر/پروفایل نباید خالی باشد!" #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "نمی‌توان نوشت در: {path}\n" "آیا اطمینان دارید که دسترسی به نوشتن دارید؟" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "کپی کردن لینک ها(لینک‌های نمادین متفاوت)" #: common/config.py:498 msgid "Expert Options" msgstr "گزینه های حرفه‌ای" #: common/config.py:502 #, 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} سهم(share) مانت شده sshfs هست. sshfs از " "هاردلینک ها پشتیبانی نمیکند. لطفاً از حالت 'SSH' به جای آن استفاده کنید." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "نمی‌توان کرونتب را پیدا کرد.\n" "آیا اطمینان دارید که کرون نصب شده است؟\n" "اگر نه، شما بایستی همه پشتیبان های خودکار را غیرفعال کنید." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "نوشتن کرونتب جدید شکست خورد." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "برنامه‌ریزی udev با حالت {mode} کار نمی‌کند" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "پروفایل \"{name}\" از قبل وجود دارد." #: common/configfile.py:736 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 password" msgstr "لطفاً رمز عبور را تایید کنید" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "کلمه‌عبور مطابقت ندارد." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "گرفتن اسنپ‌شات" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "نمی‌توان {mountprocess} را از {mountpoint} جدا کرد." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} پیدا نشد. لطفا به‌عنوان مثال {} را نصب کنید." #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "مونت‌پوینت {} خالی نیست." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "پروفایل '{profile}': رمز عبور را وارد کنید برای {mode}:. " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "شکست خورد" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "بازگردانی دسترسی ها" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "تمام" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "به تعویق انداختن پشتیبان گیری در هنگام باتری" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "نمیتوان پوشه اسنپ‌شات را پیدا کرد.\n" "اگر در یک درایو جداشدنی قرار دارد، لطفاً آن را وصل کنید." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "صبر برای %s ثانیه." msgstr[1] "صبر برای %s ثانیه." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "گرفتن اسنپ‌شات {snapshot_id} شکست خورد." #: common/snapshots.py:826 msgid "Finalizing" msgstr "درحال نهایی کردن" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "نمی‌توان پوشه ساخت" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "درحال ذخیره فایل کانفیگ…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "در حال ذخیره دسترسی‌ها…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "{snapshot_id} رها شده‌ای پیدا شد که میتوان ادامه داد." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "حذف کردن پوشه {snapshot_id} رها شده‌ای از آخرین اجرا" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "نمیتوان پوشه را حذف کرد" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "در حال گرفتن اسنپ‌شات" #: common/snapshots.py:1254 msgid "Success" msgstr "موفق" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "انتقال جزئی به دلیل فایل‌های منبع ناپدید شده (به \"man rsync\" مراجعه کنید)" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' با کد خروج {exit_code} به پایان رسید" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "برای جزئیات بیشتر به \"man rsync\" مراجعه کنید" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "کدهای خروجی منفی rsync شماره سیگنال هستند، به \"kill -l\" و \"man kill\" " "مراجعه کنید" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "چیزی تغییر نیافت، اسنپ‌شات ضروری جدیدی نیست" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "نمی‌توان {new_path} را به {path} تغییر نام داد" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "حذف هوشمند" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "درحال پاک کردن اسنپ‌شات های قدیمی" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "درحال تلاش برای نگهداری حداقل فضای آزاد" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "درحال تلاش برای نگهداری حداقل {perc} آزاد آی‌نودها" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "الان" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "نمی‌توان {sshfs} را مانت کرد" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent پیدا نشد. لطفا از نصب بودن آن مطمئن شوید." #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "کلید خصوصی اس‌اس‌اچ باز نشد. رمز عبور یا رمز عبور اشتباه برای cron موجود " "نیست." #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "متن رمز {cipher} برای {host} شکست خورد." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "مسیر ریموت وجود دارد اما یک دایرکتوری نیست." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "مسیر ریموت قابل نوشتن نیست." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "مسیر ریموت قایل اجرا نیست." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "نمی‌توان پوشه ریموت ساخت." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "مسیر ریموت {host} دستور {command} را پشتیبانی نمیکند" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "برای دستورالعمل های بیشتر به \"man backintime\" مراجعه کنید" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "بررسی دستورات روی میزبان {host} خطای ناشناخته ای را نشان داد" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "میزبان ریموت {host} از هاردلینک‌ها پشتیبانی نمی کند" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" "کلید عمومی اس‌اس‌اچ(ssh-key) \"{pubkey}\" را به هاست ریموت \"{host}\" کپی " "کنید" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "لطفاً رمز عبور را برای \"{user}\" وارد کنید" #: qt/app.py:167 msgid "Shortcuts" msgstr "میانبرها" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "این پوشه در اسنپ‌شات انتخاب شده فعلی وجود ندارد." #: qt/app.py:252 msgid "Add to Include" msgstr "اضافه کردن به شامل‌ها" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "اضافه کردن به استثناها" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} پیکربندی نشده. مایل به تمایلات هستید که پیکربندی قبلی را بازگردانی" " کنی؟" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "پوشه اسنپ‌شات‌ها پیدا نشد.\n" "اگر در یک درایو جداشدنی قرار دارد، لطفاً آن را متصل و سپس باشه را بزنید." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "گرفتن اسنپ‌شات" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "از زمان و اندازه اصلاح برای تشخیص تغییر فایل استفاده کنید." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "گرفتن اسنپ‌شات (حالت چک‌سام)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "از چک‌سام برای شناسایی تغییرات استفاده کن." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "توقف پروسه اسنپ‌شات" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "ادامه پروسه اسنپ‌شات" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "لغو پروسه اسنپ‌شات" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "تازه سازی لیست اسنپ‌شات" #: qt/app.py:480 msgid "Name snapshot" msgstr "نام‌بردن اسنپ‌شات" #: qt/app.py:484 msgid "Remove snapshot" msgstr "حذف اسنپ‌شات" #: qt/app.py:488 msgid "View snapshot log" msgstr "مشاهده لاگ اسنپ‌شات" #: qt/app.py:492 msgid "View last log" msgstr "مشاهده آخرین لاگ" #: qt/app.py:496 msgid "Manage profiles…" msgstr "مدیریت پروفایل‌ها…" #: qt/app.py:500 msgid "Shutdown" msgstr "خاموش" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "بعد از تمام شدن اسنپ‌شات سیستم را خاموش کن." #: qt/app.py:504 msgid "Setup language…" msgstr "زبان راه‌اندازی…" #: qt/app.py:508 msgid "Exit" msgstr "خروج" #: qt/app.py:512 msgid "Help" msgstr "کمک" #: qt/app.py:516 msgid "Profiles config file" msgstr "فایل پیکربندی پروفایل ها" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "وبسایت" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "لاگ تغییرات" #: qt/app.py:525 msgid "FAQ" msgstr "سوالات متداول" #: qt/app.py:528 msgid "Ask a question" msgstr "سوال بپرس" #: qt/app.py:531 msgid "Report a bug" msgstr "گزارش باگ" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "مترجم‌ها" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "درباره" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "بازگردانی" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "فایل ها یا پوشه های انتخاب شده را به مقصد اصلی بازگردان." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "بازگردانی به …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "فایل ها یا پوشه های انتخاب شده را به محل جدید بازگردان." #: qt/app.py:552 #, fuzzy msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "پوشه درحال نمایش را با تمام محتویات اش را به مقصد اصلی بازگردان." #: qt/app.py:557 #, fuzzy msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "پوشه درحال نمایش را با تمام محتویات اش را به مقصد جدید بازگردان." #: qt/app.py:560 msgid "Up" msgstr "بالا" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "نمایش فایل های مخفی" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "گرفتن اسنپ‌شات" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "بازگردان" #: qt/app.py:644 msgid "&Help" msgstr "کمک" #: qt/app.py:761 #, fuzzy msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "اگر شما این پنجره را ببندید، Back In Time دیگر قادر نیست که سیستم شما را بعد از تمام شدن اسنپ‌شات خاموش کند.\n" "میخوای واقعاً ببندی؟" #: qt/app.py:905 msgid "Working:" msgstr "در حال کار کردن:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "تامام، پشتیبان‌گیری لازم نیست" #: qt/app.py:962 msgid "Working" msgstr "درحال کار" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "خطا" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "فرستاده‌شده" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "سرعت" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "ETA" #: qt/app.py:1050 msgid "Global" msgstr "جهانی" #: qt/app.py:1051 msgid "Root" msgstr "ریشه" #: qt/app.py:1052 msgid "Home" msgstr "خانه" #: qt/app.py:1067 msgid "Backup folders" msgstr "پوشه های پشتیبانی" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "نام اسنپ‌شات" #: qt/app.py:1202 #, fuzzy 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:1294 #, fuzzy, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "فایل های محلی را پستیبان بگیر قبل از رونویسی کردن یا\n" "حذف کردن با دنباله‌ی {suffix}." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "نسخه‌های جدیدتر از فایل‌ها با دنباله {suffix} قبل از بازگردانی تغییر نام داده خواهند شد.\n" "اگر دیگر به آن احتیاجی ندارید، میتوانید آن را با {cmd} حذف کنید" #: qt/app.py:1314 #, 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:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "حذف کردن فایل های جدیدتر در پوشه اصلی" #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "فایل‌ها یا پوشه‌های انتخاب شده را به محل اصلی بازگردان و\n" "فایل‌هایی/پوشه‌هایی که در اسنپ‌شات نیستند را پاک کن.\n" "این فایل‌هایی/پوشه‌هایی را که در هنگام اسنپ‌شات گرفتن مورد استثنا قرار گرفته شده‌اند را پاک خواهد کرد!\n" "شدیداً مواظب باش، شدید!!!" #: qt/app.py:1361 #, 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] "" "آیا شما واقعا میخواهید این فایل(ها) را بازگردانی کنید\n" "به پوشه جدید {path}" msgstr[1] "" "آیا شما واقعا میخواهید این فایل(ها) را بازگردانی کنید\n" "به پوشه جدید {path}" #: qt/app.py:1370 #, fuzzy 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" "آیا شما مطمئنید که میخواهید تمامی فایل های جدیدتر را در {path} حذف کنید؟" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "آیا شما مطمئنید که میخواهید تمامی فایل‌های جدیدتر را در پوشه اصلی خود حذف " "کنید؟" #: qt/app.py:1393 #, fuzzy msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "هشدار: پاک کردن فایل‌ها در فایل های سیستمی ریشه می‌تواند کل سیستم شما را " "خراب کند!!!" #: qt/app.py:1623 msgid "Snapshot" msgstr "اسنپ‌شات" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "بازگرداندن {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "{path} را بازگردان به ..." #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "نویسندگان" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "مترجم‌ها" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "لایسنس" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 #, fuzzy msgid "System default" msgstr "پیش‌فرض" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "استفاده از زبان سیستم عامل." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "ترجمه شده: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "پلتفرم ترجمه" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "ترجمه شما" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "آخرین نمای لاگ" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "نمای لاگ اسنپ‌شات" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "پروفایل" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "اسنپ‌شات‌ها" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "فیلتر" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "همه" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "تغییرات" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "خطاها" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "اطلاعات" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] خطا، [I] اطلاعات، [C] تغییرات" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "رمزگشایی مسیرها" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "کپی" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "رمزگشایی" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "آیا می‌خواهید این را استثنا قرار بدهید؟" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "سوال" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "آخرین لاگ را ببین" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "اجرای {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "درحال کار…" #: qt/qttools.py:370 msgid "Today" msgstr "امروز" #: qt/qttools.py:377 msgid "Yesterday" msgstr "دیروز" #: qt/qttools.py:386 msgid "This week" msgstr "این هفته" #: qt/qttools.py:393 msgid "Last week" msgstr "آخرین هفته" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "این یک اسنپ‌شات نیست ولی یک نمای زنده از فایل محلی شماست" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "آخرین بررسی {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "لاگ را کامل نشان بده" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "مدیریت پروفایل‌ها" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "ویرایش" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "اضافه" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "حذف" #: qt/settingsdialog.py:127 msgid "&General" msgstr "کلی" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "حالت" #: qt/settingsdialog.py:152 #, 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\" بیندازید." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "کجا اسنپ‌شات‌ها ذخیره شود" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "پوشه" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "تنظیمات SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "میزبان(هاست)" #: qt/settingsdialog.py:204 msgid "Port" msgstr "پورت" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "کاربر" #: qt/settingsdialog.py:214 msgid "Path" msgstr "مسیر" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "متن رمز" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "کلید خصوصی" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "یک فایل کلید خصوصی که وجود داره انتخاب کنید (به طور معمول \"id_rsa\" " "نامگذاری شده)" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" "یک کلید SSH جدید بدون رمز عبور بساز (مجاز نیست اگر یک فایل کلید خصوصی درحال " "حاضر انتخاب شده باشد)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "رمز عبور" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "رمز عبور را در دسته کلید ذخیره کنید" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "رمز عبور کش برای کرون (مشکل امنیتی: ریشه می‌تواند رمز عبور را بخواند)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "پیشرفته" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "مسیر کامل اسنپ‌شات" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "برنامه‌ریزی" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "غیرفعال" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "در هر بوت/ریبوت" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "هر {n} دقیقه" msgstr[1] "هر {n} دقیقه" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "هر ساعت" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "هر {n} ساعت" msgstr[1] "هر {n} ساعت" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "ساعت سفارشی" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "هر روز" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "مکرر (آناکرون)" #: qt/settingsdialog.py:375 #, fuzzy msgid "When drive gets connected (udev)" msgstr "وقتی درایو متصل شد (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "هر هفته" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "هر ماه" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "هر سال" #: qt/settingsdialog.py:383 msgid "Day" msgstr "روز" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "روز هفته" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "ساعت‌" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "ساعت" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Back In Time را مکرر اجرا کن. این زمانی کاربردی است که رایانه معمولاً روشن " "نیست." #: qt/settingsdialog.py:442 msgid "Every" msgstr "هر" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "ساعت" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "روز" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "هفته" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "ماه" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "شامل" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "فایل‌ها و پوشه‌ها را شامل کن" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "اضافه کردن فایل" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "اضافه کردن پوشه" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "استثنا" #: qt/settingsdialog.py:528 #, fuzzy, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "کارت های وحشی({example1}) مورد توجه قرار نخواهند گرفت با حالت 'SSH رمزنگاری‌شده'.\n" "فقط ستاره های جداگانه مجاز است ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "الگوها، فایل‌ها یا پوشه‌ها را استثنا کن" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "بسیار پیشنهاد شده است" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "اضافه کردن پیش‌فرض" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "استثنا قرار بده فایل های بزرگتر از: " #: qt/settingsdialog.py:594 #, fuzzy, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "فایل‌های بزرگتر از مقدار در %(prefix)s را استثنا قرار بده.\n" "با 'حالت rsync کامل' غیرفعال، این فقط برروی فایل‌های جدید اثر می‌گذارد.\n" "به دلیل rsync، این یک گزینه منتقل‌کننده است، نه یک گزینه استثنا.\n" "به این خاطر فایل‌های بزرگی ک قبلاً پشتیبان‌گیری شده بودند در اسنپ‌شات‌ها باقی میمانند\n" "حتی اگر تغییر کنند." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "حذف خودکار" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "قدیمی‌تر از" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "سال" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "اگر هست حافظه آزاد کمتر از" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "اگر هست آی‌نود های آزاد کمتر از" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "حذف هوشمند" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "در پس‌زمینه برروی هاست ریموت اجرا کن." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "تجربی" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "تمامی اسنپ‌شات ها را تا آخر نگهدار" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "روز‌" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "یک اسنپ‌شات را در روز تا آخر نگهدار" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "یک اسنپ‌شات را در هفته تا آخر نگهدار" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "هفته" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "یک اسنپ‌شات را در ماه تا آخر نگهدار" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "ماه" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "یک اسنپ‌شات را در سال تا سالیان سال نگهدار" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "اسنپ‌شات‌های نامگذاری شده را حذف نکن" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "گزینه‌ها" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "فعال کردن اعلان‌ها" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "غیرفعال کردن اسنپ‌شات‌ها در هنگام باتری" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "وضعیت باتری از طرف سیستم در دسترس نیست" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "فقط یک اسنپ‌شات در زمان اجرا کن" #: qt/settingsdialog.py:763 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "دیگر اسنپ‌شات ها تا وقتی که اسنپ‌شات فعلی تمام نشده است، مسدود می‌شوند.\n" "این یک گزینه جهانی است. به این خاطر برای تمامی پروفایل های کاربر اثر می‌گذارد.\n" "ولیکن که همچنین شما نیاز دارید که این را برای همه کاربرها فعال کنید." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "پشتیبان‌گیری از فایل‌های تغییر مکان یافته در هنگام بازگردانی" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "ادامه دادن در هنگام خطا (اسنپ‌شات ناقص میمانند)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "از سرجمع برای شناسایی تغییرات استفاده کن" #: qt/settingsdialog.py:793 #, fuzzy msgid "Take a new snapshot whether there were changes or not." msgstr "یک اسنپ‌شات جدید بگیر بدون در نظر گرفتن اینکه آیا تغییر داشته یا خیر." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "سطح لاگ" #: qt/settingsdialog.py:805 msgid "None" msgstr "هیچکدام" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "گزینه‌های حرفه‌ای" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "این گزینه ها را اگر میدانی که داری چه کار میکنی تغییر بده!" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "'rsunc' را با '{cmd}' اجرا کن:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "به عنوان کار کرون" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "برروی هاست ریموت" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "وقتی که درحال گرفتن اسنپ‌شات دستی است" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(لطفاً 'nocache' را نصب کنید تا این گزینه فعال شود)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "برروی ماشین محلی" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "stdout را به /dev/null در کار‌های کرون منتقل کنید." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "stderr را به /dev/null در کار‌های کرون منتقل کنید." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "استفاده پهنای باند rsync را محدود کن" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/ثانیه" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "حفظ ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "حفظ ویژگی‌های گسترده (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "کپی پیوندهای ناامن (فقط با پیوندهای مطلق کار میکند)" #: qt/settingsdialog.py:1024 #, fuzzy, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "گزینه‌ها باید نقل شده باشند مثل {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "پیست گزینه های اضافه به rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "پیشوندی برای اجرا قبل از هر دستور برروی هاست ریموت.\n" "متغیر ها نیاز دارند که با \\$FOO فرار کنند.\n" "این rsync را لمس نمیکند. پس برای اضافه کردن پیشوند\n" "برای rsync استفاده کن از \"%(cbRsyncOptions)s\" با\n" "%(rsync_options_value)s\n" "\n" "%(default)s:%(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "پیش‌فرض" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "اضافه کردن پیشوند به دستورات SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "بررسی کن اگر هاست ریموت آنلاین هست" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "هشدار: اگر غیرفعال بود و هاست ریموت\n" "در دسترس نیست، این میتواند باعث\n" "خطاهایی عجیب غریب شود." #: qt/settingsdialog.py:1071 #, fuzzy msgid "Check if remote host supports all necessary commands" msgstr "بررسی کن که اگر هاست ریموت تمامی دستورات ضروری را پشتیبانی میکند" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "هشدار: اگر غیرفعال بود و هاست ریموت\n" "تمام دستورات ضروری را پشتیبانی نمیکند،\n" "این میتواند باعث خطاهایی عجیب غریب شود." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "بازگردانی پیکربندی" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "ویرایش user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "پروفایل جدید" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "تغییر نام پروفایل" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "آیا شما واقعا مطمئن هستید که میخواهید پروفایل \"{name}\" را پاک کنید؟" #: qt/settingsdialog.py:1416 #, 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 برای پشتیبان‌گیری دوره‌ای هر سه ساعت" #: qt/settingsdialog.py:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "شما برای SSH یک فایل کلید خصوصی انتخاب نکرده‌اید.\n" "آیا مایل به تمایلات هستید که یک جفت کلید خصوصی/عمومی بدون رمز عبور جدید بسازیم؟" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "فایل کلید خصوصی \"{file}\" وجود ندارد." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "آیا مایل به تمایلات هستید که کلید SSH عمومی خود را کپی کنید به\n" "هاست ریموت تا ورود بدون رمز عبور فعال شود؟" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "اعتباری از هاست {host} نمی‌توان ایجاد کرد.\n" "\n" "کلید اثر انگشت {keytype} هست:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" "لطفاً این اثر انگشت را تایید کنید! آیا مایل به تمایلات هستید که این را به " "فایل 'known_hosts' خودتان اضافه کنید؟" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "استثنا کردن الگو" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "استثنا کردن فایل" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "استثنا کردن پوشه" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "شامل کردن فایل" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, fuzzy, 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:1893 msgid "Include folder" msgstr "شامل کردن پوشه" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "آیا شما میخواهید پوشه اسنپ‌شات را تغییر دهید؟" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "ساختن کلید SSH جدید در {path} شکست خورد" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "مسیر کامل اسنپ‌شات: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "فعال" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "غیرفعال" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "بازگردانی تنظیمات" #: qt/settingsdialog.py:2125 #, fuzzy, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "لطفاً به سمت اسنپ‌شات هدایت شوید از جایی که میخواید پیکربندی {appName} را بازگردانی کنید. مسیر شاید به این شکل باشد:\n" "{samplePath}\n" "\n" "اگر اسنپ‌شات‌ها شما روی یک درایو ریموت است یا آنها رمزنگاری شده‌اند، شما اول باید آنها را دستی مانت کنید. اگر شما از حالت SSH استفاده میکنید شاید نیاز داشته باشید که ورود کلید عمومی راه اندازی کنید به هاست ریموت {addFuse}.\n" "یه نگاه به 'man backintime' بنداز." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "هیچ پیکربندی‌ پیدا نشد" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "اسکریپت user-callback هیچ خط shebang ندارد (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang در اسکریپت user-callback قابل اجرا نیست." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "از ۱٪ و ۲٪ برای پارامترهای مسیر استفاده کن" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "فقط اسنپ‌شات‌ها را مقایسه کن" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "فقط اسنپ‌شات‌های مساوی را لیست کن به: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "بررسی ژرفناک (بیشتر دقیق، ولی کند)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "پاک کردن" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "انتخاب همه" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "برو به" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "گزینه‌ها" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "شما نمی‌توانید یک اسنپ‌شات را با خودش مقایسه کنید" #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "دستور پیدا نشده" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "آیا شما جدی می‌خواین \"{file}\" را در اسنپ‌شات \"{snapshot_id}\" پاک کنید؟" #: qt/snapshotsdialog.py:375 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "آیا شما جدی میخواهید \"{file}\" را در {count} (تا) اسنپ‌شات پاک کنید؟" #: qt/snapshotsdialog.py:380 #, fuzzy msgid "This cannot be revoked!" msgstr "این نمی‌تواند لغو بشود!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "هشدار" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "\"{path}\" را مورد استثنا قرار میدهی از اسنپ‌شات‌های آینده؟" #~ msgid " and add your user to group 'fuse'" #~ msgstr " و کاربر خود را به گروه 'fuse' اضافه کنید" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "\"%s\" یک پیوند نمادین است. هدف پیوند خورده پشتیبان‌گیری نمیشود تا وقتی که شما این را هم شامل کنید.\n" #~ "آیا مایل به تمایلات هستید که در عوض پیوندهای نمادین هدف را شامل کنید؟" #~ msgid "&Snapshot" #~ msgstr "اسنپ‌شات‌ها" #~ msgid "&View" #~ msgstr "نمایه" #~ msgid "Config File Help" #~ msgstr "پیکربندی فایل کمک" #~ msgid "Diff" #~ msgstr "مقایسه کن" #~ msgid "Diff Options" #~ msgstr "گزینه‌های مقایسه" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "پشتیبان‌گیری کل سیستم فقط میتواند یک اسنپ‌شات بسازد که در همان لوح(های) فیزیکی با همان پارتیشن بندی لوح از منبع، بازگردانی شود؛ بازگردانی کردن به لوح های فیزیکی یا همان لوح‌ها با پارتیشن بندی متفاوت، یک سیستم بالقوه خراب و ناامن ایجاد میکند.\n" #~ "\n" #~ "پشتیبان‌گیری کامل سیستم بعضی از تنظیمات را که سفارشی‌سازی شده‌اند را لغو خواهد کرد. ادامه میدهی؟" #~ msgid "Local encrypted" #~ msgstr "رمزنگاری شده محلی" #~ msgid "Modify for Full System Backup" #~ msgstr "اصلاح کردن برای پشتیبان کل سیستم" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "پروفایل:\"{name}\"" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "فایل‌ها یا پوشه‌ها را بازگردان.\n" #~ "اگر این دکمه خاکستری شد، به نظر میاید که \"{now}\" در دست چپ لیست اسنپ‌شات‌ انتخاب شده است." #~ msgid "SSH" #~ msgstr "اس‌اس‌اچ(SSH)" #~ msgid "Settings" #~ msgstr "تنظیمات" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "نمایش محتوای لوح فعلی" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "نمایش اسنپ‌شات ساخته شده در {timestamp}" #~ msgid "Working..." #~ msgstr "در حال کار..." #~ msgid "You can't include backup folder!" #~ msgstr "شما نمی‌توانید پوشه پشتیبان را شامل کنید!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "شما نمی‌توانید زیرپوشه پشتیبان را شامل کنید!" #~ msgid "You can't remove the last profile!" #~ msgstr "شما نمیتوانید آخرین پروفایل را حذف کنید!" backintime-1.4.3/common/po/fi.po000066400000000000000000001441471455673541400165220ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-14 11:56+0000\n" "Last-Translator: tsilari \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Varoitus" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Pääprofiili" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Paikallinen" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH – yksityinen avain" #: common/config.py:304 #, fuzzy msgid "encrypted" msgstr "Paikallinen salattu" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Salaus" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH salattu" #: common/config.py:317 msgid "Default" msgstr "Oletus" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profiili: ”{name}”" #: common/config.py:349 #, fuzzy msgid "Snapshots folder is not valid!" msgstr "Otoskansio ei ole kelvollinen!" #: common/config.py:361 #, fuzzy msgid "You must select at least one folder to back up!" msgstr "Valitse varmuuskopioitavaksi vähintään yksi kansio!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Backup -kansiota ei voi sisällyttää." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Backup -alikansiota ei voi sisällyttää." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Väärä arvo. {path} ei ole kansio." #: common/config.py:457 #, fuzzy msgid "Host/User/Profile-ID must not be empty." msgstr "Kone/käyttäjätunnus/profiilin tunnus ei voi olla tyhjä." #: common/config.py:467 common/config.py:514 #, fuzzy, 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:484 #, fuzzy, 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:493 #, fuzzy, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopioi linkit (ratkaise symboliset linkit)" #: common/config.py:498 msgid "Expert Options" msgstr "Asiantuntija-asetukset" #: common/config.py:502 #, 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 "" "Kansion ”{path}” kohdetiedostojärjestelmä on liitetty sshfs-jako. sshfs ei " "tue kovia linkkejä. Käytä tämän sijaan SSH-tilaa." #: common/config.py:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "crontab-tiedostoa ei löydy.\n" "Varmista, että cron on asennettu.\n" "Ellei ole, poista automaattinen varmuuskopiointi käytöstä." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Uuden crontabin kirjoittaminen epäonnistui." #: common/config.py:1707 #, fuzzy, 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:1722 #, fuzzy, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Udev-aikataulu ei toimi {mode}-tilassa" #: common/config.py:1733 #, fuzzy, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Kohteelle ”{path}” ei löytynyt UUID:tä" #: common/configfile.py:107 #, fuzzy msgid "Failed to save config" msgstr "Määrityksen tallentaminen epäonnistui" #: common/configfile.py:143 #, fuzzy msgid "Failed to load config" msgstr "Määrityksen lataaminen epäonnistui" #: common/configfile.py:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profiili ”{name}” on jo olemassa." #: common/configfile.py:736 #, fuzzy msgid "The last profile cannot be removed." msgstr "Tätä ei voi kumota." #: common/encfstools.py:92 #, fuzzy, 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 #, fuzzy 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 password" msgstr "Vahvista salasana" #: common/encfstools.py:160 #, fuzzy msgid "Password doesn't match." msgstr "Salasanat eivät täsmää." #: common/encfstools.py:178 #, fuzzy 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Ota otos" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Ei voi vapauttaa {mountprocess} kohteesta {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 #, fuzzy msgid "Mountpoint {} not empty." msgstr "liitospiste %s ei ole tyhjä." #: common/password.py:240 #, fuzzy, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profiili ”{profile}”: Anna {mode}-salasana: " #: common/snapshots.py:342 common/snapshots.py:593 #, fuzzy msgid "FAILED" msgstr "EPÄONNISTUI" #: common/snapshots.py:539 common/snapshots.py:601 #, fuzzy msgid "Restore permissions" msgstr "Palauta käyttöoikeudet" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Valmis" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Viivästetään varmuuskopiointia akkukäytön ajan" #: common/snapshots.py:770 #, fuzzy 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Odotetaan %s sekunti." msgstr[1] "Odotetaan %s sekuntia." #: common/snapshots.py:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Otoksen {snapshot_id} luominen epäonnistui." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Viimeistellään" #: common/snapshots.py:949 #, fuzzy msgid "Can't create folder" msgstr "Ei voitu luoda kansiota" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Tallennetaan määritystiedostoa…" #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Tallennetaan käyttöoikeuksia…" #: common/snapshots.py:1150 #, fuzzy, 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:1169 #, fuzzy, 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:1179 #, fuzzy msgid "Can't remove folder" msgstr "Ei voida poistaa kansiota" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Otetaan otosta" #: common/snapshots.py:1254 msgid "Success" msgstr "Onnistui" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Osittain siirretty johtuen puuttuvista lähdetiedostoista ( kts. 'man rsync')" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' päättyi paluukoodilla {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Katso lisätietoja: 'man rsync'" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Mikään ei ole muuttunut: uusi otos ei ole tarpeen" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Ei voi uudelleennimetä {new_path} nimelle {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Älykäs poisto" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Poistetaan vanhoja otoksia" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Yritetään pitää vapaa vähimmäistila" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Yritetään pitää vähintään {perc} inodeja vapaana" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Nyt" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Ei voida liittää {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 #, fuzzy msgid "Remote path exists but is not a directory." msgstr "Etäsijainti on olemassa muttei kansio." #: common/sshtools.py:658 #, fuzzy msgid "Remote path is not writable." msgstr "Etäsijaintiin ei voi kirjoittaa." #: common/sshtools.py:663 #, fuzzy msgid "Remote path is not executable." msgstr "Etäsijainti ei ole ohjelmatiedosto." #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Etäsijaintia ei voitu luoda." #: common/sshtools.py:948 #, fuzzy, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Etäkone {host} ei tue komentoa {command}" #: common/sshtools.py:952 common/sshtools.py:961 #, fuzzy msgid "Look at 'man backintime' for further instructions" msgstr "Lisätietoa: ”man backintime”" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, fuzzy, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Etäkone {host} ei tue kovia linkkejä" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Vahvista käyttäjän \"{user}\" salasana" #: qt/app.py:167 msgid "Shortcuts" msgstr "Pikanäppäimet" #: qt/app.py:187 #, fuzzy msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Tätä kansiota ei ole\n" "valitussa otoksessa." #: qt/app.py:252 msgid "Add to Include" msgstr "Lisää mukaan otettaviin" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Lisää pois jätettäviin" #: qt/app.py:339 #, fuzzy, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "{appName} on määrittämättä. Haluatko palauttaa aiemman määrityksen?" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Otoskansiota ei löydy.\n" "Jos kansio on siirrettävällä asemalla, kytke se ja napsauta OK." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "Ota otos" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 #, fuzzy msgid "Take a snapshot (checksum mode)" msgstr "Ota otos tarkistussummin" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "Havaitse muutokset tarkistussummista." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Keskeytä otosprosessi" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Jatka otosprosessia" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Pysäytä otosprosessi" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Virkistä otosluettelo" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Ota otos" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Poista otos" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Näytä otosloki" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Näytä viimeisin loki" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Hallinnoi profiileja…" #: qt/app.py:500 msgid "Shutdown" msgstr "Sammuta" #: qt/app.py:502 #, fuzzy msgid "Shut down system after snapshot has finished." msgstr "Sammuta järjestelmän otoksen valmistuttua." #: qt/app.py:504 msgid "Setup language…" msgstr "Asennuskieli…" #: qt/app.py:508 msgid "Exit" msgstr "Lopeta" #: qt/app.py:512 msgid "Help" msgstr "Ohje" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Tallennetaan määritystiedostoa" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Sivusto" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Muutosloki" #: qt/app.py:525 msgid "FAQ" msgstr "UKK" #: qt/app.py:528 msgid "Ask a question" msgstr "Esitä kysymys" #: qt/app.py:531 msgid "Report a bug" msgstr "Ilmoita ohjelmavirheestä" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "Käännökset" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Tietoa" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Palauta" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "Palauta valitut tiedostot tai kansiot alkuperäiseen paikkaansa." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Palauta kohteeseen…" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "Palauta valitut tiedostot tai kansiot uuteen paikkaan." #: qt/app.py:552 #, fuzzy 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:557 #, fuzzy 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:560 msgid "Up" msgstr "Ylös" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Näytä piilotiedostot" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Vertaa otoksia…" #: qt/app.py:627 msgid "&Backup" msgstr "&Varmuuskopio" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "&Palauta" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "&Ohje" #: qt/app.py:761 #, fuzzy msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Jos suljet tämän ikkunan, BackInTime ei voi sammuttaa järjestelmää otoksen valmistuttua.\n" "Haluatko varmasti sulkea?" #: qt/app.py:905 msgid "Working:" msgstr "Käsitellään:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Valmis, varmuuskopiota ei tarvittu" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Käsitellään" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Virhe" #: qt/app.py:994 qt/qtsystrayicon.py:202 #, fuzzy msgid "Sent" msgstr "Lähetetty" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Nopeus" #: qt/app.py:996 qt/qtsystrayicon.py:204 #, fuzzy msgid "ETA" msgstr "Jäljellä" #: qt/app.py:1050 msgid "Global" msgstr "Yleisasetukset" #: qt/app.py:1051 msgid "Root" msgstr "Juuri" #: qt/app.py:1052 msgid "Home" msgstr "Koti" #: qt/app.py:1067 msgid "Backup folders" msgstr "Varmuuskopiokansiot" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Otoksen nimi" #: qt/app.py:1202 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 nämä otokset?" msgstr[1] "Oletko varma että haluat poistaa nämä otokset?" #: qt/app.py:1294 #, fuzzy, 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:1300 qt/settingsdialog.py:774 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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/app.py:1314 #, fuzzy 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:1347 msgid "Remove newer elements in original folder." msgstr "Poista alkuperäiskansiosta uudemmat elementit." #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Palauta valitut tiedostot tai kansiot alkuperäiseen paikkaan ja\n" "poista tiedostot tai kansiot, joita otoksessa ei ole.\n" "Tämä poistaa tiedostot ja kansiot, jotka jätettiin otoksesta pois!\n" "Ole hyvin varovainen!" #: qt/app.py:1361 #, 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 nämä tiedostot uuteen kansioon\n" "{path}?" msgstr[1] "" "Haluatko varmasti palauttaa nämä tiedostot uuteen kansioon\n" "{path}?" #: qt/app.py:1370 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:1385 #, fuzzy, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "VAROITUS: tiedostojen poistaminen tiedostojärjestelmän juuresta voi rikkoa " "järjestelmän!!!" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Otokset" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Palauta {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Palauta {path} kohteeseen…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "Kielivalinta astuu voimaan uudelleenkäynnistyksen jälkeen." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Tekijät" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Käännökset" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Lisenssi" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Asennuskieli" #: qt/languagedialog.py:87 msgid "System default" msgstr "Järjestelmän oletus" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Käytä järjestelmän kielivalintaa." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Käännetty: {percent}" #: qt/languagedialog.py:184 #, 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:216 #, fuzzy msgid "translation platform" msgstr "Käännökset" #: qt/languagedialog.py:232 #, fuzzy msgid "Your translation" msgstr "Käännökset" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Viimeisin lokinäkymä" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Otoslokinäkymä" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profiili" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Otokset" #: qt/logviewdialog.py:94 #, fuzzy msgid "Filter" msgstr "Suodatin" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Kaikki" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Muutokset" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Virheet" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Tiedot" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Virhe, [I] Tietoa, [C] Muutokset" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopioi" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Pura koodaus" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Haluatko jättää tämän pois?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Kysymys" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Näytä viimeisin loki" #: qt/qtsystrayicon.py:107 #, fuzzy, python-brace-format msgid "Start {appname}" msgstr "Käynnistä {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Käsitellään…" #: qt/qttools.py:370 msgid "Today" msgstr "Tänään" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Eilen" #: qt/qttools.py:386 msgid "This week" msgstr "Tällä viikolla" #: qt/qttools.py:393 msgid "Last week" msgstr "Viime viikolla" #: qt/qttools.py:539 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:544 #, fuzzy, python-brace-format msgid "Last check {time}" msgstr "Viimeksi tarkistettu {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Näytä täysi loki" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Pääprofiili" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Muokkaa" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Lisää" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Poista" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "&Yleisasetukset" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 #, fuzzy msgid "Mode" msgstr "Tila" #: qt/settingsdialog.py:152 #, 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} käyttää salaukseen EncFS:ää. Tietoturvatarkistus paljasti siitä " "äskettäin haavoittuvuuksia. Ks. ”man backintime”, ”A NOTE ON SECURITY”." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Mihin otokset tallennetaan" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Kansio" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH-asetukset" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "Konenimi" #: qt/settingsdialog.py:204 #, fuzzy msgid "Port" msgstr "Portti" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 #, fuzzy msgid "User" msgstr "Käyttäjä" #: qt/settingsdialog.py:214 #, fuzzy msgid "Path" msgstr "Sijainti" #: qt/settingsdialog.py:220 #, fuzzy msgid "Cipher" msgstr "Salausmenetelmä" #: qt/settingsdialog.py:226 #, fuzzy msgid "Private Key" msgstr "Yksityinen avain" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Salasana" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Tallenna salasana avainrenkaaseen" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Lisäasetukset" #: qt/settingsdialog.py:336 #, fuzzy msgid "Full snapshot path" msgstr "Otoksen täydellinen sijainti" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Aikataulu" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Ei käytössä" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Käynnistysten yhteydessä" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, 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:363 msgid "Every hour" msgstr "Tunnin välein" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 #, fuzzy msgid "Custom hours" msgstr "Mukautetut tunnit" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Päivittäin" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Toistuva (anacron)" #: qt/settingsdialog.py:375 #, fuzzy msgid "When drive gets connected (udev)" msgstr "Asemaa kytkettäessä (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Viikoittain" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Kuukausittain" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Joka vuosi" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Päivä" #: qt/settingsdialog.py:394 #, fuzzy msgid "Weekday" msgstr "Viikonpäivä" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "Tunti" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "Tunnit" #: qt/settingsdialog.py:435 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:442 #, fuzzy msgid "Every" msgstr "Joka" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Päivä(ä)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Viikko(a)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Ota mukaan" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Ota mukaan tiedostoja ja kansioita" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Lisää tiedosto" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Lisää kansio" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Jätä pois" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Jätä pois kuvioita, tiedostoja ja kansioita" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "Erityisesti suositellaan" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Lisää oletukset" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Jätä pois tätä suuremmat tiedostot: " #: qt/settingsdialog.py:594 #, fuzzy, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." 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:616 #, fuzzy msgid "&Auto-remove" msgstr "Automaattinen &poisto" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Vanhemmat kuin" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Vuosi(a)" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Kun vapaata tilaa on alle" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Kun vapaita inodeja on alle" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Älykäs poisto:" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "Suorita etäkoneessa taustalla." #: qt/settingsdialog.py:690 #, fuzzy msgid "EXPERIMENTAL" msgstr "KOKEELLINEN" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Säilytä kaikki otokset" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "viime päivältä (/päiviltä)." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Säilytä yksi otos kultakin päivältä" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Säilytä yksi otos kultakin viikolta" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "viime viikolta (/viikoilta)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Säilytä yksi otos kultakin kuulta" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "viime kuulta (/kuukausilta)." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Säilytä yksi otos kultakin vuodelta." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Älä poista nimettyjä otoksia." #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "&Valinnat" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Käytä ilmoituksia" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Älä tee otoksia akkukäytössä" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Järjestelmä ei tarjoa virranhallintatietoja" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Tee vain yksi otos kerrallaan" #: qt/settingsdialog.py:763 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Muut otokset estetään, kunnes nykyinen on valmis.\n" "Tämä on yleisasetus, joten se vaikuttaa käyttäjän kaikkiin profiileihin.\n" "Muille käyttäjille asetus täytyy kuitenkin ottaa käyttön erikseen." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Varmuuskopioi palautettaessa korvatut tiedostot" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Jatka virheistä huolimatta (säilytä keskeneräiset otokset)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Havaitse muutokset tarkistussummista" #: qt/settingsdialog.py:793 #, fuzzy msgid "Take a new snapshot whether there were changes or not." msgstr "Tee uusi otos, vaikkei muutoksia olisi." #: qt/settingsdialog.py:800 #, fuzzy msgid "Log Level" msgstr "Lokitaso" #: qt/settingsdialog.py:805 msgid "None" msgstr "Ei mitään" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "A&siantuntija-asetukset" #: qt/settingsdialog.py:830 #, 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Suorita rsync komennolla '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "cron-töissä" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "etäkoneilla" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "käyttäjän tehdessä itse otoksia" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "paikallisella koneella" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Ohjaa cron-töiden vakiotulosvirta /dev/nulliin." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Ohjaa cron-töiden vakiovirhevirta /dev/nulliin." #: qt/settingsdialog.py:915 #, fuzzy msgid "Limit rsync bandwidth usage" msgstr "Rajoita rsyncin kaistan käyttöä" #: qt/settingsdialog.py:918 #, fuzzy msgid "KB/sec" msgstr "kt/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Säilytä ACL:t" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Säilytä lisämääritteet (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopioi turvattomat linkit (toimii vain absoluuttisille linkeille)" #: qt/settingsdialog.py:1024 #, fuzzy, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Valitsinten arvot tulee merkitä lainausmerkkeihin, esim. {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Liitä lisävalinnat rsyncille" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "oletus" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Lisää SSH-komentoihin etuliite" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Tarkista, onko etäkone verkossa" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Varoitus: ellei ole käytössä eikä etäkone\n" "ole käytettävissä, voi johtaa outoihin\n" "virheisiin." #: qt/settingsdialog.py:1071 #, fuzzy msgid "Check if remote host supports all necessary commands" msgstr "Tarkista, tukeeko etäkone kaikkia vaadittuja komentoja" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Varoitus: ellei ole käytössä eikä etäkone tue\n" "kaikkia vaadittuja komentoja, voi johtaa\n" "outoihin virheisiin." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Palauta määritys" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Muokkaa käyttäjäkutsua" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Uusi profiili" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Muuta profiilin nimeä" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Haluatko varmasti poistaa profiilin ”{name}”?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Et valinnut SSH:lle yksityistä avainta.\n" "Haluatko luoda uuden salasanattoman julkisen ja yksityisen avaimen parin?" #: qt/settingsdialog.py:1469 #, fuzzy, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Yksityistä avaintiedostoa ”{file}” ei ole olemassa." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Haluatko kopioida julkisen SSH-avaimesi etäkoneelle\n" "salasanatonta kirjautumista varten?" #: qt/settingsdialog.py:1649 #, fuzzy, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Koneen ”{host}” autenttisuutta ei voida todentaa.\n" "\n" "{keytype}-avaimen sormenjälki on:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Jätä pois kuvio" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Jätä pois tiedosto" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Jätä pois kansio" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Ota mukaan tiedosto" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, fuzzy, 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:1893 msgid "Include folder" msgstr "Ota mukaan kansio" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Haluatko varmasti muuttaa otoskansiota?" #: qt/settingsdialog.py:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Otoksen täydellinen sijainti: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "käytössä" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "ei käytössä" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Palauta asetukset" #: qt/settingsdialog.py:2125 #, fuzzy, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Siirry otokseen, josta haluat palauttaa {appName}-määrityksen. Sijainti näyttää tälllaiselta:\n" "{samplePath}\n" "\n" "Jos otos on etäasemalla tai salattu, ne on ensin käyttäjän itse liitettävä. SSH-tilassa sinun on myös asetettava julkisen avaimen kirjautuminen etäkoneeseen.\n" "Katso lisätietoa: ”man backintime”." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "käyttäjäkutsuskriptistä puuttuu #!-rivi." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Käyttäjäkutsuskriptin #!-rivi ei viittaa ohjelmatiedostoon." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command" msgstr "Komento" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Parametrit" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Käytä sijaintiparametreina %1 ja %2" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Luettele vain otokset, jotka ovat samoja kuin: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Syvätarkistus (tarkempi, mutta hitaampi)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Poista" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Valitse kaikki" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Siirry" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Valinnat" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Otosta ei voi verrata itseensä." #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "Komentoa ei löydy" #: qt/snapshotsdialog.py:369 #, fuzzy, 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:375 #, fuzzy, 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:380 #, fuzzy msgid "This cannot be revoked!" msgstr "VAROITUS: Tätä ei voi kumota!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Jätetäänkö ”{path}” pois myöhemmistä otoksista?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " ja lisättävä käyttäjätunnuksesi ”fuse”-ryhmään" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "”%s” on symlinkki. Linkin kohdetta ei varmuuskopioida, ellei sitä oteta erikseen mukaan.\n" #~ "Haluatko ottaa mukaan symlinkkien kohteet?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Tämän lokin koodaus on purettu automaattisella hakukuviolla\n" #~ "### Ellei joidenkin sijaintien koodausta ole purettu, ne voi purkaa komennolla:\n" #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "Konetta %s ei ole ssh_known_hosts-tiedostossa" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Otos" #, fuzzy #~ msgid "&View" #~ msgstr "Näkymä" #~ msgid "..." #~ msgstr "…" #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Muutokset ja virheet" #~ msgid "Config File Help" #~ msgstr "Määritystiedoston ohje" #~ msgid "Create a new SSH key without Password." #~ msgstr "Luo uusi salasanaton SSH-avain." #~ msgid "Diff" #~ msgstr "Erot" #~ msgid "Diff Options" #~ msgstr "Diff-asetukset" #~ msgid "Error:" #~ msgstr "Virhe:" #~ msgid "Every 10 minutes" #~ msgstr "10 minuutin välein" #~ msgid "Every 12 hours" #~ msgstr "12 tunnin välein" #~ msgid "Every 30 minutes" #~ msgstr "30 minuutin välein" #~ msgid "Every 4 hours" #~ msgstr "4 tunnin välein" #~ msgid "Every 5 minutes" #~ msgstr "5 minuutin välein" #~ msgid "Every 6 hours" #~ msgstr "6 tunnin välein" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Sattui tiivisteristiriita tiivistetunnisteessa %s. Kasvata yleistä " #~ "hash_collision-arvoa ja yritä uudelleen." #~ msgid "Key File" #~ msgstr "Avaintiedosto" #~ msgid "List only different snapshots" #~ msgstr "Luettele vain eri otokset" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "Salasanaton todennus %(user)s@%(host)s epäonnistui. Lisäohjeita: ”man " #~ "backintime”" #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "”ping %s” epäonnistui. Kone ei ole käynnissä tai osoite on väärin." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profiili: ”{name}”" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Palauta ”%s”" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Palauta ”%s” kohteeseen…" #, fuzzy, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Palauta valittu tiedosto tai kansio.\n" #~ "Jos painike on harmaana, ”{now}” on todennäköisimmin valittu otosluettelossa vasemmalla." #~ msgid "Run 'ionice':" #~ msgstr "Suorita ”ionice”" #~ msgid "Run 'nice':" #~ msgstr "Suorita ”nice”:" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Suorita rsync nocachellä:" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Suorita rsync nocachellä:" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Asetukset" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Otos: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Näytä levyn nykyinen sisältö" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Selaa {timestamp} tehtyä otosta" #~ msgid "WITH ERRORS !" #~ msgstr "VIRHEITÄ!" #~ msgid "Working..." #~ msgstr "Käsitellään…" #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Itse varmuuskopiokansiota ei voi lisätä!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Varmuuskopiokansion alikansiota ei voi ottaa mukaan!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Viimeistä profiilia ei voi poistaa!" backintime-1.4.3/common/po/fo.po000066400000000000000000001111441455673541400165170ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Ávaring" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Høvuðsumhvarv" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokalt" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH privatur lykil" #: common/config.py:304 msgid "encrypted" msgstr "Krypteraður" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Kryptering" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH krypterað" #: common/config.py:317 msgid "Default" msgstr "" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "Umhvarv: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Støðumyndaskjáttan er ólóglig!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} er ikki ein skjatta." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "" #: common/config.py:467 common/config.py:514 #, fuzzy, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "" #: common/config.py:498 msgid "Expert Options" msgstr "Framkomnir kostir" #: common/config.py:502 #, 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:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "" #: common/config.py:1707 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Umhvarv \"{name}\" finnst longu." #: common/configfile.py:736 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 password" msgstr "" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Tak eina løtumynd" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "" #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "" #: common/snapshots.py:539 common/snapshots.py:601 #, fuzzy msgid "Restore permissions" msgstr "Goymi loyvi" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Liðugt" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" #: common/snapshots.py:772 #, 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:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "" #: common/snapshots.py:826 msgid "Finalizing" msgstr "" #: common/snapshots.py:949 #, fuzzy msgid "Can't create folder" msgstr "Kann ikki stovna skjáttuna" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Goymi samansetingarfíluna ..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Goymi loyvi ...." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 #, fuzzy msgid "Can't remove folder" msgstr "Kaann ikki taka burtur skjáttuna" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Tak eina løtumynd" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Tak gamlar støðumyndir burtir" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Royn at halda minstamark av tøkum plássi" #: common/snapshots.py:1737 #, 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:2821 qt/app.py:1616 msgid "Now" msgstr "Nú" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Kann ikki stovna skjáttuna." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "" #: qt/app.py:167 msgid "Shortcuts" msgstr "Snarvegir" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "Tak eina løtumynd" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Hvar skullu støðumyndirnar goymast" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Tak eina løtumynd" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Tak støðumynd burtur" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Tak eina løtumynd" #: qt/app.py:492 msgid "View last log" msgstr "" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Høvuðsumhvarv" #: qt/app.py:500 msgid "Shutdown" msgstr "" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Far úr" #: qt/app.py:512 msgid "Help" msgstr "Hjálp" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Goymi samansetingarfíluna" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Heimasíða" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "" #: qt/app.py:528 msgid "Ask a question" msgstr "" #: qt/app.py:531 msgid "Report a bug" msgstr "" #: qt/app.py:534 msgid "Translation" msgstr "" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Um forritið" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Endurstovna" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Endurstovna" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "Upp" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Vís fjaldar fílur" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Tak eina løtumynd" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "&Endurstovna" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "&Hjálp" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "Arbeiði:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Liðug, eingin trygdarriting neyðug" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Arbeiði" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "Heiltøkur" #: qt/app.py:1051 msgid "Root" msgstr "Rót" #: qt/app.py:1052 msgid "Home" msgstr "Heim" #: qt/app.py:1067 msgid "Backup folders" msgstr "Trygdarritingar-skjáttur" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Navn á støðumynd" #: qt/app.py:1202 #, fuzzy 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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:1370 #, fuzzy 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Støðumyndir" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Endurstovna {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Endurstovna {path}" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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 "" "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 {language} 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Umhvarv" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Støðumyndir" #: qt/logviewdialog.py:94 #, fuzzy msgid "Filter" msgstr "Filtur" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Alt" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Broytingar" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Villur" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Upplýsingar" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Arbeiði" #: qt/qttools.py:370 msgid "Today" msgstr "Í dag" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Í gjár" #: qt/qttools.py:386 msgid "This week" msgstr "Henda vikan" #: qt/qttools.py:393 msgid "Last week" msgstr "Fyrra vikan" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Høvuðsumhvarv" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Ritstjórna" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "&Alment" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Hvar skullu støðumyndirnar goymast" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "Vertur" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 #, fuzzy msgid "User" msgstr "Brúkari" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Framkomið" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Skrá" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Ógilda" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Við hvørjari byrjan/endurbyrjan" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, 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:363 msgid "Every hour" msgstr "" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, 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:372 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Hvønn dag" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Hvørja viku" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Hvønn mánað" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Hvønn ár" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Dag(ar)" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "Tími" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "Tími" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Hvønn" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dag(ar)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Vika(ur)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Tak við" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Legg fílu til" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Legg skjáttu til" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "Út&iloka" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Eldri enn" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Ár" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Um tað tøka plássið er minni enn" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Um tað tøka plássið er minni enn" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "Dag(ar)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "Vika(ur)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Tak støðumynd burtur" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "&Kostir" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Virkja kunningar" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "" #: qt/settingsdialog.py:805 msgid "None" msgstr "Einki" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "&Framkomnir kostir" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nýtt umhvarv" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Nýnevn umhvarv" #: qt/settingsdialog.py:1142 #, fuzzy, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Tak fílu við" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Tak skjáttur við" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Er tú viss/ur í at broyta støðumyndaskjáttu ?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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:66 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Far til" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Kostir" #: qt/snapshotsdialog.py:330 #, fuzzy 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:338 #, fuzzy msgid "Command not found" msgstr "Stýriboð ikki funni" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Støðumyndir" #~ msgid "..." #~ msgstr "..." #~ msgid "Changes & Errors" #~ msgstr "Broytingar & brek" #~ msgid "Error:" #~ msgstr "Villa:" #~ msgid "Every 10 minutes" #~ msgstr "Hvønn 10 minutt" #~ msgid "Every 5 minutes" #~ msgstr "Hvønn 5 minutt" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Umhvarv: \"{name}\"" #~ msgid "Settings" #~ msgstr "Instillingar" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Støðumynd: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Sýn núverandi innihald á diski" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Sýn støðumyndina ið gjørd varð við {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "VIÐ VILLUM !" #~ msgid "Working..." #~ msgstr "Arbeiði..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Tú kann ikki hava trygdarritsskjáttuna við !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Tú kann ikki hava eina trygdarritsskjáttu við !" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Tú kannst ikki taka tað síðsta umhvarvið burtur !" backintime-1.4.3/common/po/fr.po000066400000000000000000001554331455673541400165330ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-18 16:24+0000\n" "Last-Translator: leyouki \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Attention" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Profil principal" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Local" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Clé privée SSH" #: common/config.py:304 msgid "encrypted" msgstr "chiffré" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Chiffrement" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH chiffré" #: common/config.py:317 msgid "Default" msgstr "Par défaut" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil : \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Le dossier des instantanés n'est pas valide !" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Vous devez sélectionner au moins un dossier à sauvegarder !" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Le dossier de sauvegarde ne peut pas être inclus." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Le sous-dossier de sauvegarde ne peut pas être inclus." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Option invalide. {path} n'est pas un dossier." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Hôte/Utilisateur/Profile-ID ne doit pas être vide." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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 FAT de la destination {path} ne supporte pas les " "liens physiques. Veuillez utiliser un système de fichier Linux natif." #: common/config.py:493 #, 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 {path} est un partage SMB. Merci de vérifier que " "le serveur SMB distant supporte les liens symboliques ou sinon activer " "{copyLinks} dans {expertOptions}." #: common/config.py:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Copier les liens (supprime les liens symboliques)" #: common/config.py:498 msgid "Expert Options" msgstr "Options avancées" #: common/config.py:502 #, 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 {path} est un partage sshfs. sshfs ne supporte pas" " les liens physiques. Merci d'utiliser le mode 'SSH' à la place." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Impossible de trouver crontab.\n" "Êtes-vous sûr(e) que cron est installé ?\n" "Si ce n'est pas le cas vous devriez désactiver les sauvegardes automatiques." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Échec d'écriture de la nouvelle crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Le programme udev ne fonctionne pas avec le mode {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Le profil \"{name}\" existe déjà." #: common/configfile.py:736 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 password" msgstr "Veuillez confirmer votre mot de passe" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Le mot de passe ne correspond pas." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Prendre un instantané" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Impossible de démonter {mountprocess} depuis {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} non trouvé. Veuillez installer par exemple {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Le point de montage {} n'est pas vide." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}' : Entrez le mot de passe pour {mode}  : " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "ÉCHEC" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Restaurer les permissions" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Terminé" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Reporter la sauvegarde lors du fonctionnement sur batterie" #: common/snapshots.py:770 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:772 #, 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:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Impossible d'effectuer l'instantané {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Finalisation" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Impossible de créer le dossier" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Sauvegarde du fichier de configuration…" #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Sauvegarde des permissions…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" "L'instantané non terminé {snapshot_id} a été trouvé et peut être poursuivi." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" "Suppression du répertoire {snapshot_id} restant de la dernière exécution" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Impossible de supprimer le répertoire" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Prise de l'instantané" #: common/snapshots.py:1254 msgid "Success" msgstr "Succès" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' s'est terminé avec le code retour {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Voir 'man rsync' pour plus de détails" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Un code retour négatif de rsync est un code signal, voir 'kill -l' et 'man " "kill'" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" "Rien n'a changé, il n'est pas nécessaire de faire une nouvelle sauvegarde" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Impossible de renommer {new_path} en {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Suppression intelligente" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Supprimer les anciens instantanés" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Essai de conservation d'un espace libre minimum" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Tentative de conservation d'un minimum de {perc} d'inodes libres" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Maintenant" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Impossible de monter {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent non trouvé. Veuillez SVP vous assurer qu'il est installé." #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Impossible de déverrouiller la clé privée SSH. Mot de passe incorrect ou mot" " de passe non disponible pour cron." #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Le chiffrage {cipher} a échoué pour {host}." #: common/sshtools.py:653 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:658 msgid "Remote path is not writable." msgstr "Le chemin distant n'est pas accessible en écriture." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Le chemin distant n'est pas éxécutable." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Impossible de créer le chemin distant." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "L'hôte distant {host} n'accepte pas {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Consultez 'man backintime' pour plus d'informations" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "Les commandes de vérifications sur l'hôte {host} ont retournées une erreur " "inconnue" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "L'hôte distant {host} ne supporte pas les liens physiques" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Veuillez fournir un mot de passe pour \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Raccourcis" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Ajouter aux Inclusions" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Ajouter aux Exclusions" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} n'est pas configurée. Voulez vous restaurer une ancienne " "configuration ?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Impossible de trouver le répertoire des instantanés.\n" "S'il se trouve sur un disque externe, veuillez le connecter puis cliquer sur OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Prendre un instantané" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" "Utiliser la modification de temps & taille afin de détecter les changements " "sur les fichiers." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Prendre un instantané avec somme de contrôle" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "" "Utiliser la somme de contrôle pour détecter les changements de fichiers." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Mettre en pause la prise de l'instantané" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Reprendre la prise de l'instantané" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Arrêter la prise de l'instantané" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Rafraîchir la liste des instantanés" #: qt/app.py:480 msgid "Name snapshot" msgstr "Nommer un instantané" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Supprimer l'instantané" #: qt/app.py:488 msgid "View snapshot log" msgstr "Voir le journal des instantanés" #: qt/app.py:492 msgid "View last log" msgstr "Voir la dernière entrée du journal" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Gérer les profils" #: qt/app.py:500 msgid "Shutdown" msgstr "Arrêt" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Éteindre le système après la fin de l'instantané." #: qt/app.py:504 #, fuzzy msgid "Setup language…" msgstr "Configurer la langue" #: qt/app.py:508 msgid "Exit" msgstr "Quitter" #: qt/app.py:512 msgid "Help" msgstr "Aide" #: qt/app.py:516 msgid "Profiles config file" msgstr "Fichier de configuration des profils" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Site Web" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Notes de version" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ" #: qt/app.py:528 msgid "Ask a question" msgstr "Poser une question" #: qt/app.py:531 msgid "Report a bug" msgstr "Signaler un bogue" #: qt/app.py:534 msgid "Translation" msgstr "Traduction" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "À propos" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Restaurer" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Restaurer vers …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Dossier parent" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Afficher les fichiers cachés" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Comparer des instantanés" #: qt/app.py:627 msgid "&Backup" msgstr "&Sauvegarde" #: qt/app.py:638 msgid "&Restore" msgstr "&Restaurer" #: qt/app.py:644 msgid "&Help" msgstr "&Aide" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Si vous fermez cette fenêtre, Back In Time ne sera pas en mesure d'arrêter votre système lorsque l'instantané sera terminé.\n" "Voulez-vous vraiment fermer ?" #: qt/app.py:905 msgid "Working:" msgstr "En cours :" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Terminé, il n'y a rien à sauvegarder" #: qt/app.py:962 msgid "Working" msgstr "En cours" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Erreur" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Envoyé" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Vitesse" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Temps restant estimé" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Répertoire racine" #: qt/app.py:1052 msgid "Home" msgstr "Répertoire personnel" #: qt/app.py:1067 msgid "Backup folders" msgstr "Répertoires sauvegardés" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nommer l'instantané" #: qt/app.py:1202 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:1294 #, fuzzy, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Création d'une copie de sauvegarde avec le suffixe '{suffix}' avant d'écraser ou\n" "supprimer les fichiers locaux." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "Les nouvelles versions des fichiers seront renommées avec le préfixe '{suffix}' avant la restauration.\n" "Si vous n'avez pas besoin de ces fichiers, vous pouvez les supprimer avec '{cmd}'" #: qt/app.py:1314 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:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "Supprimer les fichiers les plus récents dans le dossier d'origine" #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Restaurer les fichiers ou répertoires vers leur emplacement d'origine et\n" "supprimer les fichiers/répertoires qui ne font pas partie de la sauvegarde.\n" "Cela va supprimer les fichiers/répertoires qui étaient exclus lors de la sauvegarde !\n" "Soyez extrêmement prudent !!!" #: qt/app.py:1361 #, 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] "" "Voulez-vous vraiment restaurer ce(s) fichier(s)\n" "dans le nouveau répertoire {path}" msgstr[1] "" "Voulez-vous vraiment restaurer ce(s) fichier(s)\n" "dans le nouveau répertoire {path}" #: qt/app.py:1370 #, fuzzy 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(s) fichier(s)" msgstr[1] "Voulez-vous vraiment restaurer ce(s) fichier(s)" #: qt/app.py:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "ATTENTION : Supprimer des fichiers dans le système de fichiers racine " "pourrait casser tout votre système !" #: qt/app.py:1623 msgid "Snapshot" msgstr "Instantané" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Restaurer {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurer {path} vers…" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Auteurs" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Traductions" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licence" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Configurer la langue" #: qt/languagedialog.py:87 msgid "System default" msgstr "Par défaut" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Utiliser la langue du système." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Traduit à:{percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "plateforme de traduction" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Votre traduction" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Voir le dernier journal" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Voir le journal des instantanés" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Instantanés" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtre" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Tous" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Modifications" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Erreurs" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Information" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Erreur, [I] Information, [C] Modification" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "décoder les chemins" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Copier" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Décode" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Voulez-vous exclure ceci ?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Question" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Voir la dernière entrée du journal" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Démarrer {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "En cours…" #: qt/qttools.py:370 msgid "Today" msgstr "Aujourd'hui" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Hier" #: qt/qttools.py:386 msgid "This week" msgstr "Cette semaine" #: qt/qttools.py:393 msgid "Last week" msgstr "La semaine dernière" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Dernière vérification {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Afficher le journal complet" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Gérer les profils" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Modifier" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Ajouter" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Retirer" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Général" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Mode" #: qt/settingsdialog.py:152 #, 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')." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Dossier pour les sauvegardes" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Dossier" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Réglages SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Hôte" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Utilisateur" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Chemin" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Type de chiffrement" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Clé privée" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Mot de passe" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Enregistrer le Mot de passe dans le trousseau" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Avancées" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Chemin complet de l'instantané" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Planification" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Désactivée" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "À chaque démarrage/re-démarrage" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "Toutes les heures" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Toutes les {n} heures" msgstr[1] "Toutes les {n} heures" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Heures personnalisées" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Quotidiennement" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "À plusieurs reprises (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Quand le disque est connecté (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Chaque semaine" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Chaque mois" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Chaque année" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Jour" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Jour de la semaine" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Heure" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Heures" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Tous les" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Heure(s)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Jour(s)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Semaine(s)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Mois" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Inclure" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Inclure les fichiers et répertoires" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Ajouter un fichier" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Ajouter un répertoire" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Exclure" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Les caractères génériques ({example1}) seront ignorés en mode 'SSH chiffré'.\n" "Seuls les astérisques simples ou doubles sont autorisés ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Exclure les motifs, fichiers ou répertoires" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Fortement recommandé" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Ajouter par défaut" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Exclure les fichiers plus gros que : " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Exclure les fichiers plus gros que la valeur en %(prefix)s.\n" "Avec 'Full rsync mode' désactivé, seuls les nouveaux fichiers seront affectés\n" "car du point de vue de rsync c'est une option de transfert et non d'exclusion.\n" "Les gros fichiers transférés précédemment resteront donc dans l'instantané\n" "même s'ils ont été modifiés." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "Suppression &automatique" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Si antérieur à" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Année(s)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Si l'espace libre est inférieur à" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Si les inodes libres sont inférieurs à" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Suppression intelligente" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Exécuter en tâche de fond sur l'hôte distant." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EXPÉRIMENTAL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Garder tous les instantanés depuis les derniers" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "jour(s)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Garder un instantané par jour depuis les derniers" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Garder un instantané par semaine depuis les dernières" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "semaine(s)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Garder un instantané par mois depuis les derniers" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "mois" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Garder un instantané par an tous les ans" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Ne pas effacer les instantanés avec des noms" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Options" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Activer les notifications" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Désactiver les sauvegardes automatiques en mode batterie" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Impossible d'obtenir l'état de l'alimentation du système" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Exécuter un seul instantané à la fois" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "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é.\n" "Ceci une option globale. Elle affectera donc tous les profils de cet utilisateur.\n" "Vous avez cependant besoin de l'activer pour tous les autres utilisateurs." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Sauvegarder les fichiers remplacés lors de la restauration" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuer en cas d'erreur (conserver les instantanés incomplets)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Utiliser la somme de contrôle pour détecter les changements" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Niveau de journalisation" #: qt/settingsdialog.py:805 msgid "None" msgstr "Aucun" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "O&ptions avancées" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Exécuter 'rsync' avec '{cmd}' :" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "comme une tâche cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "sur un serveur distant" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "lors d'un instantané manuel" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Veuillez installer 'nocache' pour activer cette option)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "sur la machine locale" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Rediriger stdout vers /dev/null dans les tâches cron." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Rediriger stderr vers /dev/null dans les tâches cron." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Limiter l'utilisation de bande passante par rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "Ko/sec" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Conserver les autorisations" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Conserver les attributs étendus (xattr)" #: qt/settingsdialog.py:991 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:1024 #, 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:1027 msgid "Paste additional options to rsync" msgstr "Passer des options supplémentaires à rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Préfixe à exécuter avant chaque commande sur l'hôte distant.\n" "Les variables doivent être échappées avec \\$FOO.\n" "Cela n'affecte pas rsync. Pour ajouter un préfixe\n" "à rsync veuillez utiliser \"%(cbRsyncOptions)s\" avec\n" "%(rsync_options_value)s\n" "\n" "%(default)s : %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "par défaut" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Ajouter un préfixe aux commandes SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Vérifier si l'hôte distant est en ligne" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Attention : si désactivé et si l'hôte distant\n" "n'est pas disponible, cela pourrait conduire à\n" "d'étranges erreurs." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Vérifier si l'hôte distant supporte toutes les commandes nécessaires" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Attention : si désactivé et si l'hôte distant\n" "ne supporte pas toutes les commandes nécessaires,\n" "cela pourrait conduire à des erreurs étranges." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Restaurer la configuration" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Éditer le script de rappel (callback)" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nouveau profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Renommer le profil" #: qt/settingsdialog.py:1142 #, 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:1416 #, 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 "" "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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Vous n'avez pas sélectionné de clé privée pour SSH.\n" "Voulez-vous générer une nouvelle paire de clé publique/privée sans mot de passe ?" #: qt/settingsdialog.py:1469 #, 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:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Voulez-vous copier votre clé publique SSH sur\n" "l'hôte distant pour activer l'identification sans mot de passe ?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "L'authenticité de l'hôte {host} ne peut pas être confirmée.\n" "\n" "L'empreinte de la clé {keytype} est :" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" "Merci de vérifier cette empreinte ! Voulez-vous l'ajouter à votre fichier " "'known_hosts' ?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Exclure selon un modèle" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Exclure un fichier" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Exclure un répertoire" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Inclure le fichier" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Inclure un répertoire" #: qt/settingsdialog.py:1930 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:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Erreur lors de la création de la nouvelle clé SSH dans {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Chemin complet de l'instantané : " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "activé" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "désactivé" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Restaurer les préférences" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Merci de naviguer dans l'instantané à partir duquel vous voulez restaurer la configuration de {appName}. Le chemin doit ressembler à :\n" "{samplePath}\n" "\n" "Si vos instantanés sont sur un disque distant ou sont chiffrés, vous devez d'abord les monter manuellement. Si vous utilisez le mode SSH, vous devez peut-être aussi configurer votre clé publique sur le serveur distant.\n" "Consultez la documentation disponible via 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Aucune configuration trouvée" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "Le script de rappel (callback) n'a pas de shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Le shebang du script de rappel n'est pas exécutable." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Arguments concernant 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Utiliser %1 et %2 pour passer les chemins en paramètre" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Instantanés différents seulement" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Lister uniquement les instantanés égaux à : " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Vérification avancée (plus sûre mais lente)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Supprimer" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Tout sélectionner" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Comparer" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Atteindre" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Arguments" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Vous ne pouvez pas comparer un instantané à lui même." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Commande non trouvée" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Ceci ne peut être annulé !" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ATTENTION" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Exclure {path} des futurs instantanés ?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " et ajouter votre utilisateur au groupe 'fuse'" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "\"%s\" 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 ?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Ce journal a été décodé avec le modèle de recherche automatique\n" #~ "### Si certains chemins ne sont pas décodés vous pouvez les décoder manuellement avec :\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s n'est pas membre du groupe 'fuse'.\n" #~ " Lancer 'sudo adduser %(user)s fuse'. Pour appliquer les modifications déconnectez-vous et reconnectez-vous.\n" #~ "Consultez 'man backintime' pour de plus amples instructions." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s non trouvé dans ssh_known_hosts." #~ msgid "&Snapshot" #~ msgstr "In&stantané" #~ msgid "&View" #~ msgstr "&Vue" #~ msgid "..." #~ msgstr "…" #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Changements et Erreurs" #~ msgid "Config File Help" #~ msgstr "Aide du fichier de configuration" #~ msgid "Create a new SSH key without Password." #~ msgstr "Créer une nouvelle clé SSH sans mot de passe." #~ msgid "Diff" #~ msgstr "Comparer" #~ msgid "Diff Options" #~ msgstr "Options de comparaison" #~ msgid "Error:" #~ msgstr "Erreur :" #~ msgid "Every 10 minutes" #~ msgstr "Toutes les 10 minutes" #~ msgid "Every 12 hours" #~ msgstr "Toutes les 12 heures" #~ msgid "Every 30 minutes" #~ msgstr "Toutes les 30 minutes" #~ msgid "Every 4 hours" #~ msgstr "Toutes les 4 heures" #~ msgid "Every 5 minutes" #~ msgstr "Toutes les 5 minutes" #~ msgid "Every 6 hours" #~ msgstr "Toutes les 6 heures" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "La sauvegarde complète du système peut uniquement créer un instantané pouvant être restauré sur le(s) même(s) disque(s) physique(s) et avec le même partitionnement que la source ; restaurer vers de nouveaux disques ou les mêmes disques avec un partitionnement différent conduira potentiellement à un système cassé et inutilisable.\n" #~ "\n" #~ "La sauvegarde complète du système écrasera certains paramètres qui avaient pu être modifiés. Continuer ?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Une collision de hash s'est produite dans hash_id %s. Incrémentez la valeur " #~ "globale hash_collision et réessayez." #~ msgid "Key File" #~ msgstr "Fichier clé" #~ msgid "List only different snapshots" #~ msgstr "Lister uniquement les instantanés différents" #~ msgid "Local encrypted" #~ msgstr "Chiffré localement" #~ msgid "Modify for Full System Backup" #~ msgstr "Modifier pour le système de sauvegarde complète" #~ msgid "Mountprocess lock timeout" #~ msgstr "Délai de montage dépassé" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "L'authentification sans mot de passe pour %(user)s@%(host)s à échoué. " #~ "Consultez 'man backintime' pour de plus amples instructions." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "Le ping de %s a échoué. Hôte en panne ou adresse incorrecte." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil : \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Restaurer '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Restaurer '%s' vers…" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Restaurer les fichiers ou dossiers sélectionnés.\n" #~ "Si ce bouton est grisé, c'est probablement parce que \"{now}\" est sélectionné dans la liste des sauvegardes à gauche." #~ msgid "Run 'ionice':" #~ msgstr "Exécuter 'ionice'" #~ msgid "Run 'nice':" #~ msgstr "Exécuter 'nice'" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Exécuter 'rsync' avec l'option 'nocache':" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Exécuter 'rsync' avec l'option 'nocache':" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Préférences" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Instantané : %s" #~ msgid "View the current disk contents" #~ msgstr "Voir le contenu actuel du disque" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Voir le contenu de l'instantané fait à {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "AVEC DES ERREURS !" #~ msgid "Working..." #~ msgstr "Traitement en cours…" #~ msgid "You can't include backup folder!" #~ msgstr "Vous ne pouvez pas inclure le dossier des sauvegardes !" #~ msgid "You can't include backup sub-folder!" #~ msgstr "" #~ "Vous ne pouvez pas inclure un sous-dossier du dossier des sauvegardes !" #~ msgid "You can't remove the last profile!" #~ msgstr "Vous ne pouvez pas supprimer le dernier profil !" backintime-1.4.3/common/po/gl.po000066400000000000000000001501711455673541400165200ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Advertencia" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Local" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Chave privada SSH" #: common/config.py:304 msgid "encrypted" msgstr "cifrado" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Cifrado" #: common/config.py:310 msgid "SSH encrypted" msgstr "Cifrado SSH" #: common/config.py:317 msgid "Default" msgstr "Predeterminado" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: «{name}»" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "O cartafol das instantáneas non é válido!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Debe escoller polo menos un cartafol para copiar!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Оpción non válido. {path} non é un cartafol." #: common/config.py:457 #, fuzzy msgid "Host/User/Profile-ID must not be empty." msgstr "O servidor/usuario/ID do perfil non poden estar baleiros." #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Non se pode escribir en: {path}\n" "Ten certeza de ter permiso de escritura?" #: common/config.py:484 #, 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 Linux." #: common/config.py:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Copiar as ligazóns (seguir as ligazóns simbólicas aos ficheiros)" #: common/config.py:498 msgid "Expert Options" msgstr "Opcións avanzadas" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Non se atopa «crontab».\n" "Ten certeza de que «cron» está instalado?\n" "Se non o está debería desactivar todas as copias automáticas." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Produciuse un fallo escribindo no novo crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "O programador udev non funciona en modo {mode}" #: common/config.py:1733 #, fuzzy, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "O perfil «{name}» xa existe." #: common/configfile.py:736 #, fuzzy msgid "The last profile cannot be removed." msgstr "Isto non se pode revogar." #: 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 "Desexa crear un novo cartafol cifrado?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Cancelar" #: common/encfstools.py:156 msgid "Please confirm password" msgstr "Confirme o contrasinal" #: common/encfstools.py:160 #, fuzzy msgid "Password doesn't match." msgstr "O contrasinal non coincide." #: common/encfstools.py:178 #, fuzzy 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Facer copia" #: common/mount.py:608 #, fuzzy, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Non é posíbel desmontar {mountprocess} de {mountpoint}." #: common/mount.py:695 #, fuzzy msgid "{} not found. Please install e.g. {}" msgstr "Non foi posíbel atopar %(proc)s. Instalar p.e. %(install_command)s" #: common/mount.py:716 #, fuzzy msgid "Mountpoint {} not empty." msgstr "o punto de montaxe %s non está libre." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Perfil «{profile}»: Introduza o contrasinal de {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "FALLOU" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Restabelecer permisos" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Feito" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Atraso da copia no modo batería" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Agardando %s segundo." msgstr[1] "Agardando %s segundos." #: common/snapshots.py:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Produciuse un fallo ao realizar a copia {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Rematando" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Non é posíbel crear o cartafol" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "gardando o ficheiro de configuración..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "gardando os permisos..." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Atopáronse restos en {snapshot_id} desde os cales se pode continuar." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Eliminando o cartafol {snapshot_id} con restos da última execución" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Non é posíbel eliminar o cartafol" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Facer copia" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Non se precisa unha instantánea nova porque non houbo cambios" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Non é posible renomear {new_path} a {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Eliminación intelixente" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Eliminando instantáneas antigas" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Tentar preservar o espazo libre mínimo" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Tentando manter un mínimo de {perc} inodos libres" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Agora" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Non é posible montar {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "Non foi posible" #: common/sshtools.py:506 #, fuzzy, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Fallou o cifrado {cipher} para {host}." #: common/sshtools.py:653 #, fuzzy msgid "Remote path exists but is not a directory." msgstr "A ruta remota existe pero non é un cartafol." #: common/sshtools.py:658 #, fuzzy msgid "Remote path is not writable." msgstr "A ruta remota non permite a escritura." #: common/sshtools.py:663 #, fuzzy msgid "Remote path is not executable." msgstr "A ruta remota non é executable." #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Non foi posible crear a ruta remota." #: common/sshtools.py:948 #, fuzzy, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "O servidor remoto {host} non permite utilizar {command}" #: common/sshtools.py:952 common/sshtools.py:961 #, fuzzy msgid "Look at 'man backintime' for further instructions" msgstr "Mire 'man backintime' para máis instrucións" #: common/sshtools.py:956 #, fuzzy, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "As ordes de comprobación no servidor {host} devolveron un erro descoñecido" #: common/sshtools.py:977 #, fuzzy, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "O servidor remoto {host} non admite hardlinks" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Introduza o contrasinal de «{user}»" #: qt/app.py:167 msgid "Shortcuts" msgstr "Atallos" #: qt/app.py:187 #, fuzzy msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Este cartafol non existe\n" "na actual imaxe seleccionada." #: qt/app.py:252 msgid "Add to Include" msgstr "Engadir a incluír" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Engadir a excluír" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} non está configurado. Desexa restaurar unha configuración " "anterior?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Non é posíbel atopar o cartafol de instantáneas.\n" "Se está nun dispositivo extraíbel débeo conectar e premer «Aceptar»." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "Facer copia" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 #, fuzzy msgid "Take a snapshot (checksum mode)" msgstr "Facer unha copia con sumas de comprobación (checksums)" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "Usar a suma de comprobación para detectar cambios." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Deter o proceso de copia" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Continuar o proceso de copia" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Parar o proceso de copia" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Actualizar lista de instantáneas" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Facer copia" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Eliminar instantánea" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Ver o rexistro de instantáneas" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Ver o último rexistro" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Perfil principal" #: qt/app.py:500 msgid "Shutdown" msgstr "Apagar" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Apagar o sistema ao rematar a instantánea." #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Saír" #: qt/app.py:512 msgid "Help" msgstr "Axuda" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "gardando o ficheiro de configuración" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Sitio web" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Rexistro de cambios" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ" #: qt/app.py:528 msgid "Ask a question" msgstr "Fai unha pregunta" #: qt/app.py:531 msgid "Report a bug" msgstr "Informar dun erro" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "Traducións" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Sobre" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Restaurar" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" "Restabelecer os ficheiros ou cartafoles seleccionados no seu destino " "orixinal." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Restablecer como …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" "Restabelecer os ficheiros ou cartafoles seleccionados nun novo destino." #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Restabelecer o cartafol actual e todo o seu contido ao destino orixinal." #: qt/app.py:557 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:560 msgid "Up" msgstr "Arriba" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Mostrar os ficheiros ocultos" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Facer copia" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "&Restaurar" #: qt/app.py:644 msgid "&Help" msgstr "A&xuda" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Se pecha esta xanela Back In Time non poderá apagar o seu sistema cando remate a instantánea.\n" "Confirma que desexa pechala?" #: qt/app.py:905 msgid "Working:" msgstr "En proceso:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Rematado, non foi necesario facer unha copia de seguranza" #: qt/app.py:962 msgid "Working" msgstr "En proceso" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Erro" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Enviada" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Velocidade" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Tempo" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Inicio" #: qt/app.py:1067 msgid "Backup folders" msgstr "Cartafoles para copiar" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nome da instantánea" #: qt/app.py:1202 #, fuzzy 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 a instantánea" msgstr[1] "Confirma que quere eliminar a instantánea" #: qt/app.py:1294 #, fuzzy, 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 ficheiros locais." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "As versións máis novas dos ficheiros renomearanse cun {suffix} antes da restauración.\n" "Se xa non os precisa pode eliminalos con {cmd}" #: qt/app.py:1314 #, fuzzy 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" "Emprégase a opción «rsync --update»." #: qt/app.py:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "Eliminar os ficheiros máis novos do cartafol orixe" #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Restabelecer os ficheiros ou cartafoles seleccionados ao destino orixinal e\n" "eliminar os ficheiros/cartafoles que no están na instantánea.\n" "Isto eliminará os ficheiros/cartafoles excluídos ao tirar a instantánea.\n" "Teña moito coidado!!!" #: qt/app.py:1361 #, 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] "" "Confirma que quere restaurar o(s) ficheiro(s)\n" "no novo cartafol {path}" msgstr[1] "" "Confirma que quere restaurar o(s) ficheiro(s)\n" "no novo cartafol {path}" #: qt/app.py:1370 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Confirma o restabelecemento deste(s) ficheiro(s)" msgstr[1] "Confirma o restabelecemento deste(s) ficheiro(s)" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Confirma que desexa eliminar os ficheiros máis novos de {path}?" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "Seguro que desexa eliminar todos os ficheiros novos do cartafol orixinal?" #: qt/app.py:1393 #, fuzzy msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "AVISO: a eliminación de ficheiros no sistema raíz pode estragar todo o " "sistema!!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Instantánea" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Restaurar {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Restablecer {path} en…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Autores" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Traducións" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licenza" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 #, fuzzy msgid "System default" msgstr "predeterminado" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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" "Levas uasado Back In Time en {language} xa varias veces.\n" "A tradución ao {language} da versión de Back In Time que tes instalada está completa só a {perc}. Independentemente dos teus coñecementos técnicos, podes colaborar na tradución e, polo tanto, co propio Back In Time.\n" "Se queres contribuír, visita {translation_platform_url}. Para obteres máis axuda ou resposta ás posíbeis preguntas, visita {back_in_time_project_website}.\n" "Descúlpanos a interrupción, esta mensaxe non se volverá a amosar. Podes abrir de novo este diálogo en calquera momento no menú de axuda.\n" "O equipo de Back In Time" #: qt/languagedialog.py:216 #, fuzzy msgid "translation platform" msgstr "Traducións" #: qt/languagedialog.py:232 #, fuzzy msgid "Your translation" msgstr "Traducións" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Última visualización do rexistro" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Visualización do rexistro da imaxe" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Perfil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Instantáneas" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtro" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Todo" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Cambios" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Erros" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Informacións" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Erro, [I] Información, [C] Cambio" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "decodificar rutas" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Copiar" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Descodificar" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Desexa excluír isto?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Pregunta" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Ver o último rexistro" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Iniciar {appname}" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "En proceso…" #: qt/qttools.py:370 msgid "Today" msgstr "Hoxe" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Onte" #: qt/qttools.py:386 msgid "This week" msgstr "Esta semana" #: qt/qttools.py:393 msgid "Last week" msgstr "A semana pasada" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Esta NON é unha copia senón unha vista actual dos ficheiros locais" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "Última comprobación {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Mostrar o rexistro completo" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Perfil principal" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Editar" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Engadir" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Eliminar" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Xeral" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Modo" #: qt/settingsdialog.py:152 #, 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." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Onde gardar as instantáneas" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Cartafol" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Axustes SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Servidor" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Porto" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Usuario" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Ruta" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Cifrador" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Chave privada" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" "Escoller un ficheiro de chave privada existente (normalmente de nome " "«id_rsas»)" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" "Crear unha chave SSH sen contrasinal (non permitido se xa se seleccionou un " "ficheiro de chave privada)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Contrasinal" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Gardar o contrasinal no chaveiro" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Caché do contrasinal para Cron (incidencia de seguridade: root pode ler o " "contrasinal)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Avanzado" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Ruta completa á instantánea" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Programar" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Desactivado" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "En todos os arranques" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Cada {n} minutos" msgstr[1] "Cada {n} minutos" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Cada hora" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Cada {n} horas" msgstr[1] "Cada {n} horas" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Horas personalizadas" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Todos os días" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Repetidamente (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Cando a unidade se conecte (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Todas as semanas" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Todos os meses" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Cada Ano" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Día" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Día da semana laboral" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Hora" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Horas" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Cada" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Hora(s)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "día(s)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "semana(s)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Mes(es)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Incluír" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Incluír os ficheiros e os cartafoles" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Engadir ficheiro" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Engadir cartafol" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Excluír" #: qt/settingsdialog.py:528 #, fuzzy, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Os comodíns ({example1}) ignoraranse no modo «SSH cifrado».\n" "Unicamente se permiten asteriscos ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Excluír os patróns, ficheiros ou cartafoles" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Altamente recomendábel" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Engadir predeterminado" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Excluír ficheiros maiores de: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Excluír os ficheiros maiores dun valor en %(prefix)s.\n" "Co «Modo resync completo» desactivado isto só afectará os novos ficheiros\n" "porque para rsync é unha opción de transferencia, non de exclusión.\n" "Por iso, os ficheiros grandes que se copiaron antes permanecerán nas copias,\n" "mesmo se tiveron cambios." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "Eliminar &automaticamente" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Máis de" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "ano(s)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Se o espazo libre é menor de" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Se os inodos libres son menos de" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Eliminación intelixente" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "Executar en segundo plano no servidor remoto." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Conservar todas as copias dos últimos" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "día(s)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Conservar unha copia diaria dos últimos" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Conservar unha copia semanal das últimas" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "semana(s)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Conservar unha copia mensual dos últimos" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "mes(es)" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Conservar unha instantánea anual" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Non eliminar as instantáneas con nome" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Opcións" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Activar as notificacións" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Desactivar as instantáneas cando estea coa batería" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "O estado da alimentación non está dispoñíbel no sistema" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Executar só unha copia á vez" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "As demais instantáenas bloquearanse até que remate a actual.\n" "Esta é unha opción global. Afectará a todos os perfís deste usuario.\n" "Pero tamén precisa activalo para o resto de usuarios." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Facer copia dos ficheiros substituídos na restauración" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuar con erros (mantér instantáneas incompletas)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Usar a suma de comprobación para detectar cambios" #: qt/settingsdialog.py:793 #, fuzzy msgid "Take a new snapshot whether there were changes or not." msgstr "Crear unha nova instantánea aínda que non houbese cambios." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "Nivel de rexistro" #: qt/settingsdialog.py:805 msgid "None" msgstr "Ningún" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "O&pcións avanzadas" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Cambie estas opcións unicamente se sabe ben o que está a facer." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Executar «rsync» con «{cmd}»:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "como un traballo de cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "nun servidor remoto" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "ao facer unha copia manual" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Instale «nocache» para activar esta opción)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "na máquina local" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redireccionar stdout a /dev/null en cronjobs." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redireccionar stderr a /dev/null en cronjobs." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Limitar o uso do largo de banda" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/seg" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Preservar permisos (ACL)" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Preservar os atributos estendidos (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Copiar as ligazóns inseguras (funciona unicamente con ligazóns absolutas)" #: qt/settingsdialog.py:1024 #, fuzzy, 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:1027 msgid "Paste additional options to rsync" msgstr "Engadir as opcións adicionais a rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Prefixo para executar antes de todas as ordes nun servidor remoto.\n" "As variábeis hainas que escapar con \\$FOO.\n" "Isto non toca rsync. Polo tanto, para engadir un prefixo\n" "para rsync empregue «%(cbRsyncOptions)s» con\n" "%(rsync_options_value)s\n" "\n" "%(default)s%(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "predeterminado" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Engadir prefixo ás ordes de SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Comprobar se o servidor remoto está en liña" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Advertencia: se está desactivado e o servidor remoto\n" "non estiver dispoñíbel, poderíanse producir\n" "erros raros." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Comprobar se o servidor remoto acepta todas as ordes" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Advertencia: se está desactivado e o servidor remoto\n" "non admite as ordes necesarias,\n" "poderíanse producir erros raros." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Restabelecer config" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Perfil novo" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Renomear o perfil" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Está certo de eliminar o perfil \"{name}\" ?" #: qt/settingsdialog.py:1416 #, 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 "" "As horas personalizadas só poden ser unha lista de horas separadas por comas" " (p.e: 8,12,18,23) ou */3 para copias periódicas cada 3 horas" #: qt/settingsdialog.py:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Non escolleu un ficheiro coa chave privada para o SSH.\n" "Gustaríalle xerar un par de chaves públicas/privadas sen contrasinal?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Non existe o ficheiro coa chave privada «{file}»." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Desexa copiar a súa chave SSH ao servidor remoto\n" "para activar o acceso sen contrasinal?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Non é posíbel estabelecer a autenticidade do servidor {host}.\n" "\n" "A pegada dixital da chave {keytype} é:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" "Comprobe esta pegada dixital! Desesa engadila ao seu ficheiro «known_hosts»?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Excluír o patrón" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Excluír o ficheiro" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Excluír o cartafol" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Incluír o ficheiro" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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. O destino ligado non se copiará até que o inclúa, tamén.\n" "Desexa incluír no seu lugar o destino das ligazóns simbólicas?" #: qt/settingsdialog.py:1893 msgid "Include folder" msgstr "Incluír o cartafol" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Confirma que desexa cambiar o cartafol de instantáneas?" #: qt/settingsdialog.py:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Ruta completa á copia: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "activado" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "desactivado" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Restabelecer axustes" #: qt/settingsdialog.py:2125 #, fuzzy, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Vaia á instantánea da que desexa restaurar a configuración de {appName}. A ruta será como esta: \n" "{samplePath}\n" "\n" "Se as instantáneas están nunha unidade remota ou están cifradas debe montalas primeiro. Se usa o modo SSH pode necesitar configurar o acceso coa chave pública ao servidor remoto.\n" "Consulte o manual do backintime." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Non se atopou config" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Orde" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Parámetros" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Usar %1 e %2 como parámetros da ruta" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Só instantáneas con diferenzas" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Listar só as instantáneas iguais a: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Comprobación exhaustiva (máis precisa pero máis lenta)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Eliminar" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Selecionar todo" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Ir a" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "&Opcións" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Non pode comparar unha instantánea con ela mesma." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Non se atopou a orde" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Confirma que desexa eliminar «{file}» na instantánea «{snapshot_id}»?" #: qt/snapshotsdialog.py:375 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Confirma que desexa eliminar «{file}» de {count} instantáneas?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Isto non se pode revogar!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ADVERTENCIA" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Excluír «{path}» de instantáneas futuras?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " e engadir o seu usuario ao grupo «fuse»" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "«%s» é unha ligazón simbólica. O destino ligado non se copiará até que o inclúa.\n" #~ "Desexa incluír tamén o destino das ligazóns simbólicas?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Este rexistro decodificouse cun patrón de busca automático\n" #~ "### Se algunhas rutas non están decodificadas pode facelo manualmente con:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s non é membro do grupo 'fuse'.\n" #~ " Execute 'sudo adduser %(user)s fuse'. Para aplicar os trocos reinicie a sesión.\n" #~ "Vexa 'man backintime' para máis instrucións." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s non se encotrou en ssh_known_hosts." #~ msgid "&Snapshot" #~ msgstr "In&stantánea" #~ msgid "&View" #~ msgstr "&Ver" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Cambios e erros" #~ msgid "Config File Help" #~ msgstr "Axuda para o ficheiro de configuración" #~ msgid "Create a new SSH key without Password." #~ msgstr "Crear unha nova chave SSH sen contrasinal." #~ msgid "Diff" #~ msgstr "Diferenzas" #~ msgid "Diff Options" #~ msgstr "Opcións de comparación" #~ msgid "Error:" #~ msgstr "Produciuse un erro" #~ msgid "Every 10 minutes" #~ msgstr "Cada 10 minutos" #~ msgid "Every 12 hours" #~ msgstr "Cada 12 horas" #~ msgid "Every 30 minutes" #~ msgstr "Cada 30 minutos" #~ msgid "Every 4 hours" #~ msgstr "Cada 4 horas" #~ msgid "Every 5 minutes" #~ msgstr "Cada 5 minutos" #~ msgid "Every 6 hours" #~ msgstr "Cada 6 horas" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Facer unha copia de todo o sistema creará unha instantánea para restabelecer no mesmo disco(s) físico co mesmo particionado que a orixe; a restauración nun novo disco físico ou no mesmo disco físico cun particionado distinto resultará nunha quebra potencial e nun sistema non usábel.\n" #~ "\n" #~ "A copia de seguranza do sistema completo sobrescribirá algúns axustes personalizados polo usuario. Continuamos?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Produciuse un conflito de hash no hash_id %s. Incremente o valor global " #~ "hash_collision e probe de novo." #~ msgid "Key File" #~ msgstr "Ficheiro de chaves" #~ msgid "List only different snapshots" #~ msgstr "Listar unicamente as copias de seguranza diferentes" #~ msgid "Local encrypted" #~ msgstr "Cifrado local" #~ msgid "Modify for Full System Backup" #~ msgstr "Modificar para copias de seguranza de todo o sistema" #~ msgid "Mountprocess lock timeout" #~ msgstr "Tempo de espera do bloqueo do proceso de montaxe" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "Fallou a autenticación de %(user)s@%(host)s. Vexa 'man backintime' para máis" #~ " instrucións." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "Fallou o ping %s. O servidor está caído ou o enderezo é incorrecto." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Perfil: «{name}»" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Restabelecer '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Restabelecer '%s' como ..." #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Restaurar o ficheiro ou cartafol seleccionado.\n" #~ "Se este botón está en gris probabelmente «{now}» está seleccionado na lista de copias da esquerda." #~ msgid "Run 'ionice':" #~ msgstr "Executar «ionice»:" #~ msgid "Run 'nice':" #~ msgstr "Executar «nice»:" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Executar «rsync» con «nocache»:" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Executar «rsync» con «nocache»:" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Axustes" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Copia de seguranza: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Ver o contido do disco actual" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Ver a instantánea feita en {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "HAI ERROS" #~ msgid "Working..." #~ msgstr "En proceso..." #~ msgid "You can't include backup folder!" #~ msgstr "Non pode incluír o cartafol da copia de seguranza!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Non pode incluír un sub-cartafol da copia de seguranza!" #~ msgid "You can't remove the last profile!" #~ msgstr "Non pode eliminar o último perfil!" backintime-1.4.3/common/po/he.po000066400000000000000000001411721455673541400165130ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "אזהרה" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "פרופיל ראשי" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "מקומי" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "מפתח SSH פרטי" #: common/config.py:304 #, fuzzy msgid "encrypted" msgstr "SSH מוצפן" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "הצפנה" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH מוצפן" #: common/config.py:317 msgid "Default" msgstr "ברירת מחדל" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "פרופיל: „{name}”" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "תיקיית תמונות מצב אינה תקנית!" #: common/config.py:361 #, fuzzy msgid "You must select at least one folder to back up!" msgstr "עליך לבחור לפחות תיקייה אחת לגיבוי!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} אינו תיקייה." #: common/config.py:457 #, fuzzy msgid "Host/User/Profile-ID must not be empty." msgstr "מארח/משתמש/מזהה פרופיל לא יכולים להישאר ריקים." #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "לא ניתן לכתוב אל: {path}\n" "האם אתה בטוח שיש ברשותך גישה לכתיבה ?" #: common/config.py:484 #, 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:493 #, fuzzy, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "העתקת קישורים (ביטול הפניה של קישורים סמליים)" #: common/config.py:498 msgid "Expert Options" msgstr "אפשרויות מתקדמות" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "לא ניתן למצוא crontab.\n" "האם cron מותקן?\n" "אם לא יש לבטל את כל הגיבויים האוטומטיים." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "כתיבת crontab חדש נכשלה." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "תזמון udev לא עובד עם המצב {mode}" #: common/config.py:1733 #, fuzzy, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "הפרופיל \"{name}\" כבר קיים." #: common/configfile.py:736 #, 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 password" msgstr "נא לאשר את הסיסמה" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "שמירת תמונת מצב" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "פרופיל ‚{profile}’: נא למלא סיסמה עבור {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "נכשל" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "שחזור הרשאות" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "בוצע" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "הגיבוי מושהה בעת שימוש בסוללה" #: common/snapshots.py:770 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "לא ניתן למצוא את תיקיית תמונות המצב.\n" "אם הוא נמצא בכונן נשלף צריך לחבר אותו." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "בהמתנה של שנייה אחת." msgstr[1] "בהמתנה של %s שניות." #: common/snapshots.py:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "כשל בשמירת תמונת מצב {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "בהליכי סיום" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "לא ניתן ליצור את התיקייה" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "קובץ ההגדרות נשמר…" #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "הרשאות נשמרות…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "נמצאו שאריות {snapshot_id} שאפשר להמשיך." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "תיקיית השאריות {snapshot_id} מהריצה האחרונה נמחקת" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "לא ניתן להסיר תיקייה" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "תמונת מצב נשמרת" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "דבר לא השתנה, לא צריך תמונת מצב חדשה" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "לא ניתן לשנות את השם {new_path} ל־{path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "הסרה חכמה" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "תמונות מצב ישנות נמחקות" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "מתבצע ניסיון לשמור על מקום פנוי מזערי" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "מתבצע ניסיון לשמור {perc} מה־inodes פנויים" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "עכשיו" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "אי אפשר לעגן את {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 #, fuzzy msgid "Remote path is not executable." msgstr "שורת הפיקוד - Shebang בסקריפט משוב משתמש אינה ניתנת להפעלה." #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "לא ניתן ליצור את התיקייה." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "העתקת מפתח ה־SSH הציבורי „{pubkey}” למארח המרוחק „{host}”" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "נא למלא סיסמה למשתמש „{user}”" #: qt/app.py:167 msgid "Shortcuts" msgstr "קיצורים" #: qt/app.py:187 #, fuzzy msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "התיקייה הזאת לא קיימת\n" "בתמונת המצב הנוכחיות." #: qt/app.py:252 msgid "Add to Include" msgstr "הוספה להכללה" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "הוספה להחרגה" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "{appName} לא מוגדר. לשחזר הגדרות קודמות?" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "לא ניתן למצוא את תיקיית תמונות המצב.\n" "אם היא נמצאת על כונן נשלף יש לחבר אותו וללחוץ על אישור." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "שמירת תמונת מצב" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 #, fuzzy msgid "Take a snapshot (checksum mode)" msgstr "שמירת תמונת מצב עם סכומי בדיקה" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "להשתמש בסכומי בדיקה לאיתור שינויים." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "השהיית שמירת תמונת מצב" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "המשך שמירת תמונת מצב" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "עצירת שמירת תמונת מצב" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "רענון רשימת תמונות המצב" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "שמירת תמונת מצב" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "הסרת תמונת מצב" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "הצגת יומן תמונות מצב" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "הצגת היומן האחרון" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "פרופיל ראשי" #: qt/app.py:500 msgid "Shutdown" msgstr "כיבוי" #: qt/app.py:502 #, fuzzy msgid "Shut down system after snapshot has finished." msgstr "לכבות את המערכת אחרי ששמירת תמונת המצב הסתיימה." #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "יציאה" #: qt/app.py:512 msgid "Help" msgstr "עזרה" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "קובץ ההגדרות נשמר" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "אתר" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "יומן שינויים" #: qt/app.py:525 msgid "FAQ" msgstr "שאלות ותשובות" #: qt/app.py:528 msgid "Ask a question" msgstr "פרסום שאלה" #: qt/app.py:531 msgid "Report a bug" msgstr "דיווח על תקלה" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "תרגומים" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "על אודות" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "שחזור" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "שחזור הקבצים או התיקיות הנבחרים ליעד המקורי." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "שחזור אל…" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "שחזור הקבצים או התיקיות הנבחרים ליעד חדש." #: qt/app.py:552 #, fuzzy msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "שחזור התיקייה שמופיעה ואת כל התוכן שלה למיקום המקורי." #: qt/app.py:557 #, fuzzy msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "שחזור התיקייה שמופיע ואת כל התוכן שלה ליעד חדש." #: qt/app.py:560 msgid "Up" msgstr "למעלה" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "הצגת קבצים נסתרים" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "שמירת תמונת מצב" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "&שחזור" #: qt/app.py:644 msgid "&Help" msgstr "ע&זרה" #: qt/app.py:761 #, fuzzy msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "סגירת החלון תגרום לכך של־Back In Time לא תהיה אפשרות לכבות את המערכת שלך עם סיום שמירת תמונת המצב.\n" "להמשיך בסגירה?" #: qt/app.py:905 msgid "Working:" msgstr "בעבודה:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "הסתיים, לא נדרש גיבוי" #: qt/app.py:962 msgid "Working" msgstr "בעבודה" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "שגיאה" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "נשלח" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "מהירות" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "זמן משוערך" #: qt/app.py:1050 msgid "Global" msgstr "כללי" #: qt/app.py:1051 msgid "Root" msgstr "תיקיית העל" #: qt/app.py:1052 msgid "Home" msgstr "תיקיית הבית" #: qt/app.py:1067 msgid "Backup folders" msgstr "תיקיות הגיבוי" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "שם תמונת המצב" #: qt/app.py:1202 #, fuzzy 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:1294 #, fuzzy, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "לגבות קבצים מקומיים בטרם דריסה\n" "או הסרה עם הסיומת {suffix}." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 #, 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:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "הסרת קבצים חדשים בתיקייה המקורית" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "" "לשחזר את הקבצים האלה לתיקייה\n" "החדשה {path}" msgstr[1] "" "לשחזר את הקבצים האלה לתיקייה\n" "החדשה {path}" #: qt/app.py:1370 #, fuzzy 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "להסיר את כל הקבצים החדשים יותר תחת {path}?" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "להסיר את כל הקבצים החדשים בתיקיית המקור שלך?" #: qt/app.py:1393 #, fuzzy msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "אזהרה: מחיקת קבצים בשורש מערכת הקבצים יכולה לשבש את כל המערכת שלך!!!" #: qt/app.py:1623 msgid "Snapshot" msgstr "תמונת מצב" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "שחזור {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "לשחזר את {path} אל…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "יוצרים" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "תרגומים" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "רישיון" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 #, fuzzy msgid "System default" msgstr "ברירת מחדל" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "פלטפורמת תרגום" #: qt/languagedialog.py:232 #, fuzzy msgid "Your translation" msgstr "תרגומים" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "תצוגת היומן האחרון" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "תצוגת יומן תמונות מצב" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "פרופיל" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "גיבויים" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "סינון" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "הכול" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "שינויים" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "שגיאות" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "מידע" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] שגיאה, [I] מידע, [C] שינוי" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "פענוח נתיבים" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "העתקה" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "פענוח" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "להחריג את זה?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "שאלה" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "הצגת היומן האחרון" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "הפעלת {appname}" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "בעבודה" #: qt/qttools.py:370 msgid "Today" msgstr "היום" #: qt/qttools.py:377 msgid "Yesterday" msgstr "אתמול" #: qt/qttools.py:386 msgid "This week" msgstr "השבוע" #: qt/qttools.py:393 msgid "Last week" msgstr "שבוע שעבר" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "זה לא גיבוי אלא מבט עכשוי על הקבצים המקומיים שלך" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "בדיקה אחרונה {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "הצגת היומן המלא" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "פרופיל ראשי" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "עריכה" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "הוספה" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "הסרה" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&כללי" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "מצב" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "איפה לשמור גיבויים" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "תיקייה" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "הגדרות SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "מארח" #: qt/settingsdialog.py:204 msgid "Port" msgstr "פתחה" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "משתמש" #: qt/settingsdialog.py:214 msgid "Path" msgstr "נתיב" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "צופן" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "מפתח פרטי" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "נא לבחור קובץ מפתח פרטי קיים (בדרך כלל נקרא „id_rsa”)" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "יצירת מפתח SSH חדש ללא סיסמה (אסור אם כבר נבחר מפתח פרטי)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "סיסמה" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "שמירת הסיסמה לצרור המפתחות" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "שמירת סיסמה ל־Cron במטמון (סיבות אבטחה: משתמש על - root יכול לקרוא סיסמה)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "הגדרות מתקדמות" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "נתיב מלא לתמונת מצב" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "תזמון" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "מושבת" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "בכל טעינה/הפעלה מחדש של מערכת" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "כל {n} דקות" msgstr[1] "כל {n} דקות" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "כל שעה" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "כל {n} שעות" msgstr[1] "כל {n} שעות" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "שעות מותאמות אישית" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "כל יום" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "במחזוריות (anacron)" #: qt/settingsdialog.py:375 #, fuzzy msgid "When drive gets connected (udev)" msgstr "כאשר כונן מתחבר (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "כל שבוע" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "כל חודש" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "כל שנה" #: qt/settingsdialog.py:383 msgid "Day" msgstr "יום" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "יום בשבוע" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "שעה" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "שעות" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "הרצת Back In Time באופן מחזורי. שימוש אם המחשב לא דולק קבוע." #: qt/settingsdialog.py:442 msgid "Every" msgstr "כל" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "שעה/ות" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "יום/ימים" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "שבוע/ות" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "חודש/ים" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "ל&כלול" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "לכלול קבצים ותיקיות" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "הוספת קובץ" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "הוספת תיקייה" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "לה&חריג" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "החרגת תבניות, קבצים או תיקיות" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "מאוד מומלץ" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "הוספת ברירת מחדל" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "החרגת קבצים שגדולים מאשר: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "ה&סרה אוטומטית" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "לפני" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "שנה/ים" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "אם המקום פנוי קטן מאשר" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "אם כמות ה־inodes הפנויה קטנה מאשר" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "הסרה חכמה" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "הרצה ברקע במארח המרוחק." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "נסיוני" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "לשמור את כל תמונות המצב האחרונות במשך" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "יום/ימים" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "לשמור תמונת מצב ביום למשך" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "לשמור תמונת מצב בשבוע למשך" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "שבועות" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "לשמור תמונת מצב בחודש למשך" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "חודשים" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "לשמור תמונת מצב אחת לשנה לכל השנים" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "אל תסיר לכידות בעלות שם" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&אפשרויות" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "אפשר הודעות" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "השבת גיבויים כאשר על בטרייה" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "מצב הכוח אינו זמין מהמערכת" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "להריץ שמירה של תמונת מצב אחת כל פעם" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "גיבוי הקבצים המוחלפים בשחזור" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "להמשיך כשיש שגיאות (לשמור על תמונות מצב חלקיות)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "להשתמש בסכומי בדיקה לאיתור שינויים" #: qt/settingsdialog.py:793 #, fuzzy msgid "Take a new snapshot whether there were changes or not." msgstr "לשמור תמונת מצב ללא תלות בקיומם של שינויים." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "רמת פירוט יומן" #: qt/settingsdialog.py:805 msgid "None" msgstr "ללא" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "אפשרויות &מומחים" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "עשו שינוים בהגדרות אלו רק אם אתם באמת יודעים מה אתם עושים." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "הרצת ‚rsync’ עם ‚{cmd}’:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "כמשימת cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "במארח המרוחק" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "בעת שמירת תמונת מצב ידנית" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(נא להתקין ‚nocache’ כדי להפעיל את האפשרות הזאת)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "במכונה המקומית" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "הפניית stdout ל־‎/dev/null במשימות ה־cron." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "הפניית stderr ל־‎/dev/null במשימות ה־cron." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "הגבלת ניצולת רוחב הפס של rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "ק״ב/שנייה" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "שימור הרשאות ובעלות" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "שימור מאפיינים מורחבים (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "העתקת קישורים בלתי מהימנים (עובד רק עם קישורים מוחלטים)" #: qt/settingsdialog.py:1024 #, fuzzy, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "חובה לשים אפשרויות במירכאות, למשל: ‎{example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "הדבקת אפשרויות נוספות ל־rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "ברירת מחדל" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "הוספת קידומת לפקודות SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "לבדוק אם המארח המרוחק מחובר" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 #, fuzzy msgid "Check if remote host supports all necessary commands" msgstr "לבדוק האם המארח המרוחק תומך בכל הפקודות הנחוצות" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "שחזור הגדרות" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "עריכת משוב משתמש" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "פרופיל חדש" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "שינוי שם לפרופיל" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "האם אתה בטוח שברצונך למחוק את הפרופיל \"{name}\" ?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "לא בחרת קובץ מפתח פרטי ל־SSH.\n" "לייצר צמד מפתחות ציבורי/פרטי ללא סיסמה?" #: qt/settingsdialog.py:1469 #, fuzzy, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "קובץ המפתח הפרטי „{file}” לא קיים." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "להעתיק את מפתח ה־SSH הציבורי שלך למארח\n" "המרוחק כדי לאפשר כניסה ללא סיסמה?" #: qt/settingsdialog.py:1649 #, fuzzy, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "לא ניתן לאמת את „{host}”.\n" "\n" "טביעת האצבע של המפתח {keytype} היא:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "נא לאמת את טביעת האצבע הזאת! להוסיף אותה לקובץ ה־‚known_hosts’ שלך?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "הכללת תבנית" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "הכללת קובץ" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "הכללת תיקיה" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "לכלול קובץ" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "לכלול תיקיה" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "האם לשנות את תיקיית הגיבויים?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "יצירת מפתח SSH חדש תחת {path} נכשלה" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "נתיב תמונת מצב מלא: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "מופעל" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "מושבת" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "שחזור הגדרות" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "לא נמצאו הגדרות" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "לסקריפט משוב משתמש אין שורת פיקוד - shebang‏ (‎#/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "שורת הפיקוד - Shebang בסקריפט משוב משתמש אינה ניתנת להפעלה." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "להשתמש ב־%1 וב־%2 כמשתני נתיב" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "תמונות מצב עם שינויים בלבד" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "להציג רק תמונות מצב זהות ל־: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "בדיקה עמוקה (מדויקת יותר, אך אטית)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "מחיקה" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "בחירה בהכול" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "מעבר אל" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "&אפשרויות" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "לא ניתן להשוות תמונת מצב לעצמה." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "הפקודה לא נמצאה" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "האם אתה באמת רוצה למחוק את \"{file}\" בגיבוי \"{snapshot_id}\"?" #: qt/snapshotsdialog.py:375 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "האם אתה באמת רוצה למחוק את \"{file}\" ב-{count} גיבויים?" #: qt/snapshotsdialog.py:380 #, fuzzy msgid "This cannot be revoked!" msgstr "אי אפשר לשלול את זה!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "אזהרה" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "האם להמנע מ-\"{path}\" בגיבויים עתידיים?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " ולהוסיף את המשתמש שלך לקבוצה ‚fuse’" #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s לא נמצא ברשימה ssh_known_hosts." #~ msgid "&Snapshot" #~ msgstr "&תמונת מצב" #~ msgid "&View" #~ msgstr "ת&צוגה" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "שינוים ושגיאות" #~ msgid "Config File Help" #~ msgstr "עזרה בקובץ הגדרות" #~ msgid "Diff" #~ msgstr "שינויים" #~ msgid "Diff Options" #~ msgstr "אפשרויות שינויים" #~ msgid "Error:" #~ msgstr "שגיאה:" #~ msgid "Every 10 minutes" #~ msgstr "כל 10 דקות" #~ msgid "Every 12 hours" #~ msgstr "כל 12 שעות" #~ msgid "Every 30 minutes" #~ msgstr "כל 30 דקות" #~ msgid "Every 4 hours" #~ msgstr "כל 4 שעות" #~ msgid "Every 5 minutes" #~ msgstr "כל 5 דקות" #~ msgid "Every 6 hours" #~ msgstr "כל 6 שעות" #~ msgid "Local encrypted" #~ msgstr "מוצפן מקומית" #~ msgid "Modify for Full System Backup" #~ msgstr "שינוי לגיבוי מלא של המערכת" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "פרופיל: „{name}”" #, python-format #~ msgid "Restore '%s'" #~ msgstr "לשחזר '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "לשחזר '%s' ל..." #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "שחזור הקובץ או התיקייה הנבחרים.\n" #~ "אם הכפתור הזה מושבת ומסומן באפור זה כנראה כיוון שבחרת ב„{now}” ברשימת תמונות המצב שמימין." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "הגדרות" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "גיבוי: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "צפיה בתוכן הנוכחי של הכונן" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "צפייה בתמונת מצב שנשמרה ב־{timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "עם שגיאות !" #~ msgid "Working..." #~ msgstr "פועל..." #~ msgid "You can't include backup folder!" #~ msgstr "לא ניתן לכלול את תיקיית הגיבוי!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "לא ניתן לכלול תת־תיקיות של תיקיית הגיבוי!" #~ msgid "You can't remove the last profile!" #~ msgstr "לא ניתן להסיר את הפרופיל האחרון!" backintime-1.4.3/common/po/hr.po000066400000000000000000001134131455673541400165250ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-27 04:41+0000\n" "Last-Translator: MarvinDarwin \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Upozorenje" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Glavni profil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "" #: common/config.py:304 msgid "encrypted" msgstr "" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "" #: common/config.py:310 msgid "SSH encrypted" msgstr "" #: common/config.py:317 msgid "Default" msgstr "" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 #, fuzzy msgid "Snapshots folder is not valid!" msgstr "Mapa snimki nije valjana !" #: common/config.py:361 #, fuzzy msgid "You must select at least one folder to back up!" msgstr "Morate odabrati barem jednu mapu za rezervu !" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} nije mapa." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "" #: common/config.py:467 common/config.py:514 #, fuzzy, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "" #: common/config.py:498 msgid "Expert Options" msgstr "Stručne opcije" #: common/config.py:502 #, 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:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "" #: common/config.py:1707 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" već postoji." #: common/configfile.py:736 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 password" msgstr "Molim Vas da potvrdite lozinku" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Uzmi snimku" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}': Unesite zaporku za {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "NEUSPJEŠNO" #: common/snapshots.py:539 common/snapshots.py:601 #, fuzzy msgid "Restore permissions" msgstr "Spremi dopuštenje" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Završeno" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:770 #, fuzzy 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:772 #, 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:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Neuspjelo uzimanje snimke {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Završavanje" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Nije moguće napraviti mapu" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Spremi datoteku postavki ..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Spremi dopuštenje ..." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Nije moguće ukloniti mapu" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Uzmi snimku" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Smart uklanjanje" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Ukloni stare snimke" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Pokušaj sačuvati min slobodno mjesta" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Pokušaj sačuvati min {perc} slobodno mjesta" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Sada" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Nije moguće napraviti mapu." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Molim Vas unesite lozinku za korisnika \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Prečaci" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "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:453 #, fuzzy msgid "Take a snapshot" msgstr "Uzmi snimku" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Osvježi popis snimki" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Uzmi snimku" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Ukloni Snimku" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Prikaži Zapis Snimke" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Prikaži Zadnji Zapis" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Glavni profil" #: qt/app.py:500 msgid "Shutdown" msgstr "" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Izlaz" #: qt/app.py:512 msgid "Help" msgstr "Pomoć" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Spremi datoteku postavki" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Web stranica" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "" #: qt/app.py:528 msgid "Ask a question" msgstr "" #: qt/app.py:531 msgid "Report a bug" msgstr "" #: qt/app.py:534 msgid "Translation" msgstr "" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "O programu" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Povrati" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Povrati" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "Gore" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Prikaži skrivene datoteke" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Uzmi snimku" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "&Povrati" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "Po&moć" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "Radim:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Završeno, nije potrebna rezerva" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Radim" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "Globalno" #: qt/app.py:1051 msgid "Root" msgstr "Korijen" #: qt/app.py:1052 msgid "Home" msgstr "Početak" #: qt/app.py:1067 msgid "Backup folders" msgstr "Mape rezerve" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Ime Snimke" #: qt/app.py:1202 #, fuzzy 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "Neuspjelo uzimanje snimke {snapshot_id} !!!" msgstr[1] "Neuspjelo uzimanje snimke {snapshot_id} !!!" msgstr[2] "Neuspjelo uzimanje snimke {snapshot_id} !!!" #: qt/app.py:1370 #, fuzzy 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Snimke" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Povrati {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Povrati {path}" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Snimke" #: qt/logviewdialog.py:94 #, fuzzy msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Sve" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Promjene" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Grješke" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Informacije" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Grješka, [I] Informacije, [C] Promjeni" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Prikaži Zadnji Zapis" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Radim" #: qt/qttools.py:370 msgid "Today" msgstr "Danas" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Jučer" #: qt/qttools.py:386 msgid "This week" msgstr "Ovaj tjedan" #: qt/qttools.py:393 msgid "Last week" msgstr "Prošli tjedan" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Glavni profil" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Uredi" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "&Općenito" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Gdje sačuvati snimke" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "Domaćin" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 #, fuzzy msgid "User" msgstr "Korisnik" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Napredno" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Raspored" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Onemogućeno" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Pri svakom paljenju/podizanju" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, 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:363 msgid "Every hour" msgstr "" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, 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:372 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Svaki dan" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Svaki tjedan" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Svaki mjesec" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Svake godine" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Dan(a)" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "Sat" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "Sat" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Svakih" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dan(a)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Tjedan(a)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Uključi" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Ukljući mape i datoteke" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Dodaj datoteku" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Dodaj mapu" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Isključi" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Iskljući sljedove, datoteke ili mape" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "Visoko preporučeno" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "&Auto-ukloni" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Starije od" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Godina" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Ako je slobodan prostor manji od" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Ako je slobodan prostor manji od" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Smart uklanjanje" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "Dan(a)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "Tjedan(a)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Nemoj uklanjati imenovane snimke" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "Op&cije" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Omogući obavijesti" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Onesposobi snimke tijekom rada na bateriji" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Stanje energije nije dostupno od sustava" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Nastavi ne grješkama (zadrži nepotpune snimke)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 #, fuzzy msgid "Log Level" msgstr "Razina Zapisa" #: qt/settingsdialog.py:805 msgid "None" msgstr "Nijedno" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "&Stručne opcije" #: qt/settingsdialog.py:830 #, 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Sačuvaj ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Novi profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Preimenuj profil" #: qt/settingsdialog.py:1142 #, fuzzy, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Isključi slijed" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Isključi datoteku" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Isključi mapu" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Uključi datoteku" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Uključi mapu" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Jeste li sigurni da želite promjeniti mapu snimki?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command" msgstr "Naredba" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters" msgstr "Parametri" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Dubinska provjera (preciznije, ali sporije)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Idi Na" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Opcije" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Nije moguće usporediti snimku s njom samom." #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "Naredba nije pronađena" #: qt/snapshotsdialog.py:369 #, 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:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Snimke" #~ msgid "..." #~ msgstr "..." #~ msgid "Changes & Errors" #~ msgstr "Promjene & Grješke" #~ msgid "Diff" #~ msgstr "Razlika" #~ msgid "Error:" #~ msgstr "Greška:" #~ msgid "Every 10 minutes" #~ msgstr "Svakih 10 minuta" #~ msgid "Every 5 minutes" #~ msgstr "Svakih 5 minuta" #~ msgid "List only different snapshots" #~ msgstr "Prikaži samo različite snimke" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #~ msgid "Settings" #~ msgstr "Postavke" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Snimka: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Prikaži trenutni sadržaj diska" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Prikaži snimku napravljenu {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "S GRJEŠKAMA !" #~ msgid "Working..." #~ msgstr "Obrađujem..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Nije moguće uključiti mapu rezerve !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Nije moguće uključiti pod-mapu rezerve !" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Nije moguće ukloniti zadnji profil!" backintime-1.4.3/common/po/hu.po000066400000000000000000001312561455673541400165350ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Fő profil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Helyi" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH privát kulcs" #: common/config.py:304 #, fuzzy msgid "encrypted" msgstr "SSH-val titkosított" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Titkosítás" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH-val titkosított" #: common/config.py:317 msgid "Default" msgstr "Alapértelmezett" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 #, fuzzy msgid "Snapshots folder is not valid!" msgstr "A pillanatkép-könyvtár nem érvényes!" #: common/config.py:361 #, fuzzy msgid "You must select at least one folder to back up!" msgstr "Legalább egy könyvtárat ki kell jelölnie a mentéshez!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "A(z) {path} nem könyvtár." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "" #: common/config.py:467 common/config.py:514 #, fuzzy, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nem tudok ide írni: {path}\n" "Biztos, hogy van írási jogod?" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Linkek másolása (szimbolikus linkeknél nem csak a hivatkozást)" #: common/config.py:498 msgid "Expert Options" msgstr "Szakértői beállítások" #: common/config.py:502 #, 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:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "A crontab nem található.\n" "Biztos, hogy telepítve van a cron?\n" "Ha nem, akkor ki kell kapcsolni az automatikus biztonsági mentést." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "" #: common/config.py:1707 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1722 #, fuzzy, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Az udev időzítése nem működik ezzel a móddal {mode}" #: common/config.py:1733 #, fuzzy, python-brace-format msgid "Couldn't find UUID for {path}" msgstr "Nem találom a \"{path}\" meghajtó UUID-ját" #: common/configfile.py:107 msgid "Failed to save config" msgstr "" #: common/configfile.py:143 msgid "Failed to load config" msgstr "" #: common/configfile.py:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "A \"{name}\" már létezik." #: common/configfile.py:736 #, fuzzy msgid "The last profile cannot be removed." msgstr "Nem lehet visszavonni." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Nem tudtam felcsatolni a következőt '{command}'" #: common/encfstools.py:139 #, fuzzy msgid "Config for encrypted folder not found." msgstr "A titkosított mappa konfigurációja nem található." #: common/encfstools.py:147 #, fuzzy msgid "Create a new encrypted folder?" msgstr "Létrehozzak egy új titkosított mappát?" #: common/encfstools.py:151 msgid "Cancel" msgstr "Mégsem" #: common/encfstools.py:156 msgid "Please confirm password" msgstr "Erősítsd meg a jelszót" #: common/encfstools.py:160 #, fuzzy msgid "Password doesn't match." msgstr "A jelszavakat nem egyeznek." #: common/encfstools.py:178 #, fuzzy msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" "Az encfs az 1.7.2-es és korábbi verzióknál hibás, ha a --reverse opciót " "használod. Frissítsd az encfs-t." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Pillanatkép készítése" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 #, fuzzy msgid "Mountpoint {} not empty." msgstr "A {} csatolási pont foglalt." #: common/password.py:240 #, fuzzy, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "A(z) '{profile}' profil: add meg a jelszót a következőhöz {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "MEGHIÚSULT" #: common/snapshots.py:539 common/snapshots.py:601 #, fuzzy msgid "Restore permissions" msgstr "Engedélyek visszaállítása" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Kész" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:770 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "A pillanatképek könyvtára nem található.\n" "Ha egy eltávolítható adathordozón van, csatlakoztasd." #: common/snapshots.py:772 #, 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:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "A {snapshot_id} pillanatkép készítése meghiúsult." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Véglegesítés" #: common/snapshots.py:949 #, fuzzy msgid "Can't create folder" msgstr "A(z) mappa nem hozható létre" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Konfigurációs fájl mentése..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Hozzáférési jogosultságok mentése..." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 #, fuzzy msgid "Can't remove folder" msgstr "A(z) mappa nem távolítható el" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Pillanatkép készítése" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1318 #, fuzzy, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Nem tudom a(z) {new_path} útvonalat átnevezni erre {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Okos eltávolítás" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Régi pillanatképek eltávolítása" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "A minimálisan megadott szabad hely kialakítása" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Megpróbálok legalább {perc} szabad inode-ot tartani" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Most" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Nem tudom felcsatolni: %s" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Nem tudtam feloldani az ssh privát kulcsát. Hibás jelszó vagy a cron nem éri" " el a jelszót." #: common/sshtools.py:506 #, fuzzy, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "A(z) {cipher} nem sikerült a {host} esetében." #: common/sshtools.py:653 #, fuzzy msgid "Remote path exists but is not a directory." msgstr "A távoli útvonal létezik, de nem könyvtár." #: common/sshtools.py:658 #, fuzzy msgid "Remote path is not writable." msgstr "A távoli útvonal nem írható." #: common/sshtools.py:663 #, fuzzy msgid "Remote path is not executable." msgstr "A távoli útvonal nem futtatható." #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Nem tudom távoli útvonalat elkészíteni." #: common/sshtools.py:948 #, fuzzy, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "A(z) {host} távoli kiszolgáló nem támogatja a következőt {command}" #: common/sshtools.py:952 common/sshtools.py:961 #, fuzzy msgid "Look at 'man backintime' for further instructions" msgstr "Nézd meg a 'man backintime'-ot további részletekért" #: common/sshtools.py:956 #, fuzzy, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" "A parancsok ellenőzése a(z) {host} kiszolgálón ismeretlen hibát okozott" #: common/sshtools.py:977 #, fuzzy, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "A(z) {host} kiszolgáló nem támogatja a hardlinkeket" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, fuzzy, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Erősítsd meg a jelszót" #: qt/app.py:167 msgid "Shortcuts" msgstr "Billentyűparancsok" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Nem található a pillanatképek könyvtára.\n" "Ha egy eltávolítható lemezen van, csatlakoztasd és nyomd meg az OK-t." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "Pillanatkép készítése" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "Checksum használata hibák felfedezéséhez." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Pillanatképek listájának frissítése" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Pillanatkép készítése" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Pillanatkép eltávolítása" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "A pillanatképek naplófájljának megtekintése" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Utolsó naplófájl megtekintése" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Fő profil" #: qt/app.py:500 msgid "Shutdown" msgstr "Leállítás" #: qt/app.py:502 #, fuzzy msgid "Shut down system after snapshot has finished." msgstr "Állítsd le a rendszert, ha elkészült a pillanatkép." #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Kilépés" #: qt/app.py:512 msgid "Help" msgstr "Súgó" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Konfigurációs fájl mentése" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Honlap" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "GYIK" #: qt/app.py:528 msgid "Ask a question" msgstr "Kérdezz" #: qt/app.py:531 msgid "Report a bug" msgstr "Hiba jelentése" #: qt/app.py:534 msgid "Translation" msgstr "" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Névjegy" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Visszaállítás" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Visszaállítás ide…" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "Fel" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Rejtett fájlok megjelenítése" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Pillanatkép készítése" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "&Visszaállítás" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "&Súgó" #: qt/app.py:761 #, fuzzy msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Ha bezárod ezt az ablakot, a Back In Time nem tudja lekapcsolni a rendszert a biztonsági mentés elvégzése után.\n" "Biztos bezárod?" #: qt/app.py:905 msgid "Working:" msgstr "Dolgozik:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Kész, nincs szükség biztonsági mentés készítésére" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Dolgozik" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "Globális" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Home" #: qt/app.py:1067 msgid "Backup folders" msgstr "Biztonsági mentés mappái" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Pillanatkép neve" #: qt/app.py:1202 #, fuzzy msgid "Are you sure you want to remove this snapshot?" msgid_plural "Are you sure you want to remove these snapshots?" msgstr[0] "Biztos, hogy el akar távolítani a következő pillanatképet" msgstr[1] "Biztos, hogy el akar távolítani a következő pillanatképet" #: qt/app.py:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "Biztos, hogy törlöd a \"{file}\"-t a {count}-pillanatképből?" msgstr[1] "Biztos, hogy törlöd a \"{file}\"-t a {count}-pillanatképből?" #: qt/app.py:1370 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Biztos, hogy törlöd a \"{file}\"-t a {count}-pillanatképből?" msgstr[1] "Biztos, hogy törlöd a \"{file}\"-t a {count}-pillanatképből?" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Pillanatképek" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Visszaállítás {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Visszaállítás {path} ide…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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özlöm!\n" "Ön már többször használta a Back In Time programot a(z) {language} nyelven.\n" "A Back In Time telepített verziójának {language} nyelvre történő fordítása {perc} befejeződött. 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" "Kérjük, látogasson el a {translation_platform_url} oldalra, ha ebben szeretne segíteni. A további segítségért és az esetleges kérdésekért kérjük, 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édpanel később is bármikor elérhető a súgó menüben.\n" "A Back In Time csapata" #: qt/languagedialog.py:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Pillanatképek" #: qt/logviewdialog.py:94 #, fuzzy msgid "Filter" msgstr "Szűrő" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Mindegyik" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Változások" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Hibák" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Információk" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] hiba, [I] információ, [C] változtat" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "dekódolás útvonalai" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Utolsó naplófájl megtekintése" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Dolgozik…" #: qt/qttools.py:370 msgid "Today" msgstr "Ma" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Tegnap" #: qt/qttools.py:386 msgid "This week" msgstr "Ezen a héten" #: qt/qttools.py:393 msgid "Last week" msgstr "Múlt héten" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Fő profil" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Szerkesztés" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "Á<alános" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 #, fuzzy msgid "Mode" msgstr "Mód" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Hova mentse a pillanatképeket" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Az SSH beállításai" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "Kiszolgáló" #: qt/settingsdialog.py:204 #, fuzzy msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 #, fuzzy msgid "User" msgstr "Felhasználó" #: qt/settingsdialog.py:214 #, fuzzy msgid "Path" msgstr "Elérési út" #: qt/settingsdialog.py:220 #, fuzzy msgid "Cipher" msgstr "Cipher" #: qt/settingsdialog.py:226 #, fuzzy msgid "Private Key" msgstr "Privát kulcs" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Jelszó" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Jelszó mentése a kulcstartóba" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "Jelszó mentése a Cron számára (biztonsági figyelmeztetés: a root látja a " "jelszót)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Szakértői" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Ütemterv" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Kikapcsolva" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Minden (újra)indításkor" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, 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:363 msgid "Every hour" msgstr "Óránként" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, 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:372 #, fuzzy msgid "Custom hours" msgstr "Egyedi beállítás (óra)" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Naponta" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:375 #, fuzzy msgid "When drive gets connected (udev)" msgstr "Ha csatlakozik a meghajtó (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Hetente" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Havonta" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Évente" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Nap" #: qt/settingsdialog.py:394 #, fuzzy msgid "Weekday" msgstr "Hét napja" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "Óra" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "Óra" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Minden" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Nap" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Hét" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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 "" "Futtassa a Back In Time-ot, amint csatolod a meghajtót (X naponta egyszer).\n" "Be fogja kérni a sudo jelszót." #: qt/settingsdialog.py:484 #, fuzzy msgid "&Include" msgstr "&Hozzáadás" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Felveendő fájlok és mappák" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Fájl hozzáadása" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Mappa hozzáadása" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Kihagyás" #: qt/settingsdialog.py:528 #, fuzzy, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "A helyettesítő karaktreket ({example1}) az 'SSH encrypted' mód nem veszi figyelembe.\n" "Csak az elválasztó csillag megengedett ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Kihagyandó minták, fájlok és mappák" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "Erősen ajánlott" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "&Automatikus eltávolítás" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Régebbi, mint" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Év" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Ha a szabad hely kevesebb, mint" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Ha a szabad inode-ok száma kevesebb, mint" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Okos eltávolítás" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 #, fuzzy msgid "EXPERIMENTAL" msgstr "KÍSÉRLETI" #: qt/settingsdialog.py:696 #, fuzzy msgid "Keep all snapshots for the last" msgstr "Tartsd meg a pillanatképeket a legutóbbi" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "napból" #: qt/settingsdialog.py:703 #, fuzzy msgid "Keep one snapshot per day for the last" msgstr "Tarts meg napi egy pillanatképet a legutóbbi" #: qt/settingsdialog.py:710 #, fuzzy msgid "Keep one snapshot per week for the last" msgstr "Tarts meg heti egy pillanatképet a legutóbbi" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "hétből" #: qt/settingsdialog.py:717 #, fuzzy msgid "Keep one snapshot per month for the last" msgstr "Tarts meg havi egy pillanatképet a legutóbbi" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "hónapból" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Tarts meg évi egy pillanatképet a legutóbbi" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Ne távolítsa el a névvel ellátott pillanatképeket" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "&Beállítások" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Értesítések engedélyezése" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Pillanatképek készítésének tiltása akkumulátorról működésnél" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "A rendszer nem adja meg az energiaellátás állapotát" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Folytatás hibák esetén (megtartja a nem teljes pillanatképeket)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Checksum használata hibák felfedezéséhez" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 #, fuzzy msgid "Log Level" msgstr "Naplózás szintje" #: qt/settingsdialog.py:805 msgid "None" msgstr "Egyik sem" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "S&zakértői beállítások" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "Ezeket a beállításokat csak akkor változtasd meg, ha biztosan tudod, mit " "csinálsz." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 #, fuzzy msgid "Limit rsync bandwidth usage" msgstr "Az rsync sávszélesség-használatának korlátja" #: qt/settingsdialog.py:918 #, fuzzy msgid "KB/sec" msgstr "KB/sec" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "ACL-ek megtartása" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Kiterjesztett jogosultságok (xattr) megtartása" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Nem biztonságos linkek másolása (csak abszolút linkekkel működik)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Új profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Profil átnevezése" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Biztosan törlöd a(z) \"{name}\" profilt?" #: qt/settingsdialog.py:1416 #, 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 "" "Az egyedi órák csak vesszővel elválasztott, listázott értékek lehetnek (pl. " "8,12,18,23), vagy */3, ha háromóránként akarsz mentést készíteni" #: qt/settingsdialog.py:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Kihagyási minta" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Kihagyandó fájl" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Kihagyandó mappa" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Felveendő fájl" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Felveendő mappa" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Biztos, hogy szeretnéd megváltoztatni a pillanatképek mappáját?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command" msgstr "Parancs" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters" msgstr "Paraméterek" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Használja ezeket útvonal-paraméterként: %1 és %2" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Csak a következőkkel megegyező pillanatképek listázása: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Alapos ellenőrzés (pontosabb, de lassú)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Töröl" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Minden kijelölése" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 #, fuzzy msgid "Go To" msgstr "Ugrás ide" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Beállítások" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Egy pillanatkép nem hasonlítható össze önmagával." #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "A következő parancs nem található" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Biztosan töröljem a \"{file}\" fájlt a \"{snapshot_id}\" pillanatképből?" #: qt/snapshotsdialog.py:375 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Biztos, hogy törlöd a \"{file}\"-t a {count}-pillanatképből?" #: qt/snapshotsdialog.py:380 #, fuzzy msgid "This cannot be revoked!" msgstr "Nem lehet visszavonni!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Kizárjam a \"{path}\"-t a jövőbeli pillanatképekből?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Ezt a naplófájlt automatikus keresési mintával dekódolták\n" #~ "### Ha néhány útvonal dekódolása elmaradt, a következővel tudod kézzel dekódolni:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "A(z) %(user)s nem tagja a 'fuse' csoportnak.\n" #~ " Futtasd le a 'sudo adduser %(user)s fuse' parancsot, majd jelentkezz ki és vissza.\n" #~ "Nézd meg a 'man backintime'-ot további részletekért." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "A(z) %s nem található itt: ssh_known_hosts." #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Pillanatképek" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Változások & Hibák" #~ msgid "Diff" #~ msgstr "Különbség" #~ msgid "Diff Options" #~ msgstr "Diff beállítások" #~ msgid "Error:" #~ msgstr "Hiba:" #~ msgid "Every 10 minutes" #~ msgstr "Tízpercenként" #~ msgid "Every 12 hours" #~ msgstr "Tizenkétóránként" #~ msgid "Every 30 minutes" #~ msgstr "30 percenként" #~ msgid "Every 4 hours" #~ msgstr "Négyóránként" #~ msgid "Every 5 minutes" #~ msgstr "Ötpercenként" #~ msgid "Every 6 hours" #~ msgstr "Hatóránként" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Hash-ütközés történt: hash_id %s. Megnövelem a hash_collision értékét, és " #~ "újra próbálom." #~ msgid "List only different snapshots" #~ msgstr "Csak az eltérő pillanatképek listázása" #~ msgid "Local encrypted" #~ msgstr "Helyi titkosított" #~ msgid "Mountprocess lock timeout" #~ msgstr "A csatolási folyamat túl sok időt vett igénybe" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "A %(user)s@%(host)s nem tudott jelszó nélkül bejelentkeztni. Nézd meg a 'man" #~ " backintime'-ot további részletekért." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "" #~ "A(z) %s ping sikertelen volt. A kiszolgáló nem működik vagy rossz a cím." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "A(z) '%s' visszaállítása" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "A(z) '%s' visszaállítása ide..." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Beállítások" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Pillanatkép: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "A lemez jelenlegi tartalmának megtekintése" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "A {timestamp} időpontban észült pillanatkép megtekintése" #~ msgid "WITH ERRORS !" #~ msgstr "HIBÁKKAL!" #~ msgid "Working..." #~ msgstr "Dolgozom..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Nem adható hozzá a biztonsági mentés mappája!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Nem adható hozzá a biztonsági mentés almappája!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Nem távolíthatod el az utolsó profilt!" backintime-1.4.3/common/po/id.po000066400000000000000000001462651455673541400165230ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-18 16:24+0000\n" "Last-Translator: andika \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Peringatan" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Profil utama" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokal" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Kunci rahasia SSH" #: common/config.py:304 msgid "encrypted" msgstr "terenkripsi" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Enkripsi" #: common/config.py:310 msgid "SSH encrypted" msgstr "Terenkripsi secara SSH" #: common/config.py:317 msgid "Default" msgstr "Bawaan" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Folder snapshot tidak valid!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Anda harus memilih setidaknya satu folder untuk backup!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Folder backup tidak boleh disertakan." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Sub folder backup tidak bisa disertakan." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opsi tidak valid. {path} bukan sebuah folder." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID tidak boleh kosong." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Salin link (dereference link simbolik)" #: common/config.py:498 msgid "Expert Options" msgstr "Opsi Tingkat Lanjut" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Tidak dapat menemukan crontab.\n" "Apakah Anda yakin cron sudah terpasang?\n" "Jika tidak, Anda harus mematikan semua backup otomatis." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Gagal menulis crontab baru." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Penjadwalan udev tidak dapat digunakan dengan mode {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" sudah ada." #: common/configfile.py:736 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 password" msgstr "Mohon konfirmasi kata sandi" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Kata sandi tidak cocok." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Ambil snapshot" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Tidak dapat melepas kait {mountprocess} dari {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} tidak ditemukan. Silakan pasang misalnya {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Titik kait {} tidak kosong." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}': Masukkan kata sandi untuk {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "GAGAL" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Pulihkan izin" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Selesai" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Tunda backup saat menggunakan baterai" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Menunggu %s detik." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Gagal untuk mengambil snapshot {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Menyelesaikan" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Tidak bisa membuat folder" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Menyimpan berkas konfigurasi…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Menyimpan izin…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Ditemukan sisa snapshot {snapshot_id} yang dapat dilanjutkan." #: common/snapshots.py:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Tidak bisa membuang folder" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Mengambil snapshot" #: common/snapshots.py:1254 msgid "Success" msgstr "Sukses" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' berakhir dengan kode keluar {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Lihat 'man rsync' untuk lebih jelasnya" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Tidak ada yang berubah, tidak perlu membuat snapshot baru" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Tidak bisa mengubah nama {new_path} ke {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Pembuangan pintar" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Membuang snapshot lama" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Mencoba menjaga ruang kosong minimum" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Mencoba menjaga minimum {perc} inode bebas" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Sekarang" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Tidak bisa mengait {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent tidak ditemukan. Pastikan sudah terpasang." #: common/sshtools.py:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cipher {cipher} gagal untuk {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Path jauh ada tapi sayangnya bukan sebuah direktori." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Path jauh tidak dapat ditulisi." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Path jauh tidak dapat dieksekusi." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Tidak bisa membuat path jauh." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Host {host} jauh tidak mendukung {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Lihat 'man backintime' untuk instruksi lebih lanjut" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Host {host} jauh tidak mendukung hardlink" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Mohon masukkan kata sandi untuk \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Pintasan" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Tambahkan untuk Disertakan" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Tambahkan pada Perkecualian" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} belum dikonfigurasi. Apakah Anda hendak memulihkan konfigurasi " "sebelumnya?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Tidak bisa menemukan folder snapshot.\n" "Jika folder tersebut ada pada drive lepasan harap tancapkan lalu tekan OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Ambil snapshot" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "Gunakan waktu & ukuran modifikasi untuk deteksi perubahan berkas." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Ambil snapshot (mode checksum)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Gunakan checksum untuk deteksi perubahan berkas." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Istirahatkan proses snapshot" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Lanjutkan proses snapshot" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Hentikan proses snapshot" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Segarkan daftar snapshot" #: qt/app.py:480 msgid "Name snapshot" msgstr "Namai snapshot" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Buang snapshot" #: qt/app.py:488 msgid "View snapshot log" msgstr "Lihat catatan log snapshot" #: qt/app.py:492 msgid "View last log" msgstr "Lihat catatan log terakhir" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Kelola profil…" #: qt/app.py:500 msgid "Shutdown" msgstr "Matikan" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Matikan sistem setelah snapshot selesai." #: qt/app.py:504 msgid "Setup language…" msgstr "Siapkan bahasa…" #: qt/app.py:508 msgid "Exit" msgstr "Keluar" #: qt/app.py:512 msgid "Help" msgstr "Bantuan" #: qt/app.py:516 msgid "Profiles config file" msgstr "Berkas konfigurasi profil" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Situs Web" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Changelog" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ" #: qt/app.py:528 msgid "Ask a question" msgstr "Ajukan sebuah pertanyaan" #: qt/app.py:531 msgid "Report a bug" msgstr "Laporkan sebuah bug" #: qt/app.py:534 msgid "Translation" msgstr "Terjemahan" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Tentang" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Pulihkan" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "Pulihkan berkas atau folder yang dipilih ke tujuan asal." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Pulihkan ke …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Naik" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Tampilkan berkas tersembunyi" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Bandingkan snapshot…" #: qt/app.py:627 msgid "&Backup" msgstr "&Backup" #: qt/app.py:638 msgid "&Restore" msgstr "&Pulihkan" #: qt/app.py:644 msgid "&Help" msgstr "B&antuan" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Jika anda menutup jendela ini Back In Time tidak akan mampu mematikan sistem Anda ketika snapshot selesai dibuat.\n" "Apakah Anda yakin ingin menutupnya?" #: qt/app.py:905 msgid "Working:" msgstr "Sedang Bekerja:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Selesai, backup tidak diperlukan" #: qt/app.py:962 msgid "Working" msgstr "Sedang Bekerja" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Kesalahan" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Terkirim" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Kecepatan" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "ETA" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Home" #: qt/app.py:1067 msgid "Backup folders" msgstr "Folder backup" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nama Snapshot" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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.\n" "Jika Anda tidak memerlukannya lagi Anda dapat menghapusnya dengan {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Buang elemen yang lebih baru di folder asal." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Pulihkan berkas atau folder yang dipilih ke tujuan asal dan\n" "hapus berkas/folder yang tidak ada dalam snapshot.\n" "Mesti sangat berhati-hati karena ini akan\n" "menghapus berkas/folder yang dikecualikan\n" "pada saat pengambilan snapshot." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "PERINGATAN: Menghapus berkas-berkas dalam sistem berkas root dapat merusak " "seluruh sistem Anda!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Snapshot" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Pulihkan {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Pulihkan {path} ke …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "Pengaturan bahasa hanya berlaku setelah memulai ulang Back In Time." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Penulis" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Terjemahan" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Lisensi" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Siapkan bahasa" #: qt/languagedialog.py:87 msgid "System default" msgstr "Baku sistem" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Gunakan bahasa sistem operasi." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Diterjemahkan: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "platform penerjemahan" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Terjemahan Anda" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Tampilan Log Terakhir" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Tampilan Log Snapshot" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Snapshot" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Semua" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Perubahan" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Kesalahan" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informasi" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[K] Kesalahan, [I] Informasi, [U] Ubah" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "dekode path" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Salin" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Dekode" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Apakah Anda ingin mengecualikan ini?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Pertanyaan" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Lihat Catatan Log Terakhir" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Mulai {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Sedang Bekerja…" #: qt/qttools.py:370 msgid "Today" msgstr "Hari ini" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Kemarin" #: qt/qttools.py:386 msgid "This week" msgstr "Minggu ini" #: qt/qttools.py:393 msgid "Last week" msgstr "Minggu lalu" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Pemeriksaan terakhir {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Tampilkan log lengkap" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Kelola profil" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Sunting" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Tambah" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Buang" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Umum" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Mode" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Lokasi penyimpanan snapshot" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Folder" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Pengaturan SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Host" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Pengguna" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Path" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Cipher" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Kunci Privat" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Kata Sandi" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Simpan Kata Sandi ke Ring Kunci" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Tingkat Lanjut" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Path snapshot lengkap" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Jadwal" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Dinonaktifkan" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Pada setiap boot/reboot" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Setiap {n} menit" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Setiap jam" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Setiap {n} jam" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "Waktu Khusus" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Setiap Hari" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Berulang (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Ketika drive tersambung (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Setiap minggu" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Setiap bulan" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Setiap tahun" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Hari" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Hari kerja" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Jam" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Jam" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Setiap" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Jam" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Hari" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Minggu" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Bulan" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Sertakan" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Menyertakan berkas dan folder" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Tambah berkas" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Tambah folder" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "K&ecualikan" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Wildcard ({example1}) akan diabaikan dengan mode 'SSH terenkripsi'.\n" "Hanya tanda bintang tunggal atau ganda yang diizinkan ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Abaikan pola, berkas, atau folder" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Sangat direkomendasikan" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Tambahkan baku" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Abaikan berkas yang lebih dari: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Abaikan berkas yang lebih dari nilai di %(prefix)s.\n" "Dengan 'Mode rsync lengkap' dimatikan, ini hanya akan mempengaruhi berkas-berkas baru\n" "karena untuk rsync ini merupakan pilihan transfer, bukan opsi pengabaian.\n" "Maka berkas-berkas besar yang telah dicadangkan sebelumnya akan tetap berada\n" "dalam snapshot-snapshot bahkan setelah mereka berubah." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "Bu&ang Otomatis" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Lebih lama dari" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Tahun" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Jika ruang kosong kurang dari" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Jika inode bebas kurang dari" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Penghapusan pintar:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Jalankan di latar belakang pada host jarak jauh." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTAL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Simpan semua snapshot selama" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "hari terakhir." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Simpan satu snapshot tiap hari selama" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Simpan satu snapshot tiap minggu selama" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "minggu terakhir." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Simpan satu snapshot tiap bulan selama" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "bulan terakhir." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Simpan satu snapshot tiap tahun untuk semua tahun." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Jangan buang snapshot yang telah dinamai." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Opsi" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Fungsikan pemberitahuan" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Matikan snapshot ketika menggunakan baterai" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Status daya tidak tersedia dari sistem" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Jalankan hanya satu snapshot pada suatu saat" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Snapshot-snapshot lain akan diblok hingga snapshot yang sekarang berjalan selesai.\n" "Ini adalah pilihan global. Jadi ini akan mempengaruhi semua profil untuk pengguna ini.\n" "Tetapi Anda harus mengaktifkan ini untuk pengguna lainnya juga." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Backup menggantikan berkas-berkas saat pemulihan" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" "Lanjukan saat ada kesalahan (simpan snapshot-snapshot yang tidak lengkap)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Gunakan checksum untuk mendeteksi perubahan" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "Ambil snapshot baru walaupun ada perubahan atau tidak." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "Level Log" #: qt/settingsdialog.py:805 msgid "None" msgstr "Nihil" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Opsi Tingkat &Lanjut" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Jalankan 'rsync' dengan '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "sebagai cron job" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "pada host jarak jauh" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "ketika mengambil snapshot manual" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Mohon pasang 'nocache' untuk mengaktifkan opsi ini)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "pada mesin lokal" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Arahkan ulang stdout ke /dev/null dalam cronjobs." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Arahkan ulang stderr ke /dev/null dalam cronjobs." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Batasi penggunaan bandwidth rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/detik" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Pertahankan ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Pertahankan atribut tambahan (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Salin tautan yang tidak aman (hanya bisa untuk tautan absolut)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opsi harus diapit tanda kutip mis.: {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Tempelkan opsi tambahan ke rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Awalan untuk dijalankan sebelum setiap perintah pada host jarak jauh.\n" "Variabel perlu di-escape dengan \\$FOO.\n" "Ini tidak menyentuh rsync. Jadi untuk menambah awalan\n" "untuk rsync gunakan \"%(cbRsyncOptions)s\" dengan\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "baku" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Tambahkan prefiks ke perintah-perintah SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Periksa apakah host jarak jauh sedang daring" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Peringatan: jika dimatikan dan host jarak jauh\n" "tidak tersedia, ini dapat menyebabkan\n" "kesalahan yang aneh." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" "Periksa apakah host jarak jauh mendukung semua perintah yang diperlukan" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Peringatan: jika dimatikan dan host jarak jauh\n" "tidak mendukung semua perintah yang dibutuhkan,\n" "ini dapat menyebabkan beberapa kesalahan aneh." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Pulihkan Konfigurasi" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Sunting user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Profil baru" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Ubah nama profil" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Anda yakin ingin menghapus profil \"{name}\" ?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Anda tidak memilih berkas kunci privat untuk SSH.\n" "Apakah Anda ingin membuat pasangan baru kunci publik/privat tanpa kata sandi?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Berkas kunci privat \"{file}\" tidak ada." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Apakah Anda ingin menyalin kunci publik SSH Anda ke\n" "host jarak jauh untuk mengaktifkan login tanpa kata sandi?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Keaslian host \"{host}\" tidak dapat diyakinkan.\n" "\n" "sidik jari kunci {keytype} adalah:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Abaikan pola" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Abaikan berkas" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Abaikan folder" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Sertakan berkas" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Sertakan folder" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Anda yakin ingin mengubah folder snapshot?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Gagal membuat kunci SSH baru dalam {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Path snapshot lengkap: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "difungsikan" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "dinonaktifkan" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Pulihkan Pengaturan" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Mohon navigasi ke snapshot sumber pemulihan konfigurasi dari {appName}. Path mungkin tampak sebagai berikut:\n" "{samplePath}\n" "\n" "Jika snapshot Anda ada pada drive jarak jauh atau bila mereka terenkipsi Anda harus memasang mereka secara manual dahulu. Jika Anda menggunakan Mode SSH Anda mungkin juga butuh untuk mengatur kunci publik untuk login ke host jarak jauh.\n" "Lihat pada 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Konfigurasi tidak ditemukan" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "skrip user-callback tidak memiliki baris shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang dalam skrip user-callback tidak dapat dijalankan." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Gunakan %1 dan %2 untuk parameter path" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Hanya snapshot yang berbeda" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Hanya cantumkan daftar snapshot yang sama dengan: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Pemeriksaan mendalam (lebih akurat, tetapi lambat)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Hapus" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Pilih Semua" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Bandingkan" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Pergi Ke" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Opsi" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Anda tidak bisa membandingkan sebuah snapshot dengan dirinya sendiri." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Perintah tidak ditemukan" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Ini tidak dapat dibatalkan!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "PERINGATAN" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Jangan ikutkan \"{path}\" dari snapshot mendatang?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " dan tambahkan user anda pada grup 'fuse'" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "\"%s\" adalah sebuah symlink. Tujuan yand dihubungkan tidak akan dibackup hingga anda memasukkannya juga.\n" #~ "Apakah anda ingin memasukkan symlinks tujuan?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Catatan ini telah di decode dengan pencarian pola otomatis\n" #~ "### Jika ada beberapa jalur yang tidak didecode anda dapat mendecodenya secara manual menggunakan:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s bukan merupakan anggota dari grup 'fuse'.\n" #~ " Jalankan 'sudo adduser %(user)s fuse'. Untuk menerapkan perubahan silahkan logout dan login kembali.\n" #~ "Lihat pada 'man backintime' untuk instruksi lebih lanjut." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s tidak ditemukan dalam ssh_known_hosts." #~ msgid "&Snapshot" #~ msgstr "&Snapshot" #~ msgid "&View" #~ msgstr "&Tampilan" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Perubahan dan Kesalahan" #~ msgid "Config File Help" #~ msgstr "Bantuan File Pengaturan" #~ msgid "Diff" #~ msgstr "Bandingkan" #~ msgid "Diff Options" #~ msgstr "Pilihan Perbandingan" #~ msgid "Error:" #~ msgstr "Kesalahan:" #~ msgid "Every 10 minutes" #~ msgstr "Setiap 10 menit" #~ msgid "Every 12 hours" #~ msgstr "Setiap 12 jam" #~ msgid "Every 30 minutes" #~ msgstr "Setiap 30 menit" #~ msgid "Every 4 hours" #~ msgstr "Setiap 4 jam" #~ msgid "Every 5 minutes" #~ msgstr "Setiap 5 menit" #~ msgid "Every 6 hours" #~ msgstr "Setiap 6 jam" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "System backup menyeluruh hanya dapat membuat sebuah snapshot untuk dipulihkan pada disk fisik dengan partisi disk yang sama dengan asalnya; memulihkan pada disk fisik atau disk yang sama dengan partisi yang berbeda akan menyebabkan kemungkinan system yang rusak dan tidak dapat digunakan.\n" #~ "\n" #~ "System backup menyeluruh akan menulis di atas beberapa settingan yang telah dikustomkan. Lanjutkan?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Tabrakan hash muncul pada hash_id %s. Naikkan nilai global hash_collision " #~ "dan cobalah lagi." #~ msgid "Local encrypted" #~ msgstr "Terenkripsi secara lokal" #~ msgid "Modify for Full System Backup" #~ msgstr "Ubah untuk System Backup Menyeluruh" #~ msgid "Mountprocess lock timeout" #~ msgstr "Kunci proses mount telah habis" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "Otentikasi tak bersandi untuk %(user)s@%(host)s gagal. Lihat pada 'man " #~ "backintime' untuk instruksi lebih lanjut." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "Ping %s gagal. Host sedang down atau alamatnya salah." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Memulihkan '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Pulihan '%s' ke ..." #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Kembalikan file atau folder yang telah dipilih.\n" #~ "Jika tombol ini abu-abu ini karena \"{now}\" dipilih di bagian kiri dari daftar snapshot." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Pengaturan" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Snapshot: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Tampilkan isi disk ini" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Lihat snapshot yang dibuat pada {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "DENGAN KESALAHAN !" #~ msgid "Working..." #~ msgstr "Sedang Bekerja..." #~ msgid "You can't include backup folder!" #~ msgstr "Anda tidak boleh menyertakan folder backup!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Anda tidak boleh menyertakan sub-folder backup !" #~ msgid "You can't remove the last profile!" #~ msgstr "Anda tidak boleh menghapus profil terakhir !" backintime-1.4.3/common/po/is.po000066400000000000000000001206641455673541400165350ustar00rootroot00000000000000# Sveinn í Felli , 2023. msgid "" msgstr "" "Project-Id-Version: Icelandic (Back In Time)\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-16 18:53+0000\n" "Last-Translator: SomeTr \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.0.2\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Aðvörun" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Aðalsnið" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Staðvært" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH einkalykill" #: common/config.py:304 msgid "encrypted" msgstr "dulritað" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Dulritun" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH dulritað" #: common/config.py:317 msgid "Default" msgstr "Sjálfgefið" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Snið: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Mappa fyrir skyndiafrit er ekki gild!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "Aftrits mappa getur ekki verið innifalin." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Aftrits undirmappa getur ekki verið innifalin." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ógildur valkostur: {path} er ekki mappa." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID getur ekki verið tómt." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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 "" "Áfanga skrákerfi fyrir {path} er í FAT formi sem virkar ekki með hörðum-" "tenglum. Vinsamlegast notaðu upprunalegt Linux skráarkerfi." #: common/config.py:493 #, 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 "" "Áfanga skráarkerfi fyrir {path} er í SMB-uppsetri stöðu. Vinsamlegast gakktu" " úr skugga um að Fjar SMB þjónn styður symlink-a eða virkjaðu {copyLinks} í " "{expertOptions}." #: common/config.py:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Afrita tengla (afbyggja tákntengi - dereference symbolic links)" #: common/config.py:498 msgid "Expert Options" msgstr "Ítarlegri valkostir" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Finn ekki crontab.\n" "Ertu viss um að cron sé uppsett?\n" "Ef ekki, ættirðu að gera alla sjálfvirka öryggisafritun óvirka." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Mistókst að skrifa nýtt crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Áætlað udev virkar ekki með hamnum {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Sniðið \"{name}\" er þegar til staðar." #: common/configfile.py:736 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 password" msgstr "Staðfestu aðgangsorð" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Lykilorðin samsvara ekki." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Taka skyndiafrit" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Get ekki aftengt {mountprocess} úr {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} fannst ekki. Settu upp t.d. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Tengipunktur {} ekki laus." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Snið '{profile}': Settu inn lykilorð fyrir {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "MISTÓKST" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Endurheimta heimildir" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Búið" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Fresta afritun þegar rafhlöður eru í notkun" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" #: common/snapshots.py:772 #, 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:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Mistókst að taka skyndiafritið {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Geng frá" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Get ekki búið til möppu" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Vista stillingaskrá..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Vista aðgangsheimildir..." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Get ekki fjarlægt möppu" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Taka skyndiafrit" #: common/snapshots.py:1254 msgid "Success" msgstr "Tókst" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Skoðaðu 'man rsync' til að sjá frekari upplýsingar" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Gat ekki endurnefnt {new_path} sem {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Snjöll fjarlæging" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Fjarlægja gömul skyndiafrit" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Reyna að halda í lágmarksmagn af lausu plássi" #: common/snapshots.py:1737 #, 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:2821 qt/app.py:1616 msgid "Now" msgstr "Núna" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Get ekki tengt {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Fjartengd slóð er til, en er ekki mappa." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Fjartengd slóð er ekki skrifanleg." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Fjartengd slóð er ekki keyranleg." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Tókst ekki að útbúa fjartengda slóð." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Settu inn lykilorð fyrir \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Flýtilyklar" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "Bæta við meðfylgjandi" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Bæta við útilokun" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" #: qt/app.py:453 msgid "Take a snapshot" msgstr "Taka skyndiafrit" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Nota gátsummu til að skynja breytingar á skrám." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Gera hlé á vinnslu skyndiafrits" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Halda áfram með vinnslu skyndiafrits" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Stöðva vinnslu skyndiafrits" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Endurlesa lista yfir skyndiafrit" #: qt/app.py:480 msgid "Name snapshot" msgstr "Nefndu skyndiafrit" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Fjarlægja skyndiafrit" #: qt/app.py:488 msgid "View snapshot log" msgstr "Skoða atvikaskrá skyndiafrita" #: qt/app.py:492 msgid "View last log" msgstr "Skoða síðustu atvikaskrá" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Sýsla með notkunarsnið" #: qt/app.py:500 msgid "Shutdown" msgstr "Slökkva" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Slökkva á kerfi þegar skyndiafritun lýkur." #: qt/app.py:504 #, fuzzy msgid "Setup language…" msgstr "Setja upp tungumál" #: qt/app.py:508 msgid "Exit" msgstr "Hætta" #: qt/app.py:512 msgid "Help" msgstr "Hjálp" #: qt/app.py:516 msgid "Profiles config file" msgstr "Stillingaskrá notkunarsniða" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Vefsvæði" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Breytingasaga" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ / Algengar spurningar" #: qt/app.py:528 msgid "Ask a question" msgstr "Spyrja spurninga" #: qt/app.py:531 msgid "Report a bug" msgstr "Tilkynna um villu" #: qt/app.py:534 msgid "Translation" msgstr "Þýðing" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Um forritið" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Endurheimta" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "Endurheimta valdar skrár eða möppur á upprunalegar staðsetningar." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Endurheimta í …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "Upp" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Sýna faldar skrár" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Bera saman skyndiafrit" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "Endu&rheimta" #: qt/app.py:644 msgid "&Help" msgstr "&Hjálp" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "Að vinna:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Búið, engin öryggisafritun nauðsynleg" #: qt/app.py:962 msgid "Working" msgstr "Að vinna" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Villa" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Sent" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Hraði" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Áætluð lok" #: qt/app.py:1050 msgid "Global" msgstr "Víðvært" #: qt/app.py:1051 msgid "Root" msgstr "Kerfisstjóri (root)" #: qt/app.py:1052 msgid "Home" msgstr "Einkamappa (Home)" #: qt/app.py:1067 msgid "Backup folders" msgstr "Öryggisafritunarmöppur" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Heiti skyndiafrits" #: qt/app.py:1202 #, fuzzy 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 skyndiafritið" msgstr[1] "Ertu viss um að þú viljir fjarlægja skyndiafritið" #: qt/app.py:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "Fjarlægja nýjar skrár í upprunalegri möppu" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "" "Ertu viss um að þú viljir endurheimta skrárnar\n" "í nýju möppuna {path}" msgstr[1] "" "Ertu viss um að þú viljir endurheimta skrárnar\n" "í nýju möppuna {path}" #: qt/app.py:1370 #, fuzzy 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 skrárnar" msgstr[1] "Ertu viss um að þú viljir endurheimta skrárnar" #: qt/app.py:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 msgid "Snapshot" msgstr "Skyndiafrit" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Endurheimta {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Endurheimta {path} í …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Höfundar" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Þýðingar" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Notkunarleyfi" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Setja upp tungumál" #: qt/languagedialog.py:87 msgid "System default" msgstr "Sjálfgefið í kerfinu" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Nota tungumál stýrikerfis." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Þýtt: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "þýðingakerfið" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Þýðing þín" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Snið" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Skyndiafrit" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Sía" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Allt" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Breytingar" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Villur" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Upplýsingar" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Villa, [I] Upplýsingar, [C] Breyta" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "afkóða slóðir" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Afrita" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Afkóða" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Viltu undanskilja þetta?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Spurning" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Skoða síðasta annál" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Ræsa {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Í vinnslu…" #: qt/qttools.py:370 msgid "Today" msgstr "Í dag" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Í gær" #: qt/qttools.py:386 msgid "This week" msgstr "Í þessari viku" #: qt/qttools.py:393 msgid "Last week" msgstr "Í síðustu viku" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "Síðasta athugun {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Birta fullan annál" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Sýsla með notkunarsnið" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Breyta" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Bæta við" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Fjarlægja" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Almennt" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Hamur" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Hvar á að vista skyndiafrit" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Mappa" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH stillingar" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Miðlari" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Gátt" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Notandi" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Slóð" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Dulritunarlykill" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Einkalykill" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Lykilorð" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Vista lykilorð í lyklakippu" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Nánar" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Heildarslóð skyndiafrita" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Vinnuáætlun" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Óvirkt" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Við hverja ræsingu/endurræsingu" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "Á klukkustundar fresti" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Á klukkustundar fresti" msgstr[1] "Á klukkustundar fresti" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Sérsniðnar klukkustundir" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Daglega" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Endurtekið (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Þegar drif er tengt (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Vikulega" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Mánaðarlega" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Árlega" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Dagur" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Vikudagur" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Klukkustund" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Klukkustundir" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 msgid "Every" msgstr "Hverjar" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "klukkustund(ir)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "dag(a)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "viku(r)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "mánuð(ir)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "Ta&ka með" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Hafa með skrár og möppur" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Bæta við skrá" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Bæta við möppu" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Undanskilja" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Undanskilja mynstur, skrár eða möppur" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Sérstaklega mælt með" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Bæta við sjálfgefnu" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Hunsa skrár stærri en: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "Sjálfvirk fj&arlæging" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Eldri en" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "ár" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Ef laust pláss er minna en" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Ef lausir i-hnútar (inodes) eru færri en" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Snjöll fjarlæging" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Keyra í bakgrunni á fjartengdri vél." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "Á TILRAUNASTIGI" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Geyma öll skyndiafrit síðustu" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "dag(a)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Geyma eitt skyndiafrit á dag síðustu" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Geyma eitt skyndiafrit á viku síðustu" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "viku(r)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Geyma eitt skyndiafrit á mánuði síðustu" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "mánuði(r)" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Geyma eitt skyndiafrit á ári" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Ekki fjarlægja nefnd skyndiafrit" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "Valk&ostir" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Virkja tilkynningar" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Koma í veg fyrir skyndiafritun þegar rafhlaða er í notkun" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Staða rafhlöðu er ekki tiltæk frá kerfinu" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Keyra aðeins eitt skyndiafrit í einu" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Öryggisafrit skipti út skrám við endurheimtingu" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Halda áfram við villur (geyma ókláruð skyndiafrit)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Nota gátsummu til að skynja breytingar" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Stig atvikaskráningar" #: qt/settingsdialog.py:805 msgid "None" msgstr "Ekkert" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Ítarlegri &valkostir" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Keyra 'rsync' með '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "sem cron-verk" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "á fjartengdri vél" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "við að taka handvirkt skyndiafrit" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "á staðværri vél" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Endurbeina stdout í /dev/null í cron-verkum." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Endurbeina stderr í /dev/null í cron-verkum." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Takmarka notkun rsync á bandbreidd" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/sek" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Varðveita ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Varðveita ítarleg eigindi (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Afrita ótrygga tengla (virkar aðeins með algilda tengla)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Líma viðbótarvalkosti inn í rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "sjálfgefið" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Bæta forskeyti við SSH-skipanir" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Athuga hvort fjarlæg vél er á netinu" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Athuga hvort fjartengd vél styðji allar nauðsynlegar skipanir" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Endurheimta stillingaskrá" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nýtt snið" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Endurnefna snið" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Einkalykilsskráin \"{file}\" er ekki til." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Útilokunarmynstur" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Undanskilja skrá" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Undanskilja möppu" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Hafa með skrá" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Hafa með möppu" #: qt/settingsdialog.py:1930 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:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Heildarslóð skyndiafrita: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "virkt" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "óvirkt" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Endurheimta stillingar" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Engin stillingaskrá fannst" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Skipun" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Færibreytur" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Taka skyndiafrit mismunar" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Ítarleg yfirferð (nákvæmari, en hægvirkt)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Eyða" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Velja allt" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Bera saman" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Fara í" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Valkostir" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Þú getur ekki borið skyndiafrit saman við sjálft sig." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Skipun fannst ekki" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Þetta er ekki hægt að afturkalla!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "AÐVÖRUN" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Útiloka {path} framvegis frá skyndiafritun?" #, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Snið: {name}" #~ msgid "View the current disk contents" #~ msgstr "Skoða innihald á diski" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Skoða skyndiafritið sem búið var til {timestamp}" backintime-1.4.3/common/po/it.po000066400000000000000000001522451455673541400165360ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2023-12-05 15:54+0000\n" "Last-Translator: ctrlaltca \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.2.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Attenzione" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Profilo principale" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Locale" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Chiave privata SSH" #: common/config.py:304 msgid "encrypted" msgstr "cifrato" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Cifratura" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH con cifratura" #: common/config.py:317 msgid "Default" msgstr "Predefinito" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profilo: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Cartella delle istantanee non valida!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Scegliere almeno una cartella per il backup!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "La cartella di backup non può essere inclusa." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "La sotto-cartella di backup non può essere inclusa." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opzione non valida. {path} non è una cartella." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/Utente/Profilo non possono essere vuoti." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Copia collegamenti (segue i collegamenti simbolici)" #: common/config.py:498 msgid "Expert Options" msgstr "Opzioni per esperti" #: common/config.py:502 #, 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 i collegamenti fisici. Utilizza invece la modalità" " 'SSH'." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Impossibile trovare crontab.\n" "Sei sicuro che cron sia installato?\n" "Se non lo è, disattivare tutti i backup automatici." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Impossibile scrivere nuovi crontab." #: common/config.py:1707 #, 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:1722 #, 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:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Il profilo \"{name}\" esiste già." #: common/configfile.py:736 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 password" msgstr "Per favore conferma la password" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "La password non corrisponde." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Prendi istantanea" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Impossibile smontare {mountprocess} da {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} non trovato. Si prega di installare ad es. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "In punto di montaggio {} non è vuoto." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profilo '{profile}': Inserisci la password per {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "FALLITO" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Ripristina permessi" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Fatto" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Backup posticipato durante funzionamento a batteria" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Aspettando %s secondo." msgstr[1] "Aspettando %s secondi." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Creazione dell'istantanea {snapshot_id} fallita." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Finalizzazione" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Impossibile creare la cartella" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Salvataggio file di configurazione…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Salvataggio permessi…" #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Impossibile eliminare la cartella" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Generazione dell'istantanea" #: common/snapshots.py:1254 msgid "Success" msgstr "Successo" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' terminato con codice di uscita {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Vedi 'man rsync' per ulteriori dettagli" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Nulla è cambiato, nessuna nuova istantanea necessaria" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Non posso rinominare {new_path} in {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Rimozione intelligente" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Rimozione vecchie istantanee" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Tentativo di mantenere lo spazio libero minimo" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Tentativo di mantenere almeno il {perc} di inode liberi" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Adesso" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Non riesco a montare {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Non trovo ssh-agent. Accertati che sia installato." #: common/sshtools.py:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cifratura {cipher} fallita per {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Il percorso remoto esiste ma non è una cartella." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Il percorso remoto non è scrivibile." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Il percorso remoto non è eseguibile." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Non ho potuto creare il percorso remoto." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "L'host remoto {host} non supporta {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Guarda 'man backintime' per ulteriori istruzioni" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "L'host remoto {host} non supporta gli hard link" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Inserire la password per \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Collegamenti" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Aggiungi alle inclusioni" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Aggiungi alle esclusioni" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} non è configurato. Vuoi ripristinare una configurazione " "precedente?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Impossibile trovare la cartella delle istantanee.\n" "Se si tratta di un disco rimovibile, collegarlo e premere OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Prendi istantanea" #: qt/app.py:455 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:458 msgid "Take a snapshot (checksum mode)" msgstr "Prendi istantanea (usando i checksums)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Usa il checksum per rilevare i cambiamenti." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Metti in pausa il processo di istantanea" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Riprendi il processo di istantanea" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Interrompi il processo di istantanea" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Aggiorna lista istantanee" #: qt/app.py:480 msgid "Name snapshot" msgstr "Nome Istantanea" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Rimuovi istantanea" #: qt/app.py:488 msgid "View snapshot log" msgstr "Visualizza registro istantanea" #: qt/app.py:492 msgid "View last log" msgstr "Visualizza ultimo registro" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Gestione profili…" #: qt/app.py:500 msgid "Shutdown" msgstr "Spegnimento" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Arresta il sistema quando l'istantanea è terminata." #: qt/app.py:504 msgid "Setup language…" msgstr "Imposta lingua…" #: qt/app.py:508 msgid "Exit" msgstr "Esci" #: qt/app.py:512 msgid "Help" msgstr "Aiuto" #: qt/app.py:516 msgid "Profiles config file" msgstr "File di configurazione profili" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Sito web" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Registro dei cambiamenti" #: qt/app.py:525 msgid "FAQ" msgstr "Domande frequenti" #: qt/app.py:528 msgid "Ask a question" msgstr "Fai una domanda" #: qt/app.py:531 msgid "Report a bug" msgstr "Segnala un bug" #: qt/app.py:534 msgid "Translation" msgstr "Traduzione" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Informazioni" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Ripristina" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Ripristina su…" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Su" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Mostra i file nascosti" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Confronta istantanee…" #: qt/app.py:627 msgid "&Backup" msgstr "&Backup" #: qt/app.py:638 msgid "&Restore" msgstr "&Ripristina" #: qt/app.py:644 msgid "&Help" msgstr "&Aiuto" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Se chiudi questa finestra Back In Time non sarà in grado di arrestare il sistema al termine dell'istantanea.\n" "Vuoi veramente chiuderla?" #: qt/app.py:905 msgid "Working:" msgstr "Lavoro in corso:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Fatto, nessun backup necessario" #: qt/app.py:962 msgid "Working" msgstr "Lavoro in corso" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Errore" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Inviati" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Velocità" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Tempo rimanente stimato" #: qt/app.py:1050 msgid "Global" msgstr "Globale" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Home" #: qt/app.py:1067 msgid "Backup folders" msgstr "Cartelle di backup" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nome istantanea" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "Le versioni nuove dei file verranno rinominate con il suffisso {suffix} prima del ripristino.\n" "Se non ne hai più bisogno, puoi rimuoverle con {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Rimuovi gli elementi più recenti nella cartella originale." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Ripristina i file o le cartelle selezionati nella destinazione originale e\n" "elimina i file / le cartelle che non sono nell'istantanea.\n" "Fai molta attenzione perché questo\n" "cancellerà i file / le cartelle che sono stati\n" "esclusi durante l'acquisizione dell'istantanea." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "ATTENZIONE: cancellare i file nella radice del filesystem potrebbe " "danneggiare l'intero sistema!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Istantanea" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Ripristina {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Ripristina {path} su…" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Autori" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Traduzioni" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licenza" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Imposta lingua" #: qt/languagedialog.py:87 msgid "System default" msgstr "Default del sistema" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Usa la lingua del sistema operativo." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Tradotto: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "piattaforma di traduzione" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "La tua traduzione" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Visualizzazione Ultimo Registro" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Visualizza il registro dell'istantanea" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profilo" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Istantanee" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtro" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Tutti" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Cambiamenti" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Errori" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informazione" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Errore, [I] Informazione, [C] Cambiamento" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "decodifica i percorsi" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Copia" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Decodifica" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Vuoi escluderlo?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Domanda" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Visualizza ultimo log" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Avvia {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Lavoro in corso…" #: qt/qttools.py:370 msgid "Today" msgstr "Oggi" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Ieri" #: qt/qttools.py:386 msgid "This week" msgstr "Questa settimana" #: qt/qttools.py:393 msgid "Last week" msgstr "Scorsa settimana" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Ultimo controllo {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Mostra il registro completo" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Gestione profili" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Modifica" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Aggiungi" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Rimuovi" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Generale" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Modalità" #: qt/settingsdialog.py:152 #, 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'." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Dove salvare le istantanee" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Cartella" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Impostazioni di SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Host" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Porta" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Utente" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Percorso" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Cifratura" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Chiave privata" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Password" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Salva la password nel gestore delle password" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Avanzate" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Percorso completo per le istantanee" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Pianificazione" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Disabilitato" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Ad ogni avvio/riavvio" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Ogni minuto" msgstr[1] "Ogni {n} minuti" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Ogni ora" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 msgid "Custom hours" msgstr "Orario personalizzato" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Ogni giorno" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Ripetutamente (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Quando il drive viene connesso (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Ogni settimana" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Ogni mese" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Ogni anno" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Giorno" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Giorno della settimana" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Ora" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Ore" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Ogni" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Ora(e)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Giorno(i)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Settimana(e)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Mese(i)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Includi" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Includi file e cartelle" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Aggiungi file" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Aggiungi cartella" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Escludi" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Le wildcard ({example1}) verranno ignorate nella modalità 'SSH cifrato'.\n" "Sono ammessi solo asterischi singoli o doppi ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Esclusione pattern, file o cartelle" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Fortemente raccomandato" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Aggiungi predefiniti" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Escludi file più grandi di: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Escludi file più grandi del valore in %(prefix)s.\n" "Se la 'Modalità rsync completo' è disabilitata, avrà effetto solo sui nuovi file\n" "perché rsync la tratta come un'opzione di trasferimento, non di esclusione.\n" "Per questo motivo i file copiati in precedenza rimarranno nelle istantanee\n" "anche se sono cambiati." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "Rimozione &automatica" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Più vecchi di" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Anno(i)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Se lo spazio libero è minore di" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Se gli inode liberi sono meno del" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Rimozione intelligente:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Esegui in sottofondo sull'host remoto." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "SPERIMENTALE" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Mantieni tutte le istantanee degli ultimi" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "giorno/i." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Mantieni un'istantanea al giorno per gli ultimi" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Mantieni un'istantanea alla settimana per le ultime" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "settimana/e." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Mantieni un'istantanea al mese per gli ultimi" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "mese/i." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Mantieni un'istantanea all'anno per tutti gli anni." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Non rimuovere le istantanee a cui è stato assegnato un nome." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Opzioni" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Attiva le notifiche" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Disabilita le istantanee quando si usa la batteria" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Impossibile stabilire la modalità di alimentazione" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Esegui solo un'istantanea alla volta" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Le altre istantanee verranno bloccate finché l'istantanea corrente non sarà terminata.\n" "Questa è un'opzione globale, pertanto avrà effetto su tutti i profili di questo utente.\n" "Tuttavia devi attivare quest'opzione anche per gli altri utenti." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Esegui il backup dei file sostituiti durante il ripristino" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continua in caso di errore (mantiene un'istantanea incompleta)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Usa il checksum per riconoscere i cambiamenti" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Livello dei log" #: qt/settingsdialog.py:805 msgid "None" msgstr "Nulla" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Op&zioni per esperti" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Esegui 'rsync' con '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "come cron job" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "sull'host remoto" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "quando si prende un'istantanea manuale" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Per favore installa 'nocache' per abilitare questa opzione)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "sulla macchina locale" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirigi stdout verso /dev/null nei cronjob." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirigi stderr verso /dev/null nei cronjob." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Limita la banda utilizzata da rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Preserva ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Preserva attributi estesi (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Copia collegamenti non sicuri (funziona solo con collegamenti assoluti)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Le opzioni devono essere racchiuse tra virgolette, es. {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Passa opzioni addizionali ad rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Prefisso da eseguire prima di ogni comando sull'host remoto.\n" "Le variabili necessitano di escaping con \\$FOO.\n" "Questo non tocca rsync. Quindi per aggiungere un prefisso\n" "per rsync usa \"%(cbRsyncOptions)s\" con\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "default" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Aggiungi prefisso ai comandi SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Controlla se l'host remoto è online" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Attenzione: se disabilitato e l'host remoto\n" "non è disponibile, questo potrebbe portare ad alcuni\n" "errori strani." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Controlla se l'host remoto supporta tutti i comandi necessari" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Attenzione: se disabilitato e l'host remoto\n" "non supporta tutti i comandi necessari,\n" "questo potrebbe portare ad alcuni strani errori." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Ripristina configurazione" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Modifica callback utente" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nuovo profilo" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Rinomina profilo" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Non hai scelto un file di chiave privata per SSH.\n" "Vorresti generare una nuova coppia di chiavi pubblica / privata senza password?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Il file chiave privata \"{file}\" non esiste." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Vuoi copiare la tua chiave SSH pubblica sull'\n" "host remoto per abilitare il login senza password?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "L'autenticità dell'host \"{host}\" non può essere stabilita.\n" "\n" "L'impronta digitale della chiave {keytype} è:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Pattern di esclusione" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Escludi file" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Escludi cartella" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Includi file" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Includi cartella" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Sei sicuro di voler cambiare la cartella delle istantanee?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Impossibile creare una nuova chiave SSH in {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Percorso completo per le istantanee: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "abilitato" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "disabilitato" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Ripristina impostazioni" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Per favore localizza l'istantanea dalla quale vuoi ripristinare la configurazione di {appName}. Il percorso potrebbe assomigliare a:\n" "{samplePath}\n" "\n" "Se le tue istantanee sono su un'unità remota o se sono cifrate, devi prima montarle manualmente. Se usi la modalità SSH, potresti anche aver bisogno di configurare l'accesso all'host remoto mediante chiave pubblica.\n" "Dai un'occhiata a: 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Configurazione non trovata" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "lo script di callback utente non ha una linea shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Lo shebang nello script di callback dell'utente non è eseguibile." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Usa %1 e %2 come parametri del percorso" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Solo istantanee diverse" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Mostra solo istantanee uguali a: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Controllo approfondito (più accurato ma più lento)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Elimina" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Seleziona tutte" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Confronta" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Vai a" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Opzioni" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Non è possibile confrontare un'istantanea con se stessa." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Comando non trovato" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Questa operazione è irreversibile!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ATTENZIONE" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Escludere \"{path}\" dalle istantanee future?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " ed aggiungi il tuo utente al gruppo 'fuse'" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "\"%s\" è un link simbolico. La destinazione del link non sarà copiata nel backup fintantoché non la includerai esplicitamente.\n" #~ "Vuoi piuttosto includere la destinazione del link simbolico?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Questo log è stato decodificato con un percorso di ricerca automatico\n" #~ "### Se alcuni percorsi non sono decodificati, puoi decodificarli manualmente con:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "Esegui 'sudo adduser %(user)s fuse'. Per applicare le modifiche, disconnettersi e accedere di nuovo.\n" #~ "Guarda 'manuale di backintime' per ulteriori istruzioni.%(user)s non è un membro del gruppo'fuse'.\n" #~ "Esegui 'sudo adduser %(user)s fuse'. Per applicare le modifiche, fai logout e di nuovo login.\n" #~ "Guarda 'man backintime' per ulteriori istruzioni." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s non trovato in ssh_known_hosts." #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Istantanea" #, fuzzy #~ msgid "&View" #~ msgstr "Visualizza" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Modifica & Errori" #~ msgid "Config File Help" #~ msgstr "Guida al file di configurazione" #~ msgid "Create a new SSH key without Password." #~ msgstr "Crea una nuova chiave SSH senza password." #~ msgid "Diff" #~ msgstr "Diff" #~ msgid "Diff Options" #~ msgstr "Opzioni Diff" #~ msgid "Error:" #~ msgstr "Errore:" #~ msgid "Every 10 minutes" #~ msgstr "Ogni 10 minuti" #~ msgid "Every 12 hours" #~ msgstr "Ogni 12 ore" #~ msgid "Every 30 minutes" #~ msgstr "Ogni 30 minuti" #~ msgid "Every 4 hours" #~ msgstr "Ogni 4 ore" #~ msgid "Every 5 minutes" #~ msgstr "Ogni 5 minuti" #~ msgid "Every 6 hours" #~ msgstr "Ogni 6 ore" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Il backup del sistema completo può solo creare un'istantanea da ripristinare sullo stessodisco (i) fisico (i) con lo stesso partizionamento del disco come dalla sorgente;Ripristino su nuovi dischi fisici o sugli stessi dischi con diversi il partizionamento produrrà un sistema potenzialmente rotto e inutilizzabile\n" #~ "\n" #~ "Il backup del sistema completo sovrascriverà alcune impostazioni che potrebbero essere state personalizzate. Continuare?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Collisione creata in hash_id %s. Incrementail valore globale hash_collision " #~ "e prova di nuovo" #~ msgid "Key File" #~ msgstr "File chiave" #~ msgid "List only different snapshots" #~ msgstr "Elenca solo istantanee differenti" #~ msgid "Local encrypted" #~ msgstr "Cifratura locale" #~ msgid "Modify for Full System Backup" #~ msgstr "Modifica per il backup del sistema completo" #~ msgid "Mountprocess lock timeout" #~ msgstr "Timeout blocco processo di montaggio" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "Autenticazione senza password per %(user)s@%(host)s fallita. Guarda il 'man " #~ "backintime' per ulteriori istruzioni." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "" #~ "Il ping verso %s è fallito. L'host non funziona o l'indirizzo è sbagliato." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profilo: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Ripristina '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Riporta '%s' a..." #, fuzzy, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Ripristina il file o la cartella selezionato/a.\n" #~ "Se il pulsante è disabilitato molto probabilmente è perché \"{now}\" è selezionato nell'elenco delle istantanee sulla sinistra." #~ msgid "Run 'ionice':" #~ msgstr "Esegui 'ionice':" #~ msgid "Run 'nice':" #~ msgstr "Esegui 'nice':" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Esegui 'rsync' con 'nocache':" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Esegui 'rsync' con 'nocache':" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Impostazioni" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Istantanea: %s" #~ msgid "View the current disk contents" #~ msgstr "Visualizza il contenuto attuale del disco" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Visualizza l'istantanea fatta il {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "CON ERRORI !" #~ msgid "Working..." #~ msgstr "Lavoro in corso..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Impossibile includere la cartella di backup !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Impossibile includere una sottocartella di backup !" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Non si può rimuovere l'ultimo profilo !" backintime-1.4.3/common/po/ja.po000066400000000000000000001501411455673541400165050ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-30 08:12+0000\n" "Last-Translator: kazuo \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "警告" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "メインプロファイル" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "ローカル" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH 秘密鍵" #: common/config.py:304 msgid "encrypted" msgstr "暗号化" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "暗号" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH暗号化" #: common/config.py:317 msgid "Default" msgstr "標準設定" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "プロファイル: 「{name}」" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "スナップショットフォルダが無効です!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "バックアップするフォルダを少なくとも1つ選択する必要があります!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "バックアップフォルダを含めることはできません。" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "バックアップのサブフォルダを含めることはできません。" #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "無効なオプションです。{path} はフォルダではありません。" #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "ホスト/ユーザ/プロファイルID を入力してください。" #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "書き込めません: {path}\n" "本当に書き込み権限がありますか?" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "リンクをコピーする(シンボリックリンクの参照解除)" #: common/config.py:498 msgid "Expert Options" msgstr "上級者向けオプション" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "crontab が見つかりません。\n" "cronがインストールされていますか?\n" "もしそうでなければ、自動バックアップをすべて無効にしてください。" #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "新しい crontab の書き込みに失敗しました。" #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "モード {mode} では、udev スケジュールは使用できません" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "プロファイル 「{name}」 はすでに存在しています。" #: common/configfile.py:736 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 password" msgstr "パスワード確認" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "パスワードが一致しません。" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "encfsのバージョン1.7.2と 以前は --reverse オプションにバグがあった。encfsをアップデートしてください。" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "スナップショット取得" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "{mountprocess} を {mountpoint} からアンマウントできない。" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} が見つかりません。インストールしてください。例 {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "マウントポイント {} が空ではありません。" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "プロファイル 「{profile}」: {mode} のパスワードを入力してください " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "失敗" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "パーミッションを復元" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "完了" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "バッテリー動作のときは延期する" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "スナップショットフォルダがみつかりません。\n" "もしリムーバブルドライブ上にあるのなら、接続して下さい。" #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "%s 秒待っています。" #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "スナップショット {snapshot_id} の取得に失敗しました。" #: common/snapshots.py:826 msgid "Finalizing" msgstr "完了処理中" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "フォルダを作成できません" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "設定ファイルの保存…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "パーミッションを保存…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "以前に作成した使用できるスナップショット {snapshot_id} がありました。" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "前回作成した {snapshot_id} フォルダを削除しています" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "フォルダを削除できません" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "スナップショットの取得" #: common/snapshots.py:1254 msgid "Success" msgstr "成功" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "ソースファイルの消失による部分的な転送 (man rsync' を参照)" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' は終了コード {exit_code} で終了しました" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "詳細は'man rsync'を参照" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "ネガティブの rsync 終了コードはシグナル番号です、'kill -l' および 'man kill' を参照してください" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "変更箇所がないので新しいスナップショットは必要ありません" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{path} を {new_path} に変更できません" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "賢く削除" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "古いスナップショットを削除" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "最小限のフリースペースの確保を試みる" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "最小限の{perc}フリースイノードの確保を試みる" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "現在" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs} をマウントできません" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agentが見つかりません。インストールされているか確認してください。" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "ssh の秘密鍵を解除できませんでした。パスワードが間違っているか、cron で使用できないパスワードです。" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "暗号 {cipher} は {host} で失敗。" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "リモートパスは存在するが、ディレクトリではない。" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "リモートパスは書き込みできません。" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "リモートパスは実行可能ではありません。" #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "フォルダを作成できません。" #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "リモートホスト {host} は {command} をサポートしていない" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "詳しくは 'man backintime' を参照してください" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "ホスト {host} のチェックコマンドが不明なエラーを返しました" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "リモートホスト {host} がハードリンクをサポートしていません" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "公開鍵 \"{pubkey}\" をリモートホスト \"{host}\" にコピーしてください" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "ユーザ \"{user}\" のパスワードを入力してください" #: qt/app.py:167 msgid "Shortcuts" msgstr "ショートカット" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "現在選択されているスナップショットには\n" "このフォルダがありません。" #: qt/app.py:252 msgid "Add to Include" msgstr "含むフォルダを指定" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "除外するフォルダを指定" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "{appName} は設定されていません 前回の設定を復元しますか?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "スナップショットフォルダが見つかりません。\n" "もしリムーバブルドライブ上にあるのなら、それを接続してからOKを押してください。" #: qt/app.py:453 msgid "Take a snapshot" msgstr "スナップショットの取得" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "ファイルの変更検出には、変更時間とサイズを使用します。" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "スナップショット取得(チェックサムモード)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "変更の検出にチェックサムを使用する。" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "スナップショットの作成を一時停止する" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "スナップショットの作成を再開する" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "スナップショットの作成を停止する" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "スナップショットリストの更新" #: qt/app.py:480 msgid "Name snapshot" msgstr "名前付きスナップショット" #: qt/app.py:484 msgid "Remove snapshot" msgstr "スナップショットの削除" #: qt/app.py:488 msgid "View snapshot log" msgstr "スナップショットログの表示" #: qt/app.py:492 msgid "View last log" msgstr "最近のログを表示" #: qt/app.py:496 msgid "Manage profiles…" msgstr "プロファイルの管理…" #: qt/app.py:500 msgid "Shutdown" msgstr "電源を切る" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "スナップショットの作成終了後に電源を切る。" #: qt/app.py:504 msgid "Setup language…" msgstr "言語設定…" #: qt/app.py:508 msgid "Exit" msgstr "終了" #: qt/app.py:512 msgid "Help" msgstr "ヘルプ" #: qt/app.py:516 msgid "Profiles config file" msgstr "設定ファイルを保存" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "ウェブサイト" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "変更履歴" #: qt/app.py:525 msgid "FAQ" msgstr "よくある質問" #: qt/app.py:528 msgid "Ask a question" msgstr "質問する" #: qt/app.py:531 msgid "Report a bug" msgstr "バグを報告する" #: qt/app.py:534 msgid "Translation" msgstr "翻訳" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "情報" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "復元" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "選択されたファイルやフォルダを元の場所に復元する。" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "復元…" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "選択されたファイルやフォルダを新しい場所に復元する。" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "現在表示されているフォルダとそれに含まれるファイルを元の場所に復元する。" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "現在表示されているフォルダとそれに含まれるファイルを新しい場所に復元する。" #: qt/app.py:560 msgid "Up" msgstr "上層へ" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "不可視ファイルを表示" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "スナップショットを比較…" #: qt/app.py:627 msgid "&Backup" msgstr "&バックアップ" #: qt/app.py:638 msgid "&Restore" msgstr "復元" #: qt/app.py:644 msgid "&Help" msgstr "ヘルプ" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "このウインドウを閉じると Back In Time はスナップショットの作成終了時に電源を切ることができなくなります\n" "閉じてよろしいでしょうか?" #: qt/app.py:905 msgid "Working:" msgstr "作業中:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "完了、バックアップの必要はありませんでした" #: qt/app.py:962 msgid "Working" msgstr "作業中" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "エラー" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "送信" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "速度" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "予定完了時間" #: qt/app.py:1050 msgid "Global" msgstr "全体" #: qt/app.py:1051 msgid "Root" msgstr "ルート" #: qt/app.py:1052 msgid "Home" msgstr "ホーム" #: qt/app.py:1067 msgid "Backup folders" msgstr "バックアップフォルダ" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "スナップショット名" #: qt/app.py:1202 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "ローカルファイルを上書きまたは削除する前に\n" "末尾に {suffix} を付けたバックアップコピーを作成する。" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "復元時により新しいファイルがある場合は拡張子{suffix} 付きでファイル名変更されます\n" "もし不要の場合は以下のコマンドで削除してください : {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "元のフォルダ内の新しいファイルを削除する。" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "選択したファイルやフォルダを元の保存先に復元し\n" "スナップショットに含まれないファイルやフォルダを削除します。\n" "これによって\n" "スナップショット作成時に除外されたファイルやフォルダが\n" "削除されるので、十分注意してください。" #: qt/app.py:1361 #, 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:1370 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "本当にファイルを復元してよろしいですか?" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "{path} にあるより新しいファイルを削除してもよろしいですか?" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "元のフォルダにあるより新しいファイルを削除してもよろしいですか?" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "警告:ファイルシステムルートのファイルを削除すると、システム全体が壊れる可能性があります!" #: qt/app.py:1623 msgid "Snapshot" msgstr "スナップショット" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "{path} を復元" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "{path} を復元…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "言語設定は、Back In Time を再起動した後に有効になります。" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "著作者" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "翻訳" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "ライセンス" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "言語設定" #: qt/languagedialog.py:87 msgid "System default" msgstr "標準設定" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "オペレーティングシステムの言語を使用する。" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "翻訳された: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "翻訳プラットフォーム" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "翻訳へのご協力のお願い" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "最終ログを表示" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "スナップショットログを表示" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "プロファイル" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "スナップショット" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "フィルタ" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "全て" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "変更点" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "エラー" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "情報" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] エラー、 [I] インフォメーション、 [C] 変更点" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "復号先" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "複写" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "デコード" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "これを除外しますか?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "質問" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "最近のログを閲覧" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "{appname} を開始する" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "作業中…" #: qt/qttools.py:370 msgid "Today" msgstr "今日" #: qt/qttools.py:377 msgid "Yesterday" msgstr "昨日" #: qt/qttools.py:386 msgid "This week" msgstr "今週" #: qt/qttools.py:393 msgid "Last week" msgstr "先週" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "スナップショットではなく、ローカルファイルのライブビューです" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "最終チェック {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "すべてのログを見る" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "プロファイルの管理" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "編集" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "追加" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "削除" #: qt/settingsdialog.py:127 msgid "&General" msgstr "一般" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "モード" #: qt/settingsdialog.py:152 #, 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 “を参照ください。" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "スナップショットの保存場所" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "フォルダ" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SHH 設定" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "ホスト" #: qt/settingsdialog.py:204 msgid "Port" msgstr "ポート" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "ユーザ" #: qt/settingsdialog.py:214 msgid "Path" msgstr "パス名" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "暗号化方法" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "プライベート鍵" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "すでにあるプライベート鍵を使用する (通常 \"id_rsa\"というファイル名)" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "新しい SSH鍵をパスワードなしで作成する (すでにプライベート鍵を選択済みの場合は実行できません)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "パスワード" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "パスワードを鍵束に保存" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "パスワードを cron に渡す (セキュリティ上の問題: 管理者にパスワードを知られます)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "上級設定" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "すでに絶対パス" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "スケジュール" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "無効" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "起動/再起動毎に" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "{n} 分毎" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "毎時" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "{n} 時間毎" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "カスタム時間" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "毎日" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "繰り返し (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "ドライブが接続されたとき(udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "毎週" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "毎月" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "毎年" #: qt/settingsdialog.py:383 msgid "Day" msgstr "日" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "曜日" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "時" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "時間" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "Run Back In Time を繰り返し実行する。コンピュータを定期的に起動していない場合に便利です。" #: qt/settingsdialog.py:442 msgid "Every" msgstr "毎" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "時間" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "日" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "週" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "カ月" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "編入する" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "含むファイルとフォルダ" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "ファイルを追加" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "フォルダを追加" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "除外する" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "ワイルドカード ({example1}) は’SSH暗号化’モードでは無視される。\n" "シングルまたはダブルのアスタリスクのみが許可されます ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "ファイルやフォルダの除外パターン" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "非常におすすめ" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "標準設定に追加" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "これより大きいファイルは除外する: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "%(prefix)s より大きいファイルを除外する\n" "'Full rsync mode' を選択しているときは\n" "既にバックアップされている大きなファイルについては実行されますが、\n" "新しい大きなファイルは除外されます\n" "'rsync' の機能を利用したもので厳密にはここで除外対象を指定しているのではないからです。" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "自動削除" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "以下より古い場合" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "年" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "以下より空き容量が少ない場合" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "以下より空きのイノードが少ない場合" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "賢く削除:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "リモートホスト上のバックグラウンドで実行する。" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "実験検証中" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "すべてのスナップショットを保存する日数" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "日。" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "日別スナップショットを保存する日数" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "週別スナップショットを保存する期間" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "週間。" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "月別スナップショットを保存する期間" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "カ月。" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "年別スナップショットを保存する期間。" #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "名前付きスナップショットは削除しないでください。" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "オプション" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "通知を有効にする" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "バッテリ駆動の際はスナップショット取得を無効化" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "システムから電源情報が得られません" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "一度に作成するスナップショットの数をひとつに限定して実行する" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "他のスナップショット作業は現在のものが終了するまで保留されます。\n" "これはすべてのプロファイルに適用されます。\n" "逆に言えば、その条件が満たされないとこのオプションを使うことができません。" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "復元時に置換されたファイルをバックアップする" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "エラー発生時に継続 (不完全なスナップショットを維持)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "変更の検出にチェックサムを使用" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "変更の有無に関わらず、新しいスナップショットを取得する。" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "ログレベル" #: qt/settingsdialog.py:805 msgid "None" msgstr "なし" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "上級者向けオプション" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "何をしているか本当に理解している場合に限りこれらのオプションを変更してください。" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "オプション '{cmd}' をつけて 'rsync' を実行:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "cron として実行" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "リモートホスト上で" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "手動スナップショット撮影時" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "('nocache'をインストールしてこのオプションを有効にしてください)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "ローカルマシン上で" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "cronジョブでstdoutを/dev/nullにリダイレクトしてください。" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "cronジョブでstderrを/dev/nullにリダイレクトしてください。" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "rsync の帯域幅使用量を制限する" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/秒" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "ACL を保存" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "拡張属性を維持(xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "危険なリンクをコピー(絶対リンクに対してのみ動作)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "オプションは必ず引用符で囲むこと 例:{example} 。" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "rsync に追加オプションを貼り付ける" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "リモートホストの各コマンドの前に実行するプレフィックス。\n" "変数には \\$FOO でエスケープする必要があります。\n" "これはrsyncには触れない。そのため\n" "rsyncのプレフィックスを追加するには、\n" "”%(cbRsyncOptions)s” を %(rsync_options_value)s と一緒に使います。\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "標準設定" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "SSHコマンドにプレフィックスを追加する" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "リモートホストがオンラインかどうか確認する" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "警告: 無効でリモートホストが利用できない場合 \n" "奇妙なエラーが発生する可能性があります。" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "リモートホストが必要なコマンドをすべてサポートしているか確認する" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "警告: 無効にした場合、\n" "リモートホストが必要なコマンドをすべてサポートしていないと、\n" "奇妙なエラーが発生する可能性があります。" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "設定の復元" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "ユーザーコールバックの編集" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "新規プロファイル" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "プロファイルをリネーム" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "プロファイル ”{name}” を本当に削除しますか?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "SSH 用の秘密鍵ファイルを選択していません。\n" "新しいパスワードなしの公開鍵/秘密鍵ペアを生成しますか?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "秘密鍵ファイル「{file}」は存在しません。" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "公開 SSH 鍵をリモートホストにコピーして、\n" "パスワード不要のログインを可能にしたいですか?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "ホスト {host} の真正性を確立できない。\n" "\n" "{keytype} キー フィンガープリント:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "フィンガープリントを確認してください!’known_hosts’ ファイルに追加しますか?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "除外するパターン" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "除外するファイル" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "除外するフォルダ" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "含めるファイル" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "含めるフォルダ" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "スナップショットのフォルダを変更してもよろしいですか?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "{path} において新しい SSH鍵の作成に失敗しました" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "すでに絶対パス: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "有効" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "無効" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "設定の復元" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "{appName} の設定を復元したいスナップショットに移動してください。パスは次のようになります:\n" "{samplePath}\n" "\n" "スナップショットがリモートドライブにある場合や暗号化されている場合は、最初に手動でマウントする必要があります。SSH モードを使用する場合は、リモートホストへの公開鍵ログインを設定する必要があるかもしれません。\n" "‘man backintime’を見てください。" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "設定が見つかりません" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "ユーザーコールバックスクリプトには shebang (#!/bin/sh) 行がありません。" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "ユーザーコールバックスクリプトの Shebang が実行可能でない。" #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "パスのパラメータに %1 と %2 を使う" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "スナップショットの差分のみ" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "等しいスナップショットだけをリストアップする: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "ディープチェック (より正確ですが遅いです)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "削除" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "全て選択" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "比較" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "移動" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "オプション" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "スナップショット自体とは比較できません。" #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "コマンドが見つかりません" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "スナップショット {snapshot_id} の {file} を本当に削除したいのですか?" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "スナップショット {count} の {file} を本当に削除したいのですか?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "これを取り消すことはできない!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "警告" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "今後のスナップショットから {path} を除外しますか?" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "スナップショット" #~ msgid "&View" #~ msgstr "表示" #~ msgid "..." #~ msgstr "…" #~ msgid "Changes & Errors" #~ msgstr "変更点とエラー" #~ msgid "Config File Help" #~ msgstr "設定ファイルのヘルプ" #~ msgid "Diff" #~ msgstr "diff" #~ msgid "Diff Options" #~ msgstr "Diff のオプション" #~ msgid "Error:" #~ msgstr "エラー:" #~ msgid "Every 10 minutes" #~ msgstr "10分毎" #~ msgid "Every 5 minutes" #~ msgstr "5分毎" #~ msgid "List only different snapshots" #~ msgstr "異なるスナップショットだけを列挙" #~ msgid "Modify for Full System Backup" #~ msgstr "完全なシステムバックアップの修正" #, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "プロファイル: {name}" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "選択されたファイルやフォルダを復元\n" #~ "このボタンが灰色になっているのは、左側のリストにある\"{now}\"が選択されているからです" #~ msgid "Settings" #~ msgstr "設定" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "スナップショット:%s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "現在のディスクの内容を閲覧" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "{timestamp} に作成されたスナップショットを閲覧" #~ msgid "Working..." #~ msgstr "作業中..." #~ msgid "You can't include backup folder!" #~ msgstr "バックアップフォルダを含むことはできません!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "バックアップサブフォルダを含むことはできません!" #~ msgid "You can't remove the last profile!" #~ msgstr "最後のプロファイルを削除することが出来ません!" backintime-1.4.3/common/po/ko.po000066400000000000000000001424561455673541400165360ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-24 11:34+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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "경고" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "메인 프로파일" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "로컬" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH 개인 키" #: common/config.py:304 msgid "encrypted" msgstr "암호화된" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "암호화" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH 암호화" #: common/config.py:317 msgid "Default" msgstr "기본" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "프로파일: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "스냅샷 폴더가 올바르지 않습니다!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "백업할 폴더를 하나 이상 선택해야 합니다!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "백업 폴더를 설정에 넣을 수 없습니다." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "백업 하위 폴더를 설정에 넣을 수 없습니다." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "잘못된 옵션입니다. {path}은(는) 폴더가 아닙니다." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "호스트/사용자/프로필-ID는 비워둘 수 없습니다." #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "다음 경로에 쓸 수 없습니다: {path}\n" "쓰기 권한이 있습니까?" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "링크 복사(심볼릭 링크 역참조)" #: common/config.py:498 msgid "Expert Options" msgstr "고급 설정" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "crontab을 찾을 수 없습니다.\n" "cron이 설치되어 있습니까?\n" "그렇지 않은 경우 모든 자동 백업을 비활성화해야 합니다." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "새 crontab을 작성하지 못했습니다." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "일정 udev가 {mode} 모드에서 작동하지 않습니다" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\" 프로필이 이미 존재합니다." #: common/configfile.py:736 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 password" msgstr "암호를 확인해 주세요" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "암호가 일치하지 않습니다." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "encfs 버전 1.7.2 이하에는 --reverse 옵션과 관련된 버그가 있습니다. encfs를 업데이트하세요." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "스냅샷 생성하기" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "{mountpoint}에서 {mountprocess}을(를) 마운트 해제할 수 없습니다." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{}을(를) 찾을 수 없습니다. 예를 들어 {}을(를) 설치하세요" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "{} 마운트 지점이 비어 있지 않습니다." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "'{profile}' 프로필: {mode} 암호를 입력하세요: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "실패" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "권한 복원" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "완료" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "배터리를 사용하는 동안 백업 연기" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "스냅샷 폴더를 찾을 수 없습니다.\n" "이동식 드라이브에 있는 경우 해당 드라이브를 연결하세요." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "%s초 기다립니다." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "{snapshot_id} 스냅샷을 생성하지 못했습니다." #: common/snapshots.py:826 msgid "Finalizing" msgstr "마무리 중" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "폴더를 만들 수 없습니다" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "구성 파일 저장 중…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "권한 저장 중…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "계속할 수 있는 남은 {snapshot_id}을(를) 찾았습니다." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "마지막 실행에서 남은 {snapshot_id} 폴더 제거 중" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "폴더를 제거할 수 없습니다" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "스냅샷 생성" #: common/snapshots.py:1254 msgid "Success" msgstr "성공" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "사라진 소스 파일로 인한 부분 전송('man rsync' 참조)" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync'가 종료 코드 {exit_code}로 종료되었습니다" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "자세한 내용은 'man rsync'를 참조하세요" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "rsync 음수 종료 코드는 시그널 번호입니다. 'kill -l' 및 'man kill'을 참조하세요" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "아무것도 변경되지 않았으며 새 스냅샷이 필요하지 않습니다" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{new_path} 경로 이름을 {path} 경로 이름으로 바꿀 수 없습니다" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "스마트 제거" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "오래된 스냅샷 제거" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "최소 여유 공간을 유지하려고 합니다" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "최소 {perc}개의 사용 가능한 아이노드를 유지하려고 합니다" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "현재" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs}을(를) 마운트할 수 없습니다" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "SSH 에이전트를 찾을 수 없습니다. 설치되어 있는지 확인하시기 바랍니다." #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "SSH 개인 키를 잠금 해제할 수 없습니다. 잘못된 암호거나 cron에 암호를 사용할 수 없습니다." #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "{host}에 대한 암호 {cipher}이(가) 실패했습니다." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "원격 경로가 존재하지만 디렉터리가 아닙니다." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "원격 경로에 쓸 수 없습니다." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "원격 경로를 실행할 수 없습니다." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "원격 경로를 생성할 수 없습니다." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "원격 호스트 {host}은(는) {command}을(를) 지원하지 않습니다" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "자세한 지침은 'man backintime'을 참조하세요" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "호스트 {host}의 확인 명령이 알 수 없는 오류를 반환했습니다" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "원격 호스트 {host}은(는) 하드링크를 지원하지 않습니다" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "공개 SSH 키 \"{pubkey}\"를 원격 호스트 \"{host}\"에 복사합니다" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "\"{user}\" 암호를 입력하세요" #: qt/app.py:167 msgid "Shortcuts" msgstr "바로 가기" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "이 폴더가 존재하지 않습니다.\n" "폴더가 없습니다." #: qt/app.py:252 msgid "Add to Include" msgstr "포함할 항목 추가" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "제외할 항목 추가" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "{appName}이(가) 구성되지 않았습니다. 이전 구성을 복원하시겠습니까?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "스냅샷 폴더를 찾을 수 없습니다.\n" "이동식 드라이브에 있는 경우 연결한 다음 확인을 누르세요." #: qt/app.py:453 msgid "Take a snapshot" msgstr "스냅샷 생성" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "파일 변경 감지를 위해 수정 시간 및 크기를 사용합니다." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "체크섬을 포함한 스냅샷 생성하기" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "파일 변경 감지에 체크섬을 사용합니다." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "스냅샷 생성 일시 중지" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "스냅샷 생성 재시작" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "스냅샷 생성 중지" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "스냅샷 목록 새로 고침" #: qt/app.py:480 msgid "Name snapshot" msgstr "스냅샷 이름 변경" #: qt/app.py:484 msgid "Remove snapshot" msgstr "스냅샷 제거" #: qt/app.py:488 msgid "View snapshot log" msgstr "스냅샷 로그 보기" #: qt/app.py:492 msgid "View last log" msgstr "마지막 로그 보기" #: qt/app.py:496 msgid "Manage profiles…" msgstr "프로필 관리…" #: qt/app.py:500 msgid "Shutdown" msgstr "끄기" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "스냅샷이 끝난 후 시스템 종료하기." #: qt/app.py:504 msgid "Setup language…" msgstr "언어 설정…" #: qt/app.py:508 msgid "Exit" msgstr "끝내기" #: qt/app.py:512 msgid "Help" msgstr "도움말" #: qt/app.py:516 msgid "Profiles config file" msgstr "프로필 구성 파일" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "웹사이트" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "변경 내역" #: qt/app.py:525 msgid "FAQ" msgstr "자주 묻는 질문" #: qt/app.py:528 msgid "Ask a question" msgstr "질문하기" #: qt/app.py:531 msgid "Report a bug" msgstr "버그 신고" #: qt/app.py:534 msgid "Translation" msgstr "번역" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "정보" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "복원" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "선택한 파일이나 폴더를 원래 대상으로 복원합니다." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "다음 위치에 복원 …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "선택한 파일 또는 폴더를 새로운 대상으로 복원합니다." #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "현재 표시된 폴더와 해당 폴더의 모든 내용을 원래 대상으로 복원합니다." #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "현재 표시된 폴더와 해당 폴더의 모든 내용을 새 대상으로 복원합니다." #: qt/app.py:560 msgid "Up" msgstr "위로" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "숨겨진 파일 표시" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "스냅샷 비교…" #: qt/app.py:627 msgid "&Backup" msgstr "백업" #: qt/app.py:638 msgid "&Restore" msgstr "복원" #: qt/app.py:644 msgid "&Help" msgstr "도움말" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "이 창을 닫으면 스냅샷이 완료되었을 때 Back In Time이 시스템을 종료할 수 없습니다.\n" "정말로 닫으시겠습니까?" #: qt/app.py:905 msgid "Working:" msgstr "진행 중:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "완료되었습니다. 백업이 필요하지 않습니다" #: qt/app.py:962 msgid "Working" msgstr "진행 중" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "오류" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "전송" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "속도" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "예상 소요 시간" #: qt/app.py:1050 msgid "Global" msgstr "전역" #: qt/app.py:1051 msgid "Root" msgstr "루트" #: qt/app.py:1052 msgid "Home" msgstr "홈" #: qt/app.py:1067 msgid "Backup folders" msgstr "백업 폴더" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "스냅샷 이름" #: qt/app.py:1202 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "로컬 요소를 덮어쓰거나 제거하기 전에\n" "뒤에 오는 {suffix}를 사용하여 백업 복사본을 만듭니다." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "최신 버전의 파일은 복원하기 전에 뒤에 오는 {suffix}로 이름이 변경됩니다.\n" "더 이상 필요하지 않으면 {cmd}을(를) 사용하여 제거할 수 있습니다" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "원본 폴더에서 최신 요소를 제거합니다." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "선택한 파일 또는 폴더를 원래 대상으로 복원하고\n" "스냅샷에 없는 파일 또는 폴더를 삭제합니다.\n" "이렇게 하면 스냅샷을 생성하는 동안\n" "제외되었던 파일과 폴더가 삭제되므로\n" "각별히 주의하세요." #: qt/app.py:1361 #, 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:1370 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "정말로 이러한 요소(들)을 복원하시겠습니까?" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "{path}에 있는 모든 최신 파일을 제거하시겠습니까?" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "원본 폴더에서 최신 파일을 모두 제거하시겠습니까?" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "경고: 파일 시스템 루트에서 파일을 삭제하면 전체 시스템이 손상될 수 있습니다!" #: qt/app.py:1623 msgid "Snapshot" msgstr "스냅샷" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "{path} 복원" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "{path}를 다음 위치에 복원 …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "언어 설정은 Back In Time을 다시 시작한 후에만 적용됩니다." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "저자" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "번역" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "라이센스" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "언어 설정" #: qt/languagedialog.py:87 msgid "System default" msgstr "시스템 기본값" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "운영 체제의 언어를 사용합니다." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "{percent} 번역됨" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "번역 플랫폼" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "귀하의 번역" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "마지막 로그 보기" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "스냅샷 로그 보기" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "프로필" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "스냅샷" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "필터" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "모두" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "변경" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "오류" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "정보" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] 오류, [I] 정보, [C] 변경" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "경로 디코딩" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "복사" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "디코딩" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "제외하시겠습니까?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "질문" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "마지막 로그 보기" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "{appname} 시작" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "진행 중…" #: qt/qttools.py:370 msgid "Today" msgstr "오늘" #: qt/qttools.py:377 msgid "Yesterday" msgstr "어제" #: qt/qttools.py:386 msgid "This week" msgstr "어번 주" #: qt/qttools.py:393 msgid "Last week" msgstr "지난 주" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "이것은 스냅샷이 아니라 로컬 파일의 실시간 보기입니다" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "마지막 확인 시간 {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "전체 로그 보기" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "프로필 관리" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "편집" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "추가" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "삭제" #: qt/settingsdialog.py:127 msgid "&General" msgstr "일반(&G)" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "모드" #: qt/settingsdialog.py:152 #, 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\"를 살펴보십시오." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "스냅샷을 저장할 위치" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "폴더" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH 설정" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "호스트" #: qt/settingsdialog.py:204 msgid "Port" msgstr "포트" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "사용자" #: qt/settingsdialog.py:214 msgid "Path" msgstr "경로" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "암호" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "개인 키(비공개 키)" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "개인 키 파일을 선택하세요(일반적으로 \"id_rsa\" 파일)" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "암호 없는 새 SSH 키 생성(개인 키 파일을 이미 선택했을 경우 허용하지 않음)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "암호" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "키링(Keyring)에 암호 저장" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "크론 캐시 암호(보안 문제: 루트 계정에서 암호를 볼 수 있음)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "고급 설정" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "전체 스냅샷 위치" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "일정" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "비활성화됨" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "부팅/재부팅 할 때 마다" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "매 {n}분 마다" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "1시간마다" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "매 {n}시간마다" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "시간 주기 설정" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "매일" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "반복 (아나크론)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "드라이브를 연결했을 때(udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "매주" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "매월" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "매년" #: qt/settingsdialog.py:383 msgid "Day" msgstr "일" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "평일" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "시간" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "시간" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "반복적으로 Back In Time 실행. 컴퓨터가 비정기적으로 실행 될 때 유용한 기능." #: qt/settingsdialog.py:442 msgid "Every" msgstr "매" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "시간" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "일" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "주" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "월" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "포함(&I)" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "폴더와 파일 포함" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "파일 추가" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "폴더 추가" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "제외(&E)" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "'SSH 암호화' 모드에서는 와일드카드({example1})를 무시합니다.\n" "단일 또는 이중 애스터리스크만 허용합니다 ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "파일 또는 폴더의 예외 패턴" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "강력 추천" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "기본값 추가" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "크기가 큰 파일 제외: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "%(prefix)s 값보다 큰 파일을 제외합니다.\n" "'Full rsync mode'를 사용하지 않을 경우 새 파일에만 영향을 줍니다.\n" "rsync에서는 전송 옵션이지, 제외 옵션이 아니기 때문입니다.\n" "그래서 이전에 백업한 큰 파일은 내용이 바뀌더라도\n" "스냅샷에 남습니다." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "자동 제거(&A)" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "날짜가 다음 보다 오래된 경우" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "년" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "여유 공간이 다음보다 작은 경우" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "여유 공간이 다음보다 작은 경우" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "알아서 제거:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "원격 호스트의 백그라운드에서 실행합니다." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "실험적기능" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "모든 스냅샷의 최신 유지" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "일." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "하루에 최신 스냅샨 1건 유지" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "일주일에 최신 스냅샷 한건 유지" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "주." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "한달에 최신 스냅샷 한건 유지" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "달." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "일년에 최신 스냅샷 한건 유지." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "이름을 붙인 스냅샷은 제거하지 않습니다." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "선택 사항" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "알림 사용하기" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "배터리 사용중에는 스냅샷을 찍을 수 없습니다" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "시스템의 전원 상태를 알 수 없습니다" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "동시에 하나의 스냅샷 동작만 수행" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "현재 스냅샷을 처리할 때까지 다른 스냅샷 처리를 차단합니다.\n" "이 동작은 전역 옵션입니다. 이 사용자의 모든 프로파일에 영향을 줍니다.\n" "다른 사용자에게도 이 옵션을 설정해야합니다." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "복원시 대체 파일 백업" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "오류가 있어도 진행(미완 스냅샷 유지)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "체크섬을 이용해 변경사항 찾기" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "변경 사항과 관계없이 새로운 스냅샷 생성." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "로그 레벨" #: qt/settingsdialog.py:805 msgid "None" msgstr "없음" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "고급 설정" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "주의: 이 옵션들이 무엇을 의미하는지 정확하게 알 경우에만 바꾸세요." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "'{cmd}' 명령으로 'rsync' 실행:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "크론 작업으로 실행" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "원격 호스트에서" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "사용자정의 스냅샷을 찍을때" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(이 옵션을 활성화 하려면 'nocache'를 설치하세요)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "로컬 머신에서" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "크론 작업의 표준 출력을 /dev/null로 보냄." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "크론 작업의 오류 출력을 /dev/null로 보냄." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "rsync 대역폭 사용량 제한" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/sec" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "ACL 설정값 보존" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "확장 속성 (xattr) 보존" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "불안전한 링크 복사(절대 링크인 경우만 동작)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "옵션은 따옴표 안에 넣어야 합니다. 예. {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "rsync에 추가 옵션 넣기" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "원격 호스트에서 실행할 모든 명령 앞에 사용할 접두 옵션입니다.\n" "변수는 \\$FOO 처럼 이스케이핑해야합니다.\n" "rsync에는 해당하지 않습니다. 따라서 rsync에 접두 옵션을 추가하려면\n" "%(rsync_options_value)s와 \"%(cbRsyncOptions)s\"를\n" "사용하십시오\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "기본값" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "SSH 명령에 접두 옵션 추가" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "원격 호스트가 접근 가능한지 확인" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "경고: 이 옵션이 꺼져있거나 원격 호스트를\n" "사용할 수 없다면 이상한 오류를 유발할 수\n" "있습니다." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "원격 호스트에서 필요한 모든 명령을 지원하는지 확인" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "경고: 이 옵션이 꺼져 있거나 원격 호스트에서\n" "필요한 모든 명령을 지원하지 않으면\n" "이상한 오류를 유발할 수 있습니다." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "설정 복구" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "user-callback 편집" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "새로운 프로파일" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "프로파일명을 변경" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "정말 \"{name}\" 프로파일을 삭제하시겠습니까?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "SSH 개인키를 선택하지 않았습니다.\n" "새 암호 없이 공용/개인키 쌍을 만드시겠습니까?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "\"{file}\" 개인키 파일이 없습니다." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "암호 없는 로그인에 사용할 SSH 키를\n" "원격 호스트에 복사하시겠습니까?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "{host} 호스트 인증을 처리할 수 없습니다.\n" "\n" "{keytype} 키 형식의 지문키:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "이 지문키를 검증하세요! 'known_hosts' 파일에 추가할까요?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "제외 패턴" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "제외할 파일" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "제외할 폴더" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "파일 포함" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "폴더를 포함" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "정말 스냅샷 폴더를 바꾸시겠습니까?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "{path}에 새로운 SSH 키 생성 실패" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "전체 스냅샷 경로: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "활성화" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "비활성화" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "설정 복원" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "{appName} 설정을 복원할 스냅샷을 찾아보십시오. 경로 이름은 다음과 같습니다:\n" "{samplePath}\n" "\n" "스냅샷이 원격 드라이브에 있거나 암호화 상태라면 우선 직접 마운트하십시오. SSH 모드를 활용한다면 원격 호스트에 공용키 로그인을 설정해야 합니다.\n" "'man backintime' 내용을 살펴보십시오." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "설정 없음" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "user-callback 스크립트에 쉬뱅(#!/bin/sh) 행이 없습니다." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "user-callback 스크립트의 쉬뱅 명령을 실행할 수 없습니다." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "경로 매개변수로 %1과 %2를 사용" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "스냅샷 비교만 진행" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "다음 조건에 동일한 스냅샷만 조회: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "자세히 확인(느리지만 정확함)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "삭제" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "전체 선택" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "비교" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "이동하기" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "옵션" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "스냅샷을 자신과 비교할 수 없습니다." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "명령어를 찾을 수 없음" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "정말로 {snapshot_id} 스냅샷에서 {file} 파일을 삭제하시겠습니까?" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "정말로 {count}개의 스냅샷에서 {file} 파일을 삭제하시겠습니까?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "이 동작은 취소할 수 없습니다!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "경고" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "앞으로의 스냅샷에서 {path} 경로를 제외하시겠습니까?" #~ msgid "&Snapshot" #~ msgstr "스냅샷" #~ msgid "&View" #~ msgstr "보기" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Diff" #~ msgstr "차이점" #~ msgid "Diff Options" #~ msgstr "차이점 옵션" #~ msgid "Error:" #~ msgstr "오류:" #~ msgid "Every 10 minutes" #~ msgstr "매 10분" #~ msgid "Every 12 hours" #~ msgstr "12시간마다" #~ msgid "Every 30 minutes" #~ msgstr "30분마다" #~ msgid "Every 4 hours" #~ msgstr "4시간마다" #~ msgid "Every 5 minutes" #~ msgstr "매 5분" #~ msgid "Every 6 hours" #~ msgstr "6시간마다" #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "호스트가 다운 되었거나 잘못된 주소입니다. %s ping 실패" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "프로파일: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "%s 을/를 복원" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "다음 위치에 %s 을/를 복원" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "설정" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "스냅샷: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "현재 디스크의 내용을 봅니다" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "{timestamp}에 만들어진 스냅샷을 확인하세요" #~ msgid "Working..." #~ msgstr "동작 중..." #~ msgid "You can't include backup folder!" #~ msgstr "백업 폴더를 포함할 수는 없습니다!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "백업 하위폴더를 포함할 수 없습니다!" #~ msgid "You can't remove the last profile!" #~ msgstr "마지막 프로파일은 제거할 수 없습니다!" backintime-1.4.3/common/po/lt.po000066400000000000000000001426151455673541400165410ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Įspėjimas" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Pagrindinis profilis" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Vietinis" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH privatus raktas" #: common/config.py:304 msgid "encrypted" msgstr "užšifruotas" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Užšifravimas" #: common/config.py:310 msgid "SSH encrypted" msgstr "Užšifruotas su SSH" #: common/config.py:317 msgid "Default" msgstr "Numatytasis" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profilis: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Momentinių kopijų aplankas negalioja!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} nėra aplankas." #: common/config.py:457 #, fuzzy msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID negali būti tuščias." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopijuoti nuorodas (atsieti simbolines nuorodas)" #: common/config.py:498 msgid "Expert Options" msgstr "Parinktys ekspertams" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Nepavyko įrašyti naujo crontab failo." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Suplanuoti udev neveikia naudojant režimą {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilis \"{name}\" jau egzistuoja." #: common/configfile.py:736 #, 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 password" msgstr "Prašome patvirtinti slaptažodį" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Išsaugoti momentinę nuotrauką" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profilis '{profile}': Įveskite slaptažodį dėl {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "NEPAVYKO" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Atstatyti prieigas" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Atlikta" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Atsarginės kopijos kūrimas atidedamas, kol naudojamas akumuliatorius" #: common/snapshots.py:770 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:772 #, 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:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Nepavyko išsaugoti momentinės kopijos {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Baigiama" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Nepavyko sukurti aplanko" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Išsaugomas konfigūracijos failas..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Prieigos yra išsaugomos..." #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Nepavyko pašalinti aplanko" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Padaryti momentinę nuotrauką" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Niekas nepakito, įrašyti naujos momentinės kopijos neprivaloma" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Neįmanoma pervadinti {new_path} į {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Išmanus pašalinimas" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Senų momentinių kopijų pašalinimas" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Bandyti" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Bandoma išlaikyti mažiausiai {perc} laisvų inodų" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Dabar" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Neįmanoma įrengti {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 #, fuzzy msgid "Remote path is not executable." msgstr "„Shebang“ vartotojo atgalinio iškvietimo scenarijuje nėra vykdomas." #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Nepavyko sukurti aplanko." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Prašome įvesti slaptažodį vartotojui \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Nuorodos" #: qt/app.py:187 #, 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:252 msgid "Add to Include" msgstr "Pridėti prie įtraukti" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Pridėti, prie neįtraukti" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} nėra sukonfigūruota. Ar norite atstatyti buvusią konfigūraciją?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "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:453 #, fuzzy msgid "Take a snapshot" msgstr "Išsaugoti momentinę nuotrauką" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 #, fuzzy msgid "Take a snapshot (checksum mode)" msgstr "Išsaugoti momentinę kopiją su kontroline suma" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "Pasikeitimų aptikimui naudoti kontrolines sumas." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Pristabdyti momentinės kopijos procesą" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Pratęsti momentinės kopijos procesą" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Stabdyti momentinės kopijos procesą" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Atnaujinti momentinių kopijų sąrašą" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Išsaugoti momentinę nuotrauką" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Pašalinti momentinę kopiją" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Peržiūrėti momentinių kopijų žurnalą" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Peržiūrėti paskutinį įrašą žurnale" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Pagrindinis profilis" #: qt/app.py:500 msgid "Shutdown" msgstr "Išjungti" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Išjungti sistemą, kai momentinė kopija baigs veikti." #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Išeiti" #: qt/app.py:512 msgid "Help" msgstr "Pagalba" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Išsaugomas konfigūracijos failas" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Interneto svetainė" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Pakeitimų sąrašas" #: qt/app.py:525 msgid "FAQ" msgstr "D.U.K." #: qt/app.py:528 msgid "Ask a question" msgstr "Užduokite klausimą" #: qt/app.py:531 msgid "Report a bug" msgstr "Pranešti apie klaidą" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "Vertimai" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Apie" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Atstatyti" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" "Atkurkite pasirinktus failus arba aplankus į pradinę paskirties vietą." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Atkurti į …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "Atkurkite pasirinktus failus arba aplankus į naują paskirties vietą." #: qt/app.py:552 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:557 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:560 msgid "Up" msgstr "Aukštyn" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Rodyti paslėptus failus" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Išsaugoti momentinę nuotrauką" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "&Atstatyti" #: qt/app.py:644 msgid "&Help" msgstr "&Pagalba" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" 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:905 msgid "Working:" msgstr "Dirbu:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Atlikta, atsarginė kopija nebūtina" #: qt/app.py:962 msgid "Working" msgstr "Dirbama" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Klaida" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Išsiųsta" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Greitis" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Numatomas atvykimo laikas" #: qt/app.py:1050 msgid "Global" msgstr "Globalus" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Pradžia" #: qt/app.py:1067 msgid "Backup folders" msgstr "Atsarginių kopijų aplankai" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Momentinės kopijos pavadinimas" #: qt/app.py:1202 #, 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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/app.py:1314 #, 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:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "Pašalinkite naujesnius failus iš pradinio aplanko" #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "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:1361 #, 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:1370 #, 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:1385 #, 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:1388 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:1393 #, fuzzy msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "ĮSPĖJIMAS: Failų, esančių failų sistemos šaknyje trynimas gali sugadinti " "jūsų visą operacinę sistemą!!!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Momentinė kopija" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Atstatyti {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Atstatyti {path} į …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Autoriai" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Vertimai" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licenzija" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 #, fuzzy msgid "System default" msgstr "numatytasis" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 #, fuzzy msgid "translation platform" msgstr "Vertimai" #: qt/languagedialog.py:232 #, fuzzy msgid "Your translation" msgstr "Vertimai" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Paskutinio \"log\" vaizdas" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Momentinės kopijos žurnalo vaizdas" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profilis" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Momentinės kopijos" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtras" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Visi" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Pakeitimai" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Kaidos" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Informacija" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Klaida, [I] Informacija, [C] Pakeisti" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "iškoduoti adresus" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopijuoti" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Dekoduoti" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Ar norite tai išskirti?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Klausimas" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Peržiūrėti paskutinį įrašą žurnale" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Paleisti {appname}" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Dirbama" #: qt/qttools.py:370 msgid "Today" msgstr "Šiandien" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Vakar" #: qt/qttools.py:386 msgid "This week" msgstr "Šią savaitę" #: qt/qttools.py:393 msgid "Last week" msgstr "Praeitą savaitę" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Paskutinis patikrinimas {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Parodyti visą žurnalą" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Pagrindinis profilis" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Keisti" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Pridėti" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Ištrinti" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Bendri" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Būsena" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Kur išsaugoti momentines kopijas" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Aplankas" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH nustatymai" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Pagrindinis kompiuteris" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Prievadas" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Naudotojas" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Adresas" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Šifras" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Privatus Raktas" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Slaptažodis" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Išsaugoti slaptažodį" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Išplėstiniai parametrai" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Pilnas kelias į momentinę kopiją" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Tvarkaraštis" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Išjungta" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Po kiekvieno operacinės sistemos paleidimo/perkrovimo" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "Kas valandą" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 #, fuzzy msgid "Custom hours" msgstr "Pasirinktinos valandos" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Kasdien" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Pakartotinai (anachronistiškai)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Kai tvarkyklė yra prijungta (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Kas savaitę" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Kas mėnesį" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Kasmet" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Diena" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Savaitės diena" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Valanda" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Valandos" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Kiekvienas" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Valandą (-as)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Diena (os/ų)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Savaitė (ės/čių)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Mėnesį (-ius)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Įtraukti" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Įtraukti failus bei aplankus" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Pridėti failą" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Pridėti aplanką" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "Išs&kirti" #: qt/settingsdialog.py:528 #, fuzzy, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Pakaitos simboliai ({example1}) bus ignoruojami, kai režimas „SSH šifruotas“.\n" "Leidžiama naudoti tik vieną arba dvigubą žvaigždutę ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Išskirkite šablonus, failus ar aplankus" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Labai patariama" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Pridėti numatytąjį" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Išskirti failus, didesnius, nei: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." 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:616 msgid "&Auto-remove" msgstr "&Pašalinti automatiškai" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Senesni nei" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Metai (-ų)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Jei laisvos vietos mažiau nei" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Jei laisvų inodų mažiau nei" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Išmanus pašalinimas" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "Vykdykite nuotolinio pagrindinio kompiuterio fone." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTINIS" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Išsaugokite visas momentines kopijas paskutinei" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "Diena(os)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Paskutinį kartą išsaugokite vieną momentinę kpiją per dieną" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Paskutinį kartą išsaugokite vieną momentinę kopiją per savaitę" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "Savaitė(s)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Paskutinį kartą išsaugokite vieną momentinę kopiją per mėnesį" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "Mėnesis(iai)" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "" "Paskutinį kartą išsaugokite vieną momentinę kopiją per metus visus metus" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Nešalinti momentinių kopijų, turinčių pavadinimus" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "Pa&rinktys" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Įgalinti pranešimus" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Išjunkite momentines kopijas, kai naudojate akumuliatorių" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Maitinimo būsena nepasiekiama iš sistemos" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Vienu metu paleisti tik vieną momentinę kopiją" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "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:772 msgid "Backup replaced files on restore" msgstr "Atkūrimo metu sukurkite pakeistų failų atsarginę kopiją" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Tęskite klaidas (išsaugokite neišsamias momentines kopijas)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Pasikeitimų aptikimui naudoti kontrolines sumas" #: qt/settingsdialog.py:793 #, 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:800 msgid "Log Level" msgstr "Žurnalo lygis" #: qt/settingsdialog.py:805 msgid "None" msgstr "Tuščia" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Parinktys &ekspertams" #: qt/settingsdialog.py:830 #, 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Paleiskite „rsync“ su „{cmd}“:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "kaip cron job" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "nuotoliniame pagrindiniame kompiuteryje" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "darant momentinę kopiją rankiniu būdu" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Norėdami įjungti šią parinktį, įdiekite 'nocache')" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "vietiniame kompiuteryje" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Nukreipkite stdout į /dev/null cronjobs." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Nukreipkite stderr į /dev/null cronjobs." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Apriboti rsync pralaidumo naudojimą" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Išsaugoti ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Išsaugoti išplestinius atributus (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopijuoti nesaugias nuorodas (veikia tik su absoliučiomis nuorodomis)" #: qt/settingsdialog.py:1024 #, 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:1027 msgid "Paste additional options to rsync" msgstr "Įklijuokite papildomas parinktis į rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "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:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "numatytasis" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Pridėkite priešdėlį prie SSH komandų" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Patikrinkite, ar nuotolinis kompiuteris yra prisijungęs" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Įspėjimas: jei išjungta ir nuotolinis kompiuteris\n" "nepasiekiamas, tai gali sukelti \n" "keistas klaidas." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" "Patikrinkite, ar nuotolinis kompiuteris palaiko visas reikalingas komandas" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "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:1089 msgid "Restore Config" msgstr "Atkurti Config" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Redaguoti naudotojo-galinį iškvietimą" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Naujas profilis" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Pervadinti profilį" #: qt/settingsdialog.py:1142 #, 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:1416 #, 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 "" "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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "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:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Privataus rakto failas \"{file}\" neegzistuoja." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "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:1649 #, fuzzy, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "{host} autentiškumo nustatyti nepavyko.\n" "\n" "{keytype} rakto kontrolinis kodas yra:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Išskirti šabloną" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Išskirti failą" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Išskirti aplanką" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Įtraukti failą" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Įtraukti aplanką" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Ar tikrai norite pakeisti momentinių kopijų aplanką?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Nepavyko sukurti naujo SSH rakto {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Visas momentinės kopijos kelias: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "įjungta" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "išjungta" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Atkurti nustatymus" #: qt/settingsdialog.py:2125 #, fuzzy, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Eikite į momentinę kopiją, iš kurios norite atkurti {appName} konfigūraciją. Kelias gali atrodyti taip:\n" "{samplePath}\n" "\n" "Jei momentinės kopijosyra nuotoliniame diske arba jos užšifruotos, pirmiausia turite jas prijungti rankiniu būdu. Jei naudojate Mode SSH, jums taip pat gali reikėti nustatyti viešojo rakto prisijungimą prie nuotolinio pagrindinio kompiuterio.\n" "Pažvelkite į 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Konfigūracija nerasta" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" "vartotojo atgalinio iškvietimo scenarijus neturi shebang (#!/bin/sh) " "eilutės." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "„Shebang“ vartotojo atgalinio iškvietimo scenarijuje nėra vykdomas." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Komanda" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Parametrai" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "naudoti %1 ir %2 kaip kelio parametrus" #: qt/snapshotsdialog.py:110 #, fuzzy msgid "Differing snapshots only" msgstr "Priimti momentinę kopiją" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Išvardykite tik tokias momentines nuotraukas kaip: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Gilus apžiūrėjimas (tikslesnis, bet lėtesnis)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Ištrint" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Pasirinkti viską" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Eiti į" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "&Parinktys" #: qt/snapshotsdialog.py:330 #, 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:338 msgid "Command not found" msgstr "Komanda nerasta" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Tai negali būti atšaukta!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "DĖMESIO" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Ištraukti \"{path}\" iš momentinių nuotraukų ateityje?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " ir pridėkite savo vartotoją prie grupės 'fuse'" #~ msgid "&Snapshot" #~ msgstr "&Momentinės kopijos" #~ msgid "&View" #~ msgstr "&Rodymas" #~ msgid "..." #~ msgstr "..." #~ msgid "Changes & Errors" #~ msgstr "Pakeitimai ir klaidos" #~ msgid "Config File Help" #~ msgstr "Konfigūracinių failų pagalba" #~ msgid "Diff" #~ msgstr "Pasikeitimai" #~ msgid "Diff Options" #~ msgstr "Pasikeitimų parinktys" #~ msgid "Error:" #~ msgstr "Klaida:" #~ msgid "Every 10 minutes" #~ msgstr "Kas 10 minučių" #~ msgid "Every 5 minutes" #~ msgstr "Kas 5 minutės" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Visiška sistemos atsarginė kopija gali sukurti tik momentinę kopiją, kuri bus atkurta tame pačiame (-iuose) fiziniame (-iuose) diske (-uose) su tuo pačiu disko skaidymu kaip ir šaltinio; atkūrus naujus fizinius diskus arba tuos pačius diskus su skirtingu skaidymu, sistema gali būti sugadinta ir netinkama naudoti.\n" #~ "\n" #~ "Visa sistemos atsarginė kopija nepaisys kai kurių nustatymų, kurie galėjo būti pritaikyti. Tęsti?" #~ msgid "Modify for Full System Backup" #~ msgstr "Modifikuoti pilnai sistemos atsarginei kopijai" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profilis: \"{name}\"" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Atkurti pasirinktą failą arba aplanką.\n" #~ "Jei šis mygtukas yra pilkas, tai greičiausiai todėl, kad kairiajame momentinių kopijų sąraše pasirinkta \"{now}\"." #~ msgid "Settings" #~ msgstr "Nustatymai" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Momentinė kopija: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Žiūrėti esamo disko turinį" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Žiūrėti momentinę kopiją, padarytą {timestamp}" #~ msgid "Working..." #~ msgstr "Dirbama..." #~ msgid "You can't include backup folder!" #~ msgstr "Negalima įtraukti atsarginėms kopijoms skirto aplanko!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Negalima įtraukti atsarginėms kopijoms skirto poaplankio!" #~ msgid "You can't remove the last profile!" #~ msgstr "Negalite ištrinti paskutinio likusio profilio!" backintime-1.4.3/common/po/messages.pot000066400000000000000000000775471455673541400201300ustar00rootroot00000000000000# 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.4.2-dev\"\n" "Report-Msgid-Bugs-To: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 11:25+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "" #: common/config.py:304 msgid "encrypted" msgstr "" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "" #: common/config.py:310 msgid "SSH encrypted" msgstr "" #: common/config.py:317 msgid "Default" msgstr "" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "" #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "" #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "" #: common/config.py:498 msgid "Expert Options" msgstr "" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "" #: common/config.py:1707 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "" #: common/configfile.py:736 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 password" msgstr "" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "" #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "" msgstr[1] "" #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "" #: common/snapshots.py:826 msgid "Finalizing" msgstr "" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "" #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "" #: qt/app.py:167 msgid "Shortcuts" msgstr "" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" #: qt/app.py:453 msgid "Take a snapshot" msgstr "" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "" #: qt/app.py:480 msgid "Name snapshot" msgstr "" #: qt/app.py:484 msgid "Remove snapshot" msgstr "" #: qt/app.py:488 msgid "View snapshot log" msgstr "" #: qt/app.py:492 msgid "View last log" msgstr "" #: qt/app.py:496 msgid "Manage profiles…" msgstr "" #: qt/app.py:500 msgid "Shutdown" msgstr "" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "" #: qt/app.py:512 msgid "Help" msgstr "" #: qt/app.py:516 msgid "Profiles config file" msgstr "" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "" #: qt/app.py:528 msgid "Ask a question" msgstr "" #: qt/app.py:531 msgid "Report a bug" msgstr "" #: qt/app.py:534 msgid "Translation" msgstr "" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "" #: qt/app.py:644 msgid "&Help" msgstr "" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your " "system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "" #: qt/app.py:962 msgid "Working" msgstr "" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "" #: qt/app.py:1051 msgid "Root" msgstr "" #: qt/app.py:1052 msgid "Home" msgstr "" #: qt/app.py:1067 msgid "Backup folders" msgstr "" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "" #: qt/app.py:1202 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before " "restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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:1370 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 msgid "Snapshot" msgstr "" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "" #: qt/qttools.py:370 msgid "Today" msgstr "" #: qt/qttools.py:377 msgid "Yesterday" msgstr "" #: qt/qttools.py:386 msgid "This week" msgstr "" #: qt/qttools.py:393 msgid "Last week" msgstr "" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 msgid "&General" msgstr "" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "" msgstr[1] "" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "" msgstr[1] "" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "" #: qt/settingsdialog.py:383 msgid "Day" msgstr "" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 msgid "Every" msgstr "" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "" #: qt/settingsdialog.py:805 msgid "None" msgstr "" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s " "configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to " "manually mount them first. If you use Mode SSH you also may need to set up " "public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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:66 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "" #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" backintime-1.4.3/common/po/nb.po000066400000000000000000001400521455673541400165120ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-23 09:10+0000\n" "Last-Translator: Brazi \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Advarsel" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Hovedprofil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokal" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "privat SSH-nøkkel" #: common/config.py:304 msgid "encrypted" msgstr "kryptert" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Kryptering" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH kryptert" #: common/config.py:317 msgid "Default" msgstr "Standardverdi" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Øyeblikksbildemappe er ugyldig!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Du må velge minst en mappe å sikkerhetskopiere!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Du kan ikke inkludere sikkerhetskopi-mappen." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Du kan ikke inkludere undermapper av sikkerhetskopi-mappen." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ugyldig valg. {path} er ikke en mappe." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Vert/Bruker/Profil-ID kan ikke være tom." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopier symbolske lenker som filer (og fjern lenkereferansen)" #: common/config.py:498 msgid "Expert Options" msgstr "Avanserte innstillinger" #: common/config.py:502 #, 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 nettverksområde montert med sshfs, men " "sshfs støtter ikke harde lenker. Vennligst bruk modus 'SSH' istedenfor." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Kan ikke finne crontab.\n" "Er du sikker på at cron er installert?\n" "Hvis ikke bør du skru av alle automatiske sikkerhetskopier." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Klarte ikke å skrive ny crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Timeplan udev virker ikke med modusen {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" finnes allerede." #: common/configfile.py:736 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 password" msgstr "Vennligst bekreft passord" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Passordene stemmer ikke overens." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Ta øyeblikksbilde" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan ikke avmontere {mountprocess} fra {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "Finner ikke {}. Vennligst installer for eksempel {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Monteringspunket {} er ikke tomt." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}' Skriv passord for {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "FEILET" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Gjenopprett tillatelser" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Ferdig" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Batteridrift utsetter backup" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Venter %s sekund." msgstr[1] "Venter %s sekunder." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Øyeblikksbilde feilet {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Fullfører" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Kan ikke opprette mappe" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Lagrer konfigurasjonsfil…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Lagrer tillatelser…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Fant resten {snapshot_id} som kan fortsettes." #: common/snapshots.py:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Mappen kan ikke slettes" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Tar øyeblikksbilde" #: common/snapshots.py:1254 msgid "Success" msgstr "Suksess" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' avsluttet med kode {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Se 'man rsync' for flere detaljer" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Ingenting er endret, så nytt øyeblikksbilde er ikke nødvendig" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Kan ikke endre navn fra {new_path} til {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Smart sletting" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Slett gamle øyeblikksbilder" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Forsøk å bevare min ledig plass" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Forsøker å bevare minst {perc} ledige inodes" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Nå" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan ikke montere {sshfs}" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Chiffer {cipher} feilet for {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Fjern-katalogbanen finnes, men er ikke en mappe." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Fjern-katalogbanen er ikke skrivbar." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Fjern-katalogbanen er ikke kjørbar." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Kan ikke opprette ekstern bane." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Ekstern vert {host} støtter ikke {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Slå opp 'man backintime' for videre instruksjoner" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Kommandosjekk på verten {host} returnerte en ukjent feil" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Fjern-verten {host} støtter ikke harde lenker" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Vennligst skriv inn passord for \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Snarveier" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Mappen finnes ikke\n" "i det valgte øyeblikksbildet." #: qt/app.py:252 msgid "Add to Include" msgstr "Legg til for å inkludere" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Legg til for å ekskludere" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} er ikke konfigurerert. Vil du legge tilbake en tidligere " "konfigurasjon?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "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:453 msgid "Take a snapshot" msgstr "Ta et øyeblikksbilde" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "Bruk endringstid og størrelse for å spore fil-endringer." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Ta et øyeblikksbilde (sjekksum-modus)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Bruk sjekksummer for å spore filendringer." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Sett øyeblikksbilde-prosessen på pause" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Fortsett øyeblikksbilde-prosessen" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Stopp øyeblikksbilde-prosessen" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Oppdaterer listen over øyeblikksbilder" #: qt/app.py:480 msgid "Name snapshot" msgstr "Gi navn til øyeblikksbilde" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Fjern øyeblikksbilde" #: qt/app.py:488 msgid "View snapshot log" msgstr "Vis logg for øyeblikksbilde" #: qt/app.py:492 msgid "View last log" msgstr "Vis siste logg" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Administrer profiler…" #: qt/app.py:500 msgid "Shutdown" msgstr "Skru av" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Skrur av systemet etter at øyeblikksbildet er ferdig." #: qt/app.py:504 msgid "Setup language…" msgstr "Velg språk…" #: qt/app.py:508 msgid "Exit" msgstr "Avslutt" #: qt/app.py:512 msgid "Help" msgstr "Hjelp" #: qt/app.py:516 msgid "Profiles config file" msgstr "Profilens konfigurasjonsfil" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Nettside" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Endringslogg" #: qt/app.py:525 msgid "FAQ" msgstr "Ofte stilte spørsmål" #: qt/app.py:528 msgid "Ask a question" msgstr "Still et spørsmål" #: qt/app.py:531 msgid "Report a bug" msgstr "Rapporter en feil" #: qt/app.py:534 msgid "Translation" msgstr "Oversettelse" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Om" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Gjennopprett" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "Gjenopprett valgte filer og mapper til opprinnelig sted." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Gjennopprett til …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "Gjenopprett valgte filer og mapper til et nytt sted." #: qt/app.py:552 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:557 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:560 msgid "Up" msgstr "Opp" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Vis skjulte filer" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Sammenlign øyeblikksbilder…" #: qt/app.py:627 msgid "&Backup" msgstr "&Sikkerhetskopi" #: qt/app.py:638 msgid "&Restore" msgstr "&Gjennopprett" #: qt/app.py:644 msgid "&Help" msgstr "&Hjelp" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" 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:905 msgid "Working:" msgstr "Arbeider:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Ferdig, sikkerhetskopiering trengs ikke" #: qt/app.py:962 msgid "Working" msgstr "Arbeider" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Feil" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Sendt" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Hastighet" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Forventet ferdig" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Rot" #: qt/app.py:1052 msgid "Home" msgstr "Hjem" #: qt/app.py:1067 msgid "Backup folders" msgstr "Øyeblikksbilde mapper" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Navn på øyeblikksbilde" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Fjern elementer som er nyere i den originale mappen." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "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:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "ADVARSEL: Å slette filer i root-filsystemet kan ødelegge hele systemet ditt!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Øyeblikksbilde" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Gjenopprett {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Gjennopprett {path} til …" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Forfattere" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Oversettelser" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Lisens" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Innstalleringsspråk" #: qt/languagedialog.py:87 msgid "System default" msgstr "Standardverdi for systemet" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Bruk samme språk som operativsystemet." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Oversatt: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "oversettelses-plattform" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Din oversettelse" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Se seneste logg" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Se logg for øyeblikksbilde" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Øyeblikksbilder" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Alt" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Endringer" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Feil" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informasjon" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Feil, [I] Informasjon, [C] Endring" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "dokode stier" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopi" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Dekode" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Vil du ekskludere dette?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Spørsmål" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Vis siste logg" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Start {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Arbeider…" #: qt/qttools.py:370 msgid "Today" msgstr "Idag" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Igår" #: qt/qttools.py:386 msgid "This week" msgstr "Denne uken" #: qt/qttools.py:393 msgid "Last week" msgstr "Forrige uke" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Sist sjekket {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Vis hele loggen" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Administrer profiler" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Rediger" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Legg til" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Fjern" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Generelt" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Modus" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Hvor lagre øyeblikksbilder" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Mappe" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH-innstillinger" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Tjener" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Bruker" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Sti" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Chiffer" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Privat SSH-nøkkel" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Passord" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Lagre passord til nøkkelringen" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Avansert" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Fullstendig sti til øyeblikksbilde" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Timeplan" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Deaktivert" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "For hver start/omstart" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Hvert minutt" msgstr[1] "Hvert {n}. minutt" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Hver time" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Hver time" msgstr[1] "Hver {n}. time" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "Velg timer" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Hver dag" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Gjentakende (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Når lagringsenheten kobles til (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Hver uke" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Hver måned" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Hvert år" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Dag" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Ukedag" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Time" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Timer" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Hver" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Time(r)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dag(er)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Uke(r)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Måned(er)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Inkluder" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Inkluder filer og mapper" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Legg til fil" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Legg til katalog" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Ekskludér" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Wildcards ({example1}) vil bli ignorert i modus 'SSH encrypted'\n" "Kun enkel eller dobbel asterisk er lov ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Ekskluderingsmønstre, filer og mapper" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Svært anbefalt" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Legg til standardmønstre" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Ekskluder filer som er større enn: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." 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:616 msgid "&Auto-remove" msgstr "Automatisk &fjerning" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Eldre enn" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "År" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Hvis det er mindre ledig plass enn" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Hvis ledige inoder er færre enn" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Smart sletting:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Kjør i bakgrunnen på den eksterne verten." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTELT" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Behold alle øyeblikksbilder tatt de siste" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "dag(ene)." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Behold ett øyeblikksbilde pr dag for de siste" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Behold ett øyeblikksbilde pr uke for de siste" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "uke(ene)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Behold ett øyeblikksbilde pr måned for de siste" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "måned(ene)." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Behold ett øyeblikksbilde pr år for alle år." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Ikke fjern navngitte øyeblikksbilder." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Alternativer" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Slå på meldinger" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Slå av funksjonen snapshot ved bateridrift" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Batteristatus er ikke tilgjengelig fra systemet" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Ta kun ett øyeblikksbilde om gangen" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "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:772 msgid "Backup replaced files on restore" msgstr "Ta sikkerhetskopi av filer som blir overskrevet av gjenoppretting" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" "Fortsett når feil oppstår (behold øyeblikksbilder som ikke er komplette)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Bruk sjekksum for å oppdage endringer" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Logg-nivå" #: qt/settingsdialog.py:805 msgid "None" msgstr "Ingen" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "E&kspert-innstillinger" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Kjør 'rsync' med '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "som cron-jobb" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "på ekstern vert" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "når en lager et øyeblikksbilde manuelt" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Vennligst installer 'nocache' for å slå på denne opsjonen)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "på lokal maskin" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Rediriger stdout til /dev/null i cron-jobber." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Rediriger stderr til /dev/null i cron-jobber." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Begrens båndbredden for rsync til" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/sek" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Behold ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Behold utvidede attributter (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopier usikre lenker (virker kun på absolutte lenker)" #: qt/settingsdialog.py:1024 #, 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:1027 msgid "Paste additional options to rsync" msgstr "Lim inn tilleggsopsjoner til rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "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:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "standard" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Legg til et prefiks for SSH-kommandoer" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Sjekk om ekstern vert er oppe" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "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:1071 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:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "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:1089 msgid "Restore Config" msgstr "Gjenopprett konfigurasjon" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Endre user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Ny profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Omdøp profil" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "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:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Privatnøkkel-fil \"{file}\" finnes ikke." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "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:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Ektheten til den eksterne verten {host} kan ikke slås fast.\n" "\n" "{keytype}-nøkkelen sitt fingeravtrykk er:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Utelukk mønster" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Ekskluder fil" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Ekskludér katalog" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Inkluder fil" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Inkludér katalog" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Er du sikker på at vil bytte mappe for øyeblikksbilder?" #: qt/settingsdialog.py:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Fullstendig øyeblikksbilde-bane: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "påslått" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "avslått" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Gjenopprett innstillinger" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Vennligst naviger til øyeblikksbildet du ønsker å gjenopprette konfigurasjone til {appName} fra. Stien kan se ut som:\n" "{samplePath}\n" "\n" "Hvis øyeblikksbildene befinner seg opp en ekstern lagringsenhet eller er kryptert må du montere dem manuelt først. Hvis du bruker SSH-modus er det mulig at du også må sette opp nøkkelinnlogging til den eksterne verten.\n" "Ta en titt på 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Fant ingen konfigurasjon" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "user-callback-skriptet har ingen shebang (#!/bin/sh)-linje." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang i user-callback-skriptet er ikke eksekverbart." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Bruk %1 and %2 for path parameters" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Kun øyeblikksbilder med forskjeller" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Liste med kun like øyeblikksbilder til: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Dypsjekk (mer nøyktig, men tregt)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Slett" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Velg alle" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Sammenlign" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Gå Til" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Alternativer" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Du kan ikke sammenlikne et øyeblikksbilde med seg selv." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Fant ikke kommando" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Dette kan ikke gjøres om!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ADVARSEL" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Utelukk {path} fra fremtidige øyeblikksbilder?" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Ø&yeblikksbilder" #~ msgid "..." #~ msgstr "…" #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Diff" #~ msgstr "Diff" #~ msgid "Diff Options" #~ msgstr "Diff Valg" #~ msgid "Every 10 minutes" #~ msgstr "Hvert 10. minutt" #~ msgid "Every 12 hours" #~ msgstr "Hver tolvte time" #~ msgid "Every 30 minutes" #~ msgstr "Hvert 30. minutt" #~ msgid "Every 4 hours" #~ msgstr "Hver fjerde time" #~ msgid "Every 5 minutes" #~ msgstr "Hvert 5. minutt" #~ msgid "Every 6 hours" #~ msgstr "Hver sjette time" #~ msgid "Local encrypted" #~ msgstr "Lokal kryptert" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Innstillinger" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Øyeblikksbilde: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Se nåværende stasjons innhold" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Se øyeblikksbilde laget den {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "MED FEIL !" #~ msgid "Working..." #~ msgstr "Arbeider …" #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Du kan ikke inkludere sikkerhetskopi mappen !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Du kan ikke inkludere en sikkerhetskopi under-mappe !" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Du kan ikke fjerne den siste profilen!" backintime-1.4.3/common/po/nl.po000066400000000000000000001527221455673541400165330ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-24 11:34+0000\n" "Last-Translator: buhtz \n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Waarschuwing" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Hoofdprofiel" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokaal" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH-privésleutel" #: common/config.py:304 msgid "encrypted" msgstr "versleuteld" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Versleuteling" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH-versleuteld" #: common/config.py:317 msgid "Default" msgstr "Standaardwaarde" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profiel: “{name}”" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Momentopname-folder is ongeldig!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Selecteer tenminste één map om een back-up te kunnen maken!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Backup-map kan niet worden opgenomen." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Back-up-submap kan niet worden opgenomen." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ongeldige optie. {path} is geen map." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/Gebruiker/Profiel-ID mag niet leeg zijn." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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 " "Linux-bestandssysteem." #: common/config.py:493 #, 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 met SMB aangekoppelde netwerkopslag." " Zorg er voor dat de externe SMB-server symbolische koppelingen ondersteunt " "of activeer ‘{copyLinks}’ in ‘{expertOptions}’." #: common/config.py:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Koppelingen kopiëren (symbolische koppelingen-dereferentie)" #: common/config.py:498 msgid "Expert Options" msgstr "Geavanceerde opties" #: common/config.py:502 #, 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 met sshfs aangekoppelde netwerkopslag. " "sshfs ondersteunt geen harde koppelingen. Gebruik hiervoor in de plaats " "a.u.b. de 'SSH-modus'." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Kan crontab niet vinden.\n" "Weet u zeker dat cron geïnstalleerd is?\n" "Indien dit niet het geval is, dient u het maken van automatische reservekopieën uit te schakelen." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Kon geen nieuwe crontab schrijven." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Methode udev werkt niet bij modus {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profiel \"{name}\" bestaat al." #: common/configfile.py:736 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 password" msgstr "Bevestig a.u.b. wachtwoord" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Wachtwoord komt niet overeen." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Momentopname maken" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan {mountprocess} niet ontkoppelen van {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} niet gevonden. Installeer b.v. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Koppelpunt {} niet leeg." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profiel '{profile}': Voer wachtwoord in voor {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "MISLUKT" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Rechten herstellen" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Voltooid" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Back-up uitstellen indien op batterij" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Wacht %s seconde." msgstr[1] "Wacht %s seconden." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Kon momentopname {snapshot_id} niet maken." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Voltooien" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Kan map niet aanmaken" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Configuratiebestand opslaan…" #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Rechten opslaan…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Restant van '{snapshot_id}' gevonden waarmee u verder kunt gaan." #: common/snapshots.py:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Kan map niet verwijderen" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Momentopname wordt gemaakt" #: common/snapshots.py:1254 msgid "Success" msgstr "Succes" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Gedeeltelijke overdracht vanwege verdwenen bronbestanden (zie 'man rsync')" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' eindigde met exitcode {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Zie 'man rsync' voor meer informatie" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Er is niets veranderd, geen nieuwe momentopname nodig" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Kan {new_path} niet hernoemen naar {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Slim verwijderen" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Oude momentopnames verwijderen" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Een minimum aan vrije ruimte proberen te houden" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Een minimum van {perc} vrije inodes proberen te houden" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Nu" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan {sshfs} niet aankoppelen" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Codering {cipher} mislukt voor {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Extern pad bestaat maar is geen map." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Extern pad is niet beschrijfbaar." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Extern pad is niet uitvoerbaar." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Kon extern pad niet aanmaken." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Externe host {host} ondersteunt {command} niet" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Raadpleeg 'man backintime' voor verdere instructies" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Controlecommando's op host {host} gaven onbekende foutmelding" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Externe host {host} ondersteunt geen harde koppelingen" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "Kopieer de openbare SSH-sleutel \"{pubkey}\" naar externe host \"{host}\"" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Voer het wachtwoord in voor \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Snelkoppelingen" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Deze map bestaat niet\n" "in de huidige geselecteerde momentopname." #: qt/app.py:252 msgid "Add to Include" msgstr "Toevoegen aan Opnemen" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Toevoegen aan Uitsluiten" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} is niet geconfigureerd. Wilt u een eerdere configuratie " "herstellen?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Kan momentopnamemap niet vinden.\n" "Als deze op een verwijderbaar medium staat, gelieve dit aan te sluiten en op OK te drukken." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Een momentopname maken" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" "Modificatietijd en grootte gebruiken voor detectie van bestandswijzigingen." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Momentopname maken (modus controlegetal)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Controlegetallen gebruiken voor het opsporen van bestandswijzigingen." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Maken van momentopname pauzeren" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Maken van momentopname hervatten" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Maken van momentopname stoppen" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Momentopnamelijst vernieuwen" #: qt/app.py:480 msgid "Name snapshot" msgstr "Momentopname benoemen" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Momentopname verwijderen" #: qt/app.py:488 msgid "View snapshot log" msgstr "Momentopnamelogboek bekijken" #: qt/app.py:492 msgid "View last log" msgstr "Laatste logboek bekijken" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Profielen beheren" #: qt/app.py:500 msgid "Shutdown" msgstr "Uitschakelen" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Computer uitschakelen wanneer de momentopname voltooid is." #: qt/app.py:504 msgid "Setup language…" msgstr "Taal instellen…" #: qt/app.py:508 msgid "Exit" msgstr "Afsluiten" #: qt/app.py:512 msgid "Help" msgstr "Hulp" #: qt/app.py:516 msgid "Profiles config file" msgstr "Profielconfiguratiebestand" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Website" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Wijzigingslogboek" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ" #: qt/app.py:528 msgid "Ask a question" msgstr "Een vraag stellen" #: qt/app.py:531 msgid "Report a bug" msgstr "Een fout melden" #: qt/app.py:534 msgid "Translation" msgstr "Vertaling" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Info" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Herstellen" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Herstellen naar …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Omhoog" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Verborgen bestanden tonen" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Momentopnames vergelijken" #: qt/app.py:627 msgid "&Backup" msgstr "&Back-up maken" #: qt/app.py:638 msgid "&Restore" msgstr "&Herstellen" #: qt/app.py:644 msgid "&Help" msgstr "&Hulp" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Als u dit venster sluit, zal Back In Time de computer niet uit kunnen schakelen wanneer de momentopname voltooid is.\n" "Weet u zeker dat u het wilt sluiten?" #: qt/app.py:905 msgid "Working:" msgstr "Bezig:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Klaar, geen back-up nodig" #: qt/app.py:962 msgid "Working" msgstr "Bezig" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Fout" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Verzonden" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Snelheid" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Geschatte afhandelingstijd" #: qt/app.py:1050 msgid "Global" msgstr "Algemeen" #: qt/app.py:1051 msgid "Root" msgstr "Hoofdmap" #: qt/app.py:1052 msgid "Home" msgstr "Persoonlijke map" #: qt/app.py:1067 msgid "Backup folders" msgstr "Back-upmappen" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Naam momentopname" #: qt/app.py:1202 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 de momentopname wilt verwijderen?" msgstr[1] "Weet u zeker dat u de momentopnames wilt verwijderen?" #: qt/app.py:1294 #, fuzzy, 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" "u lokale bestanden overschrijft of verwijdert." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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.\n" "Als u ze niet meer nodig heeft kunt u ze verwijderen met {cmd}" #: qt/app.py:1314 msgid "" "Only restore elements which do not exist or\n" "are newer than those in destination.\n" "Using \"rsync --update\" option." msgstr "" "Alleen bestanden terugzetten die niet bestaan of\n" "nieuwer zijn dan die in de bestemming.\n" "De optie \"rsync --update\" wordt gebruikt." #: qt/app.py:1347 msgid "Remove newer elements in original folder." msgstr "Nieuwere bestanden in oorspronkelijke map verwijderen." #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Geselecteerde bestanden of mappen naar de oorspronkelijke bestemming herstellen\n" "en bestanden/mappen die niet in de momentopname voorkomen, verwijderen.\n" "Wees hier uiterst voorzichtig mee!\n" "Hierdoor worden bestanden/mappen verwijderd die waren uitgesloten tijdens het maken van de momentopname!" #: qt/app.py:1361 #, 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 het bestand wilt herstellen\n" "naar de nieuwe map {path}?" msgstr[1] "" "Weet u zeker dat u de bestanden wilt herstellen\n" "naar de nieuwe map {path}?" #: qt/app.py:1370 #, fuzzy 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 het/de bestand(en) wilt herstellen" msgstr[1] "Weet u zeker dat u het/de bestand(en) wilt herstellen" #: qt/app.py:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "WAARSCHUWING: bestanden verwijderen uit de hoofdmap van het bestandssysteem " "kan uw hele systeem ruïneren!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Momentopname" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "{path} herstellen" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "{path} herstellen naar …" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Auteurs" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Vertalingen" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licentie" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Taal instellen" #: qt/languagedialog.py:87 msgid "System default" msgstr "Systeemstandaard" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "De taal van het besturingssysteem gebruiken." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Vertaald: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "vertaalplatform" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Uw vertaling" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Laatste logboekweergave" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Weergave momentopnamelogboek" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profiel" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Momentopnames" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Alles" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Wijzigingen" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Fouten" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informatie" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Fout, [I] Informatie, [C] Wijziging" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "paden decoderen" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopiëren" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Decoderen" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Wilt u dit uitsluiten?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Vraag" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Laatste logboek bekijken" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "{appname} starten" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Bezig…" #: qt/qttools.py:370 msgid "Today" msgstr "Vandaag" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Gisteren" #: qt/qttools.py:386 msgid "This week" msgstr "Deze week" #: qt/qttools.py:393 msgid "Last week" msgstr "Vorige week" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Laatste controle {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Volledig logboek tonen" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Profielen beheren" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Bewerken" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Toevoegen" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Verwijderen" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Algemeen" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Modus" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Waar wilt u de momentopnames opslaan" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Map" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH-instellingen" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Host" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Poort" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Gebruiker" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Pad" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Versleutelingstype" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Privésleutel" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Wachtwoord" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Wachtwoord toevoegen aan sleutelbos" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Geavanceerd" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Volledig momentopnamepad" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Planning" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Uitgeschakeld" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Bij elke opstart/herstart" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Iedere minuut" msgstr[1] "Iedere {n} minuten" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Ieder uur" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Iedere {n} uur" msgstr[1] "Iedere {n} uur" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Aangepaste tijden" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Iedere dag" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Herhaaldelijk (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Zodra de schijf is aangesloten (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Iedere week" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Iedere maand" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Ieder jaar" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Dag" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Dag van de week" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Uur" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Uren" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Elk(e)" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Uur/Uren" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dag(en)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Week/Weken" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Maand(en)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Opnemen" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Bestanden en mappen opnemen" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Bestand toevoegen" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Map toevoegen" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Uitsluiten" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Jokertekens ({example1}) worden genegeerd in de modus 'SSH-versleuteld'.\n" "Er zijn alleen enkele of dubbele sterretjes toegestaan ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Patronen, bestanden of mappen uitsluiten" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Sterk aanbevolen" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Standaard toevoegen" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Bestanden uitsluiten die groter zijn dan: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Bestanden uitsluiten die groter zijn dan de waarde in %(prefix)s.\n" "Als 'Volledige rsync-modus' is uitgeschakeld, heeft dit alleen invloed op nieuwe bestanden,\n" "omdat dit voor rsync een overdrachtsoptie is en geen uitsluitingsoptie.\n" "Grote bestanden waarvan al eerder een back-up is gemaakt, blijven dus in momentopnames,\n" "zelfs als ze zijn gewijzigd." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "Automatisch &verwijderen" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Ouder dan" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Jaar" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Als de vrije ruimte kleiner is dan" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Als er minder inodes vrij zijn dan" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Slim verwijderen" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Op de achtergrond uitvoeren op externe host." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EXPERIMENTEEL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Alle momentopnames bewaren van de laatste" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "dag(en)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Eén momentopname per dag bewaren van de laatste" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Eén momentopname per week bewaren van de laatste" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "week/weken" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Eén momentopname per maand bewaren van de laatste" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "maand(en)" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Eén momentopname per jaar bewaren van alle jaren" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Genoemde momentopnames niet verwijderen" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "O&pties" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Meldingen activeren" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Momentopnames uitschakelen bij batterijgebruik" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Voedingsstatus niet beschikbaar via systeem" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Slechts één momentopname tegelijk uitvoeren" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Andere momentopnames worden geblokkeerd totdat de huidige momentopname is voltooid.\n" "Dit is een globale optie. Ze heeft dus invloed op alle profielen van deze gebruiker.\n" "Maar u moet dit ook voor alle andere gebruikers activeren." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Back-up maken van vervangen bestanden bij herstel" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Doorgaan bij fouten (onvolledige momentopnames bewaren)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Controlegetal gebruiken om wijzigingen te detecteren" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Logboekniveau" #: qt/settingsdialog.py:805 msgid "None" msgstr "Geen" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "&Geavanceerde opties" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "'rsync' uitvoeren met '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "als crontaak" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "op externe host" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "bij het maken van een handmatige momentopname" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Installeer 'nocache' om deze optie in te schakelen)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "op de lokale machine" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "stdout omleiden naar /dev/null in crontaken." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "stderr omleiden naar /dev/null in crontaken." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Het bandbreedtegebruik van rsync beperken" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "ACL behouden" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Uitgebreide attributen (xattr) behouden" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Onveilige koppelingen kopiëren (werkt alleen met absolute koppelingen)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opties moeten tussen aanhalingstekens staan, bijvoorbeeld {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Extra opties aan rsync toevoegen" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Voorvoegsel dat vóór elke opdracht op de externe host moet worden uitgevoerd.\n" "Variabelen moeten worden gemaskeerd met \\$FOO\n" "Dit heeft geen betrekking op rsync. Dus om een voorvoegsel\n" "voor rsync toe te voegen gebruikt u \"%(cbRsyncOptions)s\" met\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "standaard" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Voorvoegsel aan SSH-commando's toevoegen" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Controleren of de externe host online is" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Waarschuwing: als dit is uitgeschakeld en de\n" "externe host niet beschikbaar is,\n" "kan dit leiden tot rare fouten." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Controleren of de externe host alle benodigde commando's ondersteunt" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Waarschuwing: als dit is uitgeschakeld en de\n" "externe host niet alle benodigde commando's\n" "ondersteunt, kan dit leiden tot rare fouten." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Configuratie herstellen" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "user-callback bewerken" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nieuw profiel" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Profiel hernoemen" #: qt/settingsdialog.py:1142 #, 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:1416 #, 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 "" "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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "U heeft geen privésleutelbestand voor SSH gekozen.\n" "Wilt u een nieuw wachtwoordloos publiek/privé sleutelpaar genereren?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Privésleutelbestand \"{file}\" bestaat niet." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Wilt u uw openbare SSH-sleutel naar de externe host kopiëren\n" "om inloggen zonder wachtwoord mogelijk te maken?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "De authenticiteit van host {host} kan niet worden vastgesteld.\n" "\n" "{keytype}-sleutelvingerafdruk is:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Patroon uitsluiten" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Bestand uitsluiten" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Map uitsluiten" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Bestand opnemen" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Map opnemen" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Weet u zeker dat u de momentopnamemap wilt wijzigen?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Kan geen nieuwe SSH-sleutel aanmaken in {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Volledig momentopnamepad: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "ingeschakeld" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "uitgeschakeld" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Instellingen herstellen" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Navigeer naar de momentopname van waaruit u de configuratie van {appName} wilt herstellen. Het pad kan er als volgt uitzien:\n" "{samplePath}\n" "\n" "Als uw momentopnames op een externe schijf staan of als ze versleuteld zijn, moet u ze eerst handmatig aankoppelen. Als u Mode SSH gebruikt, moet u mogelijk ook een publieke sleutel instellen om in te loggen op de externe host.\n" "Bekijk eens 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Geen configuratie gevonden" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "user-callback-script heeft geen shebang-regel (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang in user-callback-script is niet uitvoerbaar." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "%1 en %2 voor padparameters gebruiken" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Alleen verschillende momentopnames" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Alleen momentopnames tonen identiek aan: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Diepe controle (nauwkeuriger, maar langzaam)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Wissen" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Alles selecteren" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Vergelijken" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Gaan naar" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Opties" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "U kunt een momentopname niet met zichzelf vergelijken." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Commando niet gevonden" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Dit kan niet worden teruggedraaid!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "WAARSCHUWING" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "{path} uitsluiten van toekomstige momentopnames?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " en uw gebruiker toe te voegen aan de groep 'fuse'" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "\"%s\" is een symbolische link. Er zal geen reservekopie gemaakt worden van het gekoppelde doel totdat u het doel zelf ook opneemt.\n" #~ "Wilt u i.p.v. de symbolische link het doel zelf opnemen in de reservekopie?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Dit logboek is gedecodeerd met automatisch zoekpatroon\n" #~ "### Als sommige paden niet gedecodeerd zijn dan kunt u deze handmatig decoderen met:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s is geen lid van de groep 'fuse'.\n" #~ " Voer de opdracht 'sudo adduser %(user)s fuse' uit. Om de wijzigingen toe te passen dient u zich eerst af te melden en daarna weer aan te melden.\n" #~ "Bekijk de 'Back In Time-handleiding' voor verdere instructies." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s is niet gevonden in de ssh_bekende_gastcomputers." #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Reservekopie" #, fuzzy #~ msgid "&View" #~ msgstr "Beeld" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Wijzigingen & fouten" #~ msgid "Config File Help" #~ msgstr "Configuratiebestand-hulp" #~ msgid "Create a new SSH key without Password." #~ msgstr "Maak een nieuwe SSH-sleutel aan zonder wachtwoord." #~ msgid "Diff" #~ msgstr "Vergelijken met" #~ msgid "Diff Options" #~ msgstr "Vergelijkopties" #~ msgid "Error:" #~ msgstr "Fout:" #~ msgid "Every 10 minutes" #~ msgstr "Elke 10 minuten" #~ msgid "Every 12 hours" #~ msgstr "Elke 12 uren" #~ msgid "Every 30 minutes" #~ msgstr "Elke 30 minuten" #~ msgid "Every 4 hours" #~ msgstr "Elke 4 uren" #~ msgid "Every 5 minutes" #~ msgstr "Elke 5 minuten" #~ msgid "Every 6 hours" #~ msgstr "Elke 6 uren" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Wanneer u met Volledige systeemreservekopie een reservekopie gaat maken zal deze alleen werken wanneer u deze herstelt naar dezelfde schijf/schijven en met dezelfde schijfpartitionering als de bron waar de reservekopie van wordt gemaakt; herstellen naar een andere schijf of dezelfde schijf maar met een andere partitionering zal mogelijk een gebroken en onbruikbaar systeem als gevolg hebben.\n" #~ "\n" #~ "Volledige systeemreservekopie zal enige instellingen die mogelijk aangepast zijn opheffen. Wilt u doorgaan?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Er heeft een Hash collision plaatsgevonden in hash_id %s. Vergroot de " #~ "globale waarde voor hash_collision en probeer het opnieuw." #~ msgid "Key File" #~ msgstr "Sleutelbestand" #~ msgid "List only different snapshots" #~ msgstr "Alleen reservekopieën tonen die verschillen" #~ msgid "Local encrypted" #~ msgstr "Lokaal versleuteld" #~ msgid "Modify for Full System Backup" #~ msgstr "Aanpassen voor volledige systeemreservekopie" #~ msgid "Mountprocess lock timeout" #~ msgstr "Time-out aankoppelprocesvergrendeling" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "Authenticatie aanmelden zonder wachtwoord voor %(user)s@%(host)s is mislukt." #~ " Bekijk de 'Back In Time-handleiding' voor verdere instructies." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "" #~ "Pingen van %s is mislukt. Gastcomputer is uitgeschakeld of er is een " #~ "verkeerd adres gebruikt." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profiel: “{name}”" #, python-format #~ msgid "Restore '%s'" #~ msgstr "'%s' herstellen" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "'%s' herstellen naar..." #, fuzzy, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Geselecteerd bestand of map herstellen.\n" #~ "Wanneer deze knop grijs gedimd is komt dat hoogstwaarschijnlijk omdat \"{now}\" is geselecteerd in de reservekopielijst aan de linkerkant." #~ msgid "Run 'ionice':" #~ msgstr "'ionice' uitvoeren:" #~ msgid "Run 'nice':" #~ msgstr "'nice' uitvoeren:" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "'rsync' uitvoeren met 'nocache':" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "'rsync' uitvoeren met 'nocache':" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Instellingen" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Reservekopie: %s" #~ msgid "View the current disk contents" #~ msgstr "De huidige schijfinhoud bekijken" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "De momentopname gemaakt op {timestamp} bekijken" #~ msgid "WITH ERRORS !" #~ msgstr "MET FOUTEN!" #~ msgid "Working..." #~ msgstr "Bezig met..." #~ msgid "You can't include backup folder!" #~ msgstr "U kunt de reservekopiemap niet toevoegen!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "U kunt geen onderliggende map van de reservekopiemap toevoegen!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "U kunt het laatste profiel niet verwijderen!" backintime-1.4.3/common/po/nn.po000066400000000000000000001202211455673541400165220ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-24 11:34+0000\n" "Last-Translator: vidarlo \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Åtvaring" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Hovudprofil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokal" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "privat SSH-nøkkel" #: common/config.py:304 msgid "encrypted" msgstr "kryptert" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Kryptering" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH-kryptert" #: common/config.py:317 msgid "Default" msgstr "Standardverdi" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Mappa for augneblinksbileta er ugyldig!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Du må velja minst éi mappe for sikkerheitskopiering!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Du kan ikkje inkludere sikkerheitskopierings-mappa." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Du kan ikkje inkludere undermapper av sikkerheitskopierings-mappa." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Ugyldig val. {path} er ikkje ei mappe." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Tenar/Brukar/Profil-ID kan ikkje vere tom." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopier symbolske lenkjer som filer (og fjern lenkjereferansen)" #: common/config.py:498 msgid "Expert Options" msgstr "Avanserte innstillingar" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Klara ikkje å skriva ny crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Timeplan udev verker ikkje med modusen {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilen \"{name}\" finst allereie." #: common/configfile.py:736 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 password" msgstr "Ver venleg å stadfesta passordet" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Passordene stemmer ikkje overeins." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Ta augneblinksbilete" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan ikkje avmontere {mountprocess} frå {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "Fann ikkje {}. Ver venleg å installere t.d. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Monteringspunktet {} er ikkje tomt." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}': Oppgi passord for {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "FEILA" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Gjennopprett tillatelsar" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Fullført" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Ventar med sikkerhetskopi medan på batteri" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Ventar %s sekund." msgstr[1] "Ventar %s sekund." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Klarte ikkje å ta augneblinksbilete {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Sluttfører" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Klarer ikkje å laga mappa" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Lagre oppsettsfila…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Lagre løyve …" #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Klarer ikkje å fjerna mappa" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Ta augneblinksbilete" #: common/snapshots.py:1254 msgid "Success" msgstr "Suksess" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' avslutta med kode {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Sjå 'man rsync' for fleire detaljar" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Ingenting endra, nytt augeblinksbilete ikkje påkrevd" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Kan ikkje omdøype {new_path} til {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Smart-sletting" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Fjern gamle augneblinksbilete" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Prøver å oppretthalde minimum ledig plass" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Prøver å halde minimum {perc} ledige inodar" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "No" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan ikkje montere {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "Fann ikkje ssh-agent. Sjekk at det er installert." #: common/sshtools.py:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Kryptoalgoritmen {cipher} feila for {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Fjern sti finst, men er ikkje ei mappe." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Fjern sti er ikkje skrivbar." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Fjern sti er ikkje køyrbar." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Klarer ikkje å laga mappa på det andre systemet." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Maskina {host} støttar ikkje {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Les 'man backintime' for viare instruksar" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Testkommando på maskina {host} returnerte ukjend feil" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Maskina {host} støttar ikkje hardlenker" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Oppgi passord for \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Snarvegar" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Legg til for å inkludere" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Legg til for å ekskludere" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} er ikkje sett opp. Vil du gjenopprette ein tidlegare " "konfigurasjon?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "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:453 msgid "Take a snapshot" msgstr "Taka eit augneblinksbilete" #: qt/app.py:455 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:458 msgid "Take a snapshot (checksum mode)" msgstr "Ta eit augneblinksbilete (sjekksum-modus)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Bruk sjekksummar for å oppdage endringar." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Set augneblinksbilete-prosessen på pause" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Taka opp att augneblinksbilete-prosessen" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Stansa augneblinksbilete-prosessen" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Oppdater lista over augneblinksbilete" #: qt/app.py:480 msgid "Name snapshot" msgstr "Namngjeva augneblinksbilete" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Fjern augneblinksbilete" #: qt/app.py:488 msgid "View snapshot log" msgstr "Vis logg for augneblinksbilete" #: qt/app.py:492 msgid "View last log" msgstr "Vis siste logg" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Administrer profilar…" #: qt/app.py:500 msgid "Shutdown" msgstr "Slå av" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Slår av systemet etter at augneblinksbilete er ferdig." #: qt/app.py:504 msgid "Setup language…" msgstr "Sett opp språk…" #: qt/app.py:508 msgid "Exit" msgstr "Avslutt" #: qt/app.py:512 msgid "Help" msgstr "Hjelp" #: qt/app.py:516 msgid "Profiles config file" msgstr "Profilens konfig-fil" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Nettstad" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Endringslogg" #: qt/app.py:525 msgid "FAQ" msgstr "Vanlege spørsmål" #: qt/app.py:528 msgid "Ask a question" msgstr "Still eit spørsmål" #: qt/app.py:531 msgid "Report a bug" msgstr "Rapporter ein feil" #: qt/app.py:534 msgid "Translation" msgstr "Oversetning" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Om" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Før tilbake" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Før tilbake til …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Opp" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Vis gøymde filer" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Jamstill augneblinksbilete…" #: qt/app.py:627 msgid "&Backup" msgstr "&Sikkerheitskopier" #: qt/app.py:638 msgid "&Restore" msgstr "Før &tilbake" #: qt/app.py:644 msgid "&Help" msgstr "&Hjelp" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" 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:905 msgid "Working:" msgstr "Arbeider:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Ferdig, trong ikkje tryggleikskopiera" #: qt/app.py:962 msgid "Working" msgstr "Arbeider" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Feil" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Sendt" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Hastigheit" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Forventa avslutta" #: qt/app.py:1050 msgid "Global" msgstr "Globalt" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Heim" #: qt/app.py:1067 msgid "Backup folders" msgstr "Kopieringsmapper" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Biletnamn" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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:1370 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Vil du verkeleg endra biletmappa" msgstr[1] "Vil du verkeleg endra biletmappa" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Augneblinksbilete" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Før tilbake {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Før tilbake {path}" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Omsetjingar" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Omsett: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Augneblinksbilete" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Arbeider…" #: qt/qttools.py:370 msgid "Today" msgstr "I dag" #: qt/qttools.py:377 msgid "Yesterday" msgstr "I går" #: qt/qttools.py:386 msgid "This week" msgstr "Denne veka" #: qt/qttools.py:393 msgid "Last week" msgstr "Førre veke" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Hovudprofil" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Endra" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 msgid "&General" msgstr "" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Brukar" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Chiffer" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Slått av" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Ved kvar start/omstart" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, 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:363 msgid "Every hour" msgstr "Kvar time" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, 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:372 #, fuzzy msgid "Custom hours" msgstr "Eigendefinert tidsplan" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Kvar dag" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Repetert (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Kvar veke" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Kvar månad" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Kvar år" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Dag(ar)" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Kvar" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dag(ar)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Veke(r)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Inkluder mappe" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Ekskluder fil" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "År" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Smart-sletting" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "Dag(ar)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "Veke(r)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Fjern augneblinksbiletet" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "" #: qt/settingsdialog.py:805 msgid "None" msgstr "" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Køyr 'rsync' med '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Ny profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Gje profilen nytt namn" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Vil du verkeleg sletta profilen \"{name}\"?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Ekskluder mønster" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Ekskluder fil" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Ekskluder mappe" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Inkluder mappe" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Vil du verkeleg endra biletmappa?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Kommando" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Parametrar" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Slett" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Vel alle" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Samanlikne" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Gå til" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Alternativa" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Du kan ikkje samanlikna eit augneblinksbilete med seg sjølv." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Fann ikkje kommando" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Vil du verkeleg sletta {file} i augneblinksbiletet {snapshot_id}?" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Vil du verkeleg sletta {file} i {count} augneblinksbilete?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Dette kan ikkje gjeras om!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ÅTVARING" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Augneblinksbilete" #~ msgid "..." #~ msgstr "…" #~ msgid "Diff" #~ msgstr "Diff" #~ msgid "Every 10 minutes" #~ msgstr "Kvart 10. minutt" #~ msgid "Every 5 minutes" #~ msgstr "Kvart 5. minutt" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #~ msgid "Settings" #~ msgstr "Innstillingar" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Augneblinksbilete: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Sjå på innhaldet til det gjeldande lageret" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Sjå på augneblinksbiletet som blei laga {timestamp}" #~ msgid "Working..." #~ msgstr "Arbeider …" #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Tryggleikskopimappa kan ikkje vera med!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Undermapper til tryggleikskopimappa kan ikkje vera med!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Du kan ikkje fjerna den siste profilen!" backintime-1.4.3/common/po/pl.po000066400000000000000000001254731455673541400165400ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Ostrzeżenie" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Profil główny" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "lokalnie" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Klucz prywatny SSH" #: common/config.py:304 msgid "encrypted" msgstr "zaszyfrowane" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Szyfrowanie" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH z szyfrowaniem" #: common/config.py:317 msgid "Default" msgstr "Domyślny" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Katalog migawek jest nieprawidłowy!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} nie jest katalogiem." #: common/config.py:457 #, fuzzy msgid "Host/User/Profile-ID must not be empty." msgstr "Pola Host, Użytkownik i Profil nie mogą być puste." #: common/config.py:467 common/config.py:514 #, fuzzy, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Nie mogę zapisać do: {path}\n" "Jesteś pewien, że posiadasz uprawnienia do jego zapisu?" #: common/config.py:484 #, 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 (hard-links). Proszę użyć linuksowego systemu " "plików." #: common/config.py:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopiowanie linków (odwołanie linków symbolicznych)" #: common/config.py:498 msgid "Expert Options" msgstr "Opcje zaawansowane" #: common/config.py:502 #, 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 (hard-links). Proszę użyj trybu 'SSH'." #: common/config.py:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Nie można odnaleźć tablicy cron.\n" "Czy cron jest zainstalowany ?\n" "Jeżeli nie, wszystkie automatyczne kopie zapasowe powinny zostać wyłączone." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Nie udało się zapisać nowej tablicy zadań (crontab)." #: common/config.py:1707 #, 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" " Service '{dbus_interface}' nie była dostępna" #: common/config.py:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" już istnieje." #: common/configfile.py:736 msgid "The last profile cannot be removed." msgstr "" #: 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 password" msgstr "Potwierdź hasło" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Hasło nie pasuje." #: common/encfstools.py:178 #, 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Utwórz migawkę" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Punkt montowania {} nie jest pusty." #: common/password.py:240 #, fuzzy, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}': podaj hasło do {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "NIEPOWODZENIE" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Przywracanie uprawnień" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Gotowe" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Kopia zapasowa przełożona z powodu pracy na baterii" #: common/snapshots.py:770 #, fuzzy 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żeli katalog znajduje się na dysku wymiennym proszę go podłączyć." #: common/snapshots.py:772 #, 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:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Nie udało się wykonać migawki {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Finalizuję" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Nie można utworzyć katalogu" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Zapisz plik konfiguracyjny ..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Zapis uprawnień ..." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Nie mogę usunąć katalogu" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Utwórz migawkę" #: common/snapshots.py:1254 msgid "Success" msgstr "Sukces" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Nic się nie zmieniło - nowa migawka niepotrzebna" #: common/snapshots.py:1318 #, 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:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Inteligentne usuwanie" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Usuwanie poprzednich migawek" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Spróbuj utrzymać minimalną ilość wolnego miejsca" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Spróbuj utrzymać minimalną {perc} wolnych i-węzłów" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Teraz" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Nie można zamontować {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Nie można utworzyć zdalnej ścieżki." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Wprowadź hasło dla \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Skróty" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Dodaj do wykluczonych" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Nie można odnaleźć katalogu migawek.\n" "Jeżeli katalog znajduje się na dysku wymiennym proszę go podłączyć i wcisnąć OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Utwórz migawkę" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Wykonaj migawkę (sprawdzanie sumy kontrolnej)" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "Używaj sum kontrolnych do wykrywania zmian." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Wstrzymaj wykonywanie migawki" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Wznów wykonywanie migawki" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Zakończ wykonywanie migawki" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Odśwież listę migawek" #: qt/app.py:480 msgid "Name snapshot" msgstr "Nazwa migawki" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Usuń migawkę" #: qt/app.py:488 msgid "View snapshot log" msgstr "Zobacz dziennik migawki" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Podgląd ostatniego dziennika" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Zarządzaj profilami" #: qt/app.py:500 msgid "Shutdown" msgstr "Zamykanie" #: qt/app.py:502 #, fuzzy msgid "Shut down system after snapshot has finished." msgstr "Zamknij system po wykonaniu migawki." #: qt/app.py:504 #, fuzzy msgid "Setup language…" msgstr "Ustaw język" #: qt/app.py:508 msgid "Exit" msgstr "Wyjście" #: qt/app.py:512 msgid "Help" msgstr "Pomoc" #: qt/app.py:516 msgid "Profiles config file" msgstr "Plik konfiguracyjny profili" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Strona WWW" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Lista zmian" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ" #: qt/app.py:528 msgid "Ask a question" msgstr "Zadaj pytanie" #: qt/app.py:531 msgid "Report a bug" msgstr "Zgłoś błąd" #: qt/app.py:534 msgid "Translation" msgstr "Tłumaczenie" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "O programie" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Przywróć" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Przywróć do…" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "Do góry" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Pokaż ukryte pliki" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Porównaj migawki" #: qt/app.py:627 msgid "&Backup" msgstr "&Kopia zapasowa" #: qt/app.py:638 msgid "&Restore" msgstr "&Przywróć" #: qt/app.py:644 msgid "&Help" msgstr "P&omoc" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "Pracuje:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Gotowe, archiwizacja niepotrzebna" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Pracuje" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Błąd" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Wysłane" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Prędkość" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Do końca" #: qt/app.py:1050 msgid "Global" msgstr "Globalne" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Start" #: qt/app.py:1067 msgid "Backup folders" msgstr "Katalogi kopii zapasowej" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nazwa migawki" #: qt/app.py:1202 #, fuzzy 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ąć migawkę" msgstr[2] "Czy na pewno usunąć migawkę" #: qt/app.py:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "Nie udało się wykonać migawki {snapshot_id}!!!" msgstr[1] "Nie udało się wykonać migawki {snapshot_id}!!!" msgstr[2] "Nie udało się wykonać migawki {snapshot_id}!!!" #: qt/app.py:1370 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "Czy na pewno usunąć migawkę" msgstr[1] "Czy na pewno usunąć migawkę" msgstr[2] "Czy na pewno usunąć migawkę" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 msgid "Snapshot" msgstr "Migawka" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Przywróć {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Przywróć {path} do…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Autorzy" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Tłumaczenia" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licencja" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Ustaw język" #: qt/languagedialog.py:87 msgid "System default" msgstr "Domyślne systemowe" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Użyj języka systemu operacyjnego." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Przetłumaczono: {percent}" #: qt/languagedialog.py:184 #, 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}m 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" "Prosimy, odwiedź {translation_platform_url} jeśli chcesz pomóc. W celu uzyskania odpowiedzi na pytania lub dalszych wskazówek prosimy odwiedź {back_in_time_project_website}.\n" "Przepraszamy za utrudniania, ta wiadomość nie pojawi się ponownie sama, ale będzie dostępna w menu pomocy.\n" "Zespół Back In Time" #: qt/languagedialog.py:216 msgid "translation platform" msgstr "platformę do tłumaczenia" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Twoje tłumaczenia" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Widok dziennika migawki" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Migawki" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtr" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "wszystkie" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "zmiany" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "błędy" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "informacje" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] błąd, [I] informacja, [C] zmiana" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "dekoduj ścieżki" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopiuj" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Dekoduj" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Pytanie" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Podgląd ostatniego dziennika" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Uruchom{appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Pracuje…" #: qt/qttools.py:370 msgid "Today" msgstr "Dziś" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Wczoraj" #: qt/qttools.py:386 msgid "This week" msgstr "Ten tydzień" #: qt/qttools.py:393 msgid "Last week" msgstr "Poprzedni tydzień" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "Ostatnie sprawdzenie {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Pokaż pełny dziennik" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Zarządzaj profilami" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Edycja" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Dodaj" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Usuń" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Ogólne" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Tryb" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Gdzie zapisać migawki" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Katalog" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Ustawienia SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Host" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Użytkownik" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Ścieżka" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Szyfr" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Klucz prywatny" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Hasło" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Zapisz hasło w portfelu" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Zaawansowane" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Pełna ścieżka migawki" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Harmonogram" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Nieaktywny" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Przy każdym uruchomieniu / restarcie" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Co {n} minut" msgstr[1] "Co {n} minut" msgstr[2] "Co {n} minut" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Co godzinę" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Co {n} godzin" msgstr[1] "Co {n} godzin" msgstr[2] "Co {n} godzin" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Własny harmonogram" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Codziennie" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Wielokrotnie (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Po podłączeniu dysku (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Co tydzień" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Co miesiąc" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Co rok" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Dzień" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Dzień powszedni" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Godzina" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Godziny" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 msgid "Every" msgstr "Co" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "godzin" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "dni" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "tygodni" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "miesięcy" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Dołącz" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Dołącz pliki i katalogi" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Dodaj plik" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Dodaj katalog" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Wyklucz" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Wyklucz wzorce, pliki lub katalogi" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Bardzo zalecane" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Dodaj domyślne" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Wyklucz pliki większe niż: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&Automatyczne usuwanie" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Starsze niż" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "lat" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Jeżeli wolnej przestrzeni na dysku jest mniej niż" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Jeżeli wolnych i-węzłów jest mniej niż" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Inteligentne usuwanie" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "Uruchom w tle na zdalnym hoście." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EKSPERYMENTALNE" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "dni" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Przechowaj jedną migawkę na dzień" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Przechowaj jedną migawkę na tydzień" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "tygodni(e)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Przechowaj jedną migawkę na miesiąc" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "miesięcy" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Przechowuj jedną migawkę na rok przez wszystkie lata" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Nie usuwaj nazwanych migawek" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Ustawienia" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Włącz powiadomienia" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Wyłącz migawki podczas zasilania z baterii" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Status zasilania niedostępny w systemie" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Uruchamiaj tylko jedną migawkę na raz" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Kontynuuj przy błędach (pozwól na niekompletne migawki)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Używaj sum kontrolnych do wykrywania zmian" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "Poziom dziennika" #: qt/settingsdialog.py:805 msgid "None" msgstr "Brak" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Opcje eksportu (&x)" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Zmień te ustawienia tylko jeżeli dobrze wiesz co robisz." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Uruchamiaj 'rsync' z '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "jako zadanie crona" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "na zdalnym hoście" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "podczas wykonywania ręcznej migawki" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "na lokalnej maszynie" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Przekieruj stdout z zadań crona do /dev/null." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Przekieruj stderr z zadań crona do /dev/null." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Ograniczenie przepustowości rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Zachowaj ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Zachowaj rozszerzone atrybuty (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Kopiowanie niebezpiecznych linków (działa tylko z linkami bezwzględnymi)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Dodatkowe opcje dla rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "domyślnie" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Dodaj prefiks do komend SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Sprawdź czy zdany host jest dostępny" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 #, fuzzy msgid "Check if remote host supports all necessary commands" msgstr "Sprawdź czy zdany host jest dostępny" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Przywróć konfigurację" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nowy profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Zmień nazwę profilu" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Na pewno usunąć profil \"{name}\"?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Wzór wykluczenia" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Wyklucz plik" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Wyklucz katalog" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Dołącz plik" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Uwzględnij katalog" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Na pewno zmienić katalog migawek?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Pełna ścieżka migawki: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "włączone" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "wyłączone" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Przywróć ustawienia" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Nie znaleziono konfiguracji" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Użyj %1 i %2 dla ścieżki parametrów" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Szczegółowa analiza (dokładniejsza, ale bardziej czasochłonna)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Usuń" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Wybierz wszystko" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Porównaj" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Idź do" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Opcje" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Nie możesz porównywać migawki samej ze sobą." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Nie znaleziono polecenia" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Nie udało się wykonać migawki {snapshot_id}!!!" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Nie można tego cofnąć!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "OSTRZEŻENIE" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Wykluczyć {path} z przyszłych migawek?" #~ msgid "&Snapshot" #~ msgstr "&Migawka" #~ msgid "&View" #~ msgstr "&Widok" #~ msgid "..." #~ msgstr "…" #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Zmiany i błędy" #~ msgid "Config File Help" #~ msgstr "Podręcznik pliku konfiguracji" #~ msgid "Create a new SSH key without Password." #~ msgstr "Utwórz nowy klucz SSH bez hasła" #~ msgid "Diff" #~ msgstr "Różnice" #~ msgid "Diff Options" #~ msgstr "Opcje diff" #~ msgid "Error:" #~ msgstr "Błąd:" #~ msgid "Every 10 minutes" #~ msgstr "Co 10 minut" #~ msgid "Every 12 hours" #~ msgstr "Co 12 godzin" #~ msgid "Every 30 minutes" #~ msgstr "Co 30 minut" #~ msgid "Every 4 hours" #~ msgstr "Co 4 godziny" #~ msgid "Every 5 minutes" #~ msgstr "Co 5 minut" #~ msgid "Every 6 hours" #~ msgstr "Co 6 godzin" #~ msgid "Key File" #~ msgstr "Plik klucza" #~ msgid "List only different snapshots" #~ msgstr "Wyświetl tylko różne migawki" #~ msgid "Local encrypted" #~ msgstr "lokalnie z szyfrowaniem" #~ msgid "Modify for Full System Backup" #~ msgstr "Ustaw pełną kopię systemu" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Przywróć '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Przywróć '%s' do ..." #~ msgid "Run 'ionice':" #~ msgstr "Uruchamiaj 'ionice':" #~ msgid "Run 'nice':" #~ msgstr "Uruchamiaj 'nice':" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Uruchamiaj 'rsync' z 'nocache':" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Uruchamiaj 'rsync' z 'nocache':" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Ustawienia" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Migawka: %s" #~ msgid "View the current disk contents" #~ msgstr "Pokaż obecną zawartość dysku" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Zobacz migawkę wykonaną {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "Z BŁĘDAMI!" #~ msgid "Working..." #~ msgstr "Pracuję..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Nie możesz dołączyć katalogu z kopią zapasową!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Nie można dołączyć podkatalogu backup!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Nie możesz usunąć ostatniego profilu!" backintime-1.4.3/common/po/pt.po000066400000000000000000001273031455673541400165420ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-23 09:10+0000\n" "Last-Translator: ymusachio \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Advertência" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Local" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Chave privada SSH" #: common/config.py:304 msgid "encrypted" msgstr "encriptado" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Encriptação" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH encriptado" #: common/config.py:317 msgid "Default" msgstr "Padrão" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "A pasta de snapshots não é válida!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "" "Deves selecionar pelo menos uma pasta para fazer a cópia de segurança!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "A pasta de backup não pode ser incluída." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "A subpasta de backup não pode ser incluída." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opção inválida. {path} não é uma pasta." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "O Host/Utilizador/ID de Perfil não pode estar vazio." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Copiar links (desreferenciar links simbólicos)" #: common/config.py:498 msgid "Expert Options" msgstr "Opções Avançadas" #: common/config.py:502 #, 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' em " "vez disso." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Não é possível encontrar o crontab.\n" "Tem a certeza de que o cron está instalado?\n" "Se não estiver, deve desativar todas as cópias de segurança automáticas." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Não foi possível escrever o novo crontab." #: common/config.py:1707 #, 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:1722 #, 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:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "O perfil \"{name}\" já existe." #: common/configfile.py:736 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 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:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Tirar snapshot" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Não é possível desmontar {mountprocess} de {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} não encontrado. Por favor, instale, por exemplo, {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Ponto de montagem {} não está vazio." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Perfil '{profile}': Introduza a palavra-passe para {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "FALHOU" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Restaurar permissões" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Concluído" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Adiando a cópia de segurança enquanto a bateria está em uso" #: common/snapshots.py:770 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:772 #, 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:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Falha ao tirar snapshot {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "A finalizar" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Não é possível criar a pasta" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "A guardar o ficheiro de configuração…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "A guardar permissões…" #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Não é possível remover a pasta" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "A tirar snapshot" #: common/snapshots.py:1254 msgid "Success" msgstr "Sucesso" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, 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:1268 msgid "See 'man rsync' for more details" msgstr "Consulte 'man rsync' para mais detalhes" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Nada foi alterado, não é necessário um novo snapshot" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Não é possível renomear {new_path} para {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Remoção inteligente" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "A remover snapshots antigos" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "A tentar manter o espaço livre mínimo" #: common/snapshots.py:1737 #, 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:2821 qt/app.py:1616 msgid "Now" msgstr "Agora" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Não é possível montar {sshfs}" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cifra {cipher} falhou para {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "O caminho remoto existe, mas não é um diretório." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "O caminho remoto não é gravável." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "O caminho remoto não é executável." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Não foi possível criar o caminho remoto." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "O host remoto {host} não suporta {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Consulte 'man backintime' para obter mais instruções" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "O host remoto {host} não suporta hardlinks" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Por favor, introduza a palavra-passe para \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Atalhos" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Adicionar à Inclusão" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Adicionar à Exclusão" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName}\" não está configurado. Gostaria de restaurar uma configuração " "anterior?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Não foi possível encontrar a pasta dos snapshots.\n" "Se estiver numa unidade removível, por favor, ligue-a e depois pressione OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Tirar um snapshot" #: qt/app.py:455 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:458 msgid "Take a snapshot (checksum mode)" msgstr "Tirar um snapshot (modo de verificação de checksum)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Utilizar checksums para deteção de alterações nos ficheiros." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Pausar o processo de snapshot" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Continuar o processo de snapshot" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Parar o processo de snapshot" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Atualizar lista de snapshots" #: qt/app.py:480 msgid "Name snapshot" msgstr "Nomear snapshot" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Remover snapshot" #: qt/app.py:488 msgid "View snapshot log" msgstr "Ver registo do snapshot" #: qt/app.py:492 msgid "View last log" msgstr "Ver último registo" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Gerir perfis…" #: qt/app.py:500 msgid "Shutdown" msgstr "Desligar" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Desligar o sistema após a conclusão do snapshot." #: qt/app.py:504 msgid "Setup language…" msgstr "Configurar idioma…" #: qt/app.py:508 msgid "Exit" msgstr "Sair" #: qt/app.py:512 msgid "Help" msgstr "Ajuda" #: qt/app.py:516 msgid "Profiles config file" msgstr "Ficheiro de configuração de perfis" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Página web" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Registo de alterações" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ" #: qt/app.py:528 msgid "Ask a question" msgstr "Fazer uma pergunta" #: qt/app.py:531 msgid "Report a bug" msgstr "Reportar um erro" #: qt/app.py:534 msgid "Translation" msgstr "Tradução" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Sobre" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Restaurar" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Restaurar para …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Cima" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Mostrar ficheiros ocultos" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Comparar snapshots…" #: qt/app.py:627 msgid "&Backup" msgstr "&Backup" #: qt/app.py:638 msgid "&Restore" msgstr "&Restaurar" #: qt/app.py:644 msgid "&Help" msgstr "&Ajuda" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Se fechar esta janela, o Back In Time não conseguirá desligar o seu sistema quando o snapshot estiver concluído.\n" "Deseja mesmo fechar?" #: qt/app.py:905 msgid "Working:" msgstr "A trabalhar:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Concluído, sem necessidade de cópia de segurança" #: qt/app.py:962 msgid "Working" msgstr "A trabalhar" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Erro" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Enviado" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Velocidade" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "ETA (tempo estimado)" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Raiz" #: qt/app.py:1052 msgid "Home" msgstr "Pasta Pessoal" #: qt/app.py:1067 msgid "Backup folders" msgstr "Pastas de Cópia de Segurança" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nome do Snapshot" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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.\n" "Se já não precisa delas, pode removê-las com {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Remover elementos mais recentes na pasta original." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Restaurar os ficheiros ou pastas selecionados para o destino original e\n" "eliminar os ficheiros ou pastas que não estão no snapshot.\n" "Tenha extrema precaução, pois isto irá\n" "eliminar ficheiros e pastas que foram\n" "excluídos durante a criação do snapshot." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Capturas de ecrã" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Restaurar {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Restaurar {path}" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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 Back In Time em {language} algumas vezes.\n" "A tradução para {language} da versão do Back In Time 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 Back In Time.\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 Back In Time" #: qt/languagedialog.py:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Perfil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Capturas de ecrã" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Alterações" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Erros" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Informações" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "A processar" #: qt/qttools.py:370 msgid "Today" msgstr "Hoje" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Ontem" #: qt/qttools.py:386 msgid "This week" msgstr "Esta semana" #: qt/qttools.py:393 msgid "Last week" msgstr "Semana passada" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Perfil Principal" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Editar" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "&Geral" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Onde guardar as snapshots" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "Servidor" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 #, fuzzy msgid "User" msgstr "Utilizador" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 #, fuzzy msgid "Private Key" msgstr "Chave privada SSH" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Agenda" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Desactivado" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "A cada arranque/reinício" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "A cada {n} minutos" msgstr[1] "A cada {n} minutos" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "A cada hora" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "A cada {n} horas" msgstr[1] "A cada {n} horas" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "A Horas Definidas" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Diariamente" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Repetidamente (anacron)" #: qt/settingsdialog.py:375 #, fuzzy msgid "When drive gets connected (udev)" msgstr "Qunado o dispositifo for conectado (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Semanalmente" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Mensalmente" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Anualmente" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Dia(s)" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "Hora" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "Hora(s)" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "A cada" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Hora(s)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dia(s)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Semana(s)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Mês/Meses" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Incluir" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Adicionar um ficheiro" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 #, fuzzy msgid "Add folder" msgstr "Adicionar pasta" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Excluir" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "Altamente recomendado" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "&Auto-remoção" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Mais antigo do que" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Ano(s)" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Se espaço livre é inferior a" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Se espaço livre é inferior a" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Remoção inteligente" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 #, fuzzy msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "Dia(s)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "Semana(s)" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "Mês/Meses" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Não remover snapshots nomeadas" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "&Opções" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Activar as notificações" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Use a soma de verificação para detectar mudanças" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "" #: qt/settingsdialog.py:805 msgid "None" msgstr "" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "O&pções Avançadas" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Novo perfil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Renomear perfil" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Tem a certeza que desja remover este perfil \"{name}\" ?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Excluir ficheiro" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Excluir pasta" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Incluir pasta" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Tem a certeza que desja remover este perfil" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command" msgstr "Comando" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters" msgstr "Parâmetros" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Ir para" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Opções" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "" #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "Comando não encontrado" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Falhou a criação da snapshot {snapshot_id} !!!" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "CUIDADO" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Excluir {path} dos snapshots futuros?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Este ficheiro log foi descodificado com os padrões automáticos de pesquisa\n" #~ "### Se alguns caminhos não estão descodificados pode descodifica-los manualmente com:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s não é membro do grupo 'fuse'.\n" #~ " Executar 'sudo adduser %(user)s fuse'. Para aplicar as alterações faça logout e login.\n" #~ "Leia 'man backintime' para mais informações." #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Capturas de ecrã" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Alterações & Erros" #~ msgid "Error:" #~ msgstr "Erro:" #~ msgid "Every 10 minutes" #~ msgstr "A cada 10 minutos" #~ msgid "Every 12 hours" #~ msgstr "A cada 12 horas" #~ msgid "Every 30 minutes" #~ msgstr "A cada 30 minutos" #~ msgid "Every 4 hours" #~ msgstr "A cada quatro horas" #~ msgid "Every 5 minutes" #~ msgstr "A cada 5 minutos" #~ msgid "Every 6 hours" #~ msgstr "A cada 6 horas" #~ msgid "Local encrypted" #~ msgstr "Local encriptado" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Perfil: \"{name}\"" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Configurações" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Ver conteúdo actual do disco" #~ msgid "WITH ERRORS !" #~ msgstr "COM ERROS !" #~ msgid "Working..." #~ msgstr "A Processar..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Não pode incluir a directoria de backup !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Não pode incluir a sub-directoria de backup !" #~ msgid "You can't remove the last profile!" #~ msgstr "Não pode remover o último perfil!" backintime-1.4.3/common/po/pt_BR.po000066400000000000000000001501611455673541400171230ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-19 10:47+0000\n" "Last-Translator: ymusachio \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Aviso" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Perfil principal" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Local" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Chave privada SSH" #: common/config.py:304 msgid "encrypted" msgstr "criptografado" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Criptografia" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH criptografado" #: common/config.py:317 msgid "Default" msgstr "Padrão" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Perfil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Pasta para Snapshots não é válida!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Você deve selecionar pelo menos uma pasta para backup!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Pasta backup não pode ser incluída." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Sub-pasta backup não pode ser incluída." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opção inválida. {path} não é uma pasta." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/Usuário/Perfil não devem estar vazios." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Copiar links (desreferenciar links simbólicos)" #: common/config.py:498 msgid "Expert Options" msgstr "Opções avançadas" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Não é possível localizar crontab.\n" "Você tem certeza se o cron está instalado?\n" "Se não você deve desabilitar todos os backups automáticos." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Falha ao escrever um novo crontab." #: common/config.py:1707 #, 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:1722 #, 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:1733 #, 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 o arquivo de configuração" #: common/configfile.py:143 msgid "Failed to load config" msgstr "Falha ao carregar o arquivo de configuração" #: common/configfile.py:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "O perfil \"{name}\" já existe." #: common/configfile.py:736 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 password" msgstr "Por favor, confirme a senha" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Senha não corresponde." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" "As versões até 1.7.2 do encfs possuem um bug com a opção --reverse. Por " "favor, atualize o encfs." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Criar snapshot" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Não é possível desmontar {mountprocess} de {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} não encontrado. Por favor, instale por exemplo {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "O ponto de montagem {} não está vazio." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Perfil '{profile}': Digite a senha para {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "FALHOU" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Permissões de restauração" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Pronto" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Adiando o backup enquanto estiver na bateria" #: common/snapshots.py:770 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 conecte-a." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Aguardando %s segundo." msgstr[1] "Aguardando %s segundos." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Falha ao criar snapshot {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Finalizando" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Não é possível criar a pasta" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Salvando arquivo de configuração…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Salvando permissões…" #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Não é possível remover a pasta" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Criando snapshot" #: common/snapshots.py:1254 msgid "Success" msgstr "Sucesso" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, 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:1268 msgid "See 'man rsync' for more details" msgstr "Veja 'man rsync' para mais detalhes" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Nada mudou, nenhum snapshot novo necessário" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Não é possível renomear {new_path} para {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Remoção Inteligente" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Removendo snapshots antigos" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Tentando manter o mínimo de espaço livre" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Tentando manter o mínimo de inodes {perc} livres" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Agora" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Não é possível montar {sshfs}" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cifra {cipher} falhou para {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "O caminho remoto existe, mas não é um diretório." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "O caminho remoto não é gravável." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "O caminho remoto não é executável." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Não foi possível criar o caminho remoto." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "O host remoto {host} não suporta {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Consulte 'man backintime' para instruções adicionais" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "O host remoto {host} não suporta hardlinks" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "Copiar ssh-key pública \"{pubkey}\" para o host remoto \"{host}\"" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Por favor, digite a senha para \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Atalhos" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Adicione à Lista de Inclusões" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Adicione à Lista de Exclusões" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} não está configurado. Você gostaria de restaurar uma configuração " "anterior?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Não é possível localizar a pasta de snapshots.\n" "Se ela estiver em uma unidade removível, por favor conecte-a e pressione OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Criar snapshot" #: qt/app.py:455 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:458 msgid "Take a snapshot (checksum mode)" msgstr "Criar snapshot (modo checksum)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Utilizar checksums para a detecção de alterações em arquivos." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Pausar processo de snapshot" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Resumir processo de snapshot" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Parar processo de snapshot" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Atualizar lista de snapshots" #: qt/app.py:480 msgid "Name snapshot" msgstr "Nomear snapshot" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Remover snapshot" #: qt/app.py:488 msgid "View snapshot log" msgstr "Visualizar log dos snapshots" #: qt/app.py:492 msgid "View last log" msgstr "Visualizar último log" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Gerenciar perfis…" #: qt/app.py:500 msgid "Shutdown" msgstr "Desligar" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Desligar o sistema após a finalização do snapshot." #: qt/app.py:504 msgid "Setup language…" msgstr "Configurar idioma…" #: qt/app.py:508 msgid "Exit" msgstr "Sair" #: qt/app.py:512 msgid "Help" msgstr "Ajuda" #: qt/app.py:516 msgid "Profiles config file" msgstr "Arquivo de configuração de perfis" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Website" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Registro de alterações" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ (perguntas frequentes)" #: qt/app.py:528 msgid "Ask a question" msgstr "Faça uma pergunta" #: qt/app.py:531 msgid "Report a bug" msgstr "Relatar um problema (bug)" #: qt/app.py:534 msgid "Translation" msgstr "Traduções" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Sobre" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Restaurar" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Restaurar para …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Acima" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Mostrar arquivos ocultos" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Comparar snapshots…" #: qt/app.py:627 msgid "&Backup" msgstr "&Backup" #: qt/app.py:638 msgid "&Restore" msgstr "&Restaurar" #: qt/app.py:644 msgid "&Help" msgstr "&Ajuda" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Se você fechar essa janela, o Back In Time não será capaz de desligar o seu sistema quando o snapshot estiver finalizado.\n" "Você realmente deseja fechar?" #: qt/app.py:905 msgid "Working:" msgstr "Trabalhando:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Concluído, backup não necessário" #: qt/app.py:962 msgid "Working" msgstr "Trabalhando" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Erro" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Enviado" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Velocidade" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "ETA (tempo estimado)" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Raiz" #: qt/app.py:1052 msgid "Home" msgstr "Home" #: qt/app.py:1067 msgid "Backup folders" msgstr "Pastas de backup" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nome do Snapshot" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "Versões mais recentes dos arquivos serão renomeadas com o sufixo '{suffix}' antes de serem restauradas.\n" "Se você não precisar mais delas, pode removê-las com '{cmd}'" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Remover elementos mais recentes na pasta original." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Restaurar arquivos ou pastas selecionadas para o destino original e\n" "excluir arquivos ou pastas que não estão no snapshot.\n" "Tenha extrema cautela, pois isso irá\n" "deletar os arquivos e pastas que foram\n" "excluídos durante a criação do snapshot." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "AVISO: Apagar arquivos no sistema de arquivos raiz (root) pode destruir todo" " o seu sistema!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Snapshot" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Restaurar {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurar {path} para …" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Autores" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Traduções" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licença" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Configurar idioma" #: qt/languagedialog.py:87 msgid "System default" msgstr "Padrão do sistema" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Use o idioma do sistema operacional." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Traduzido: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "plataforma de tradução" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Sua tradução" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Visualização do Último Log" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Visualização do Log do Snapshot" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Perfil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Snapshots" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtrar" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Tudo" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Alterações" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Erros" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informação" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Erro, [I] Informação, [C] Alteração" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "decodificar caminhos" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Copiar" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Decodificar" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Você deseja excluir isto?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Pergunta" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Visualizar Último Log" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Inicializar {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Trabalhando…" #: qt/qttools.py:370 msgid "Today" msgstr "Hoje" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Ontem" #: qt/qttools.py:386 msgid "This week" msgstr "Esta semana" #: qt/qttools.py:393 msgid "Last week" msgstr "Semana passada" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Última verificação {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Mostrar Log Completo" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Gerenciar perfis" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Editar" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Adicionar" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Remover" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Geral" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Modo" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Onde salvar os snapshots" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Pasta" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Configurações do SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Host" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Porta" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Usuário" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Caminho" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Cifra" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Chave Privada" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Senha" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Salvar a Senha no Chaveiro" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Avançado" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Caminho completo do snapshot" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Agendar" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Desabilitado" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Em cada início/reinício" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "A cada hora" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 msgid "Custom hours" msgstr "Horas personalizadas" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Todo dia" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Repetidamente (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Quando o drive for conectado (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Toda semana" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Todo mês" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Todo ano" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Dia" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Dia da Semana" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Hora" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Horas" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Todo" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Hora(s)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dia(s)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Semana(s)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Mês(es)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Incluir" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Incluir arquivos e pastas" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Adicionar arquivo" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Adicionar pasta" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Excluir" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Os wildcards ({example1}) serão ignorados no modo 'SSH criptografado'.\n" "Apenas asteriscos únicos ou duplos são permitidos ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Instruções, arquivos ou pastas a serem excluídos" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Altamente recomendado" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Adicionar padrão" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Excluir arquivos maiores que: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Excluir arquivos maiores que o valor em %(prefix)s.\n" "Com o 'Modo completo do rsync' desativado, isso afetará apenas novos arquivos,\n" "pois, para o rsync, essa é uma opção de transferência, não uma opção de exclusão.\n" "Portanto, arquivos grandes que foram feitos backup anteriormente permanecerão nos snapshots\n" "mesmo que tenham sido alterados." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&Remoção automática" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Mais antigo que" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Ano(s)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Se o espaço livre for menor que" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Se o número de inodes livres é menor que" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Remoção inteligente:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Executar em segundo plano no host remoto." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Manter todos os snapshots pelos últimos" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "dia(s)." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Manter um snapshot por dia pelos últimos" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Manter um snapshot por semana pelas últimas" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "semana(s)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Manter um snapshot por mês pelos últimos" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "mês(es)." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Manter um snapshot por ano por todos os anos." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Não remover os snapshots com nome." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Opções" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Habilitar notificações" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Desativar snapshot quando estiver na bateria" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Status de energia não disponível no sistema" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Executar apenas um snapshot por vez" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Outros snapshots ficarão bloqueados até que o snapshot atual seja completado.\n" "Esta é uma opção global. Por isso, irá afetar todos os perfis para este usuário.\n" "No entanto, você precisa ativá-la também para todos os outros usuários." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Backup de arquivos substituídos na restauração" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuar em caso de erros (manter snapshots incompletos)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Usar checksum para detectar alterações" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Nível de Log" #: qt/settingsdialog.py:805 msgid "None" msgstr "Nenhum" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "O&pções Avançadas" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Executar 'rsync' com '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "como uma tarefa cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "no host remoto" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "quando for criar um snapshot manual" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Por favor, instale 'nocache' para habilitar esta opção)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "na máquina local" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirecionar o stdout para /dev/null nas tarefas cron." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirecionar o stderr para /dev/null nas tarefas cron." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Limitar a largura de banda utilizada pelo rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/seg" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Preservar ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Preservar atributos extendidos (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Copiar links inseguros (funciona apenas com links absolutos)" #: qt/settingsdialog.py:1024 #, 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:1027 msgid "Paste additional options to rsync" msgstr "Colar opções adicionais para o rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Prefixo a ser executado antes de cada comando no host remoto.\n" "Variáveis precisam ser escapadas com \\$FOO.\n" "Isso não afeta o rsync. Então para adicionar um prefixo\n" "ao rsync, use \"%(cbRsyncOptions)s\" com\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "padrão" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Adicionar prefixo aos comandos SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Verificar se host remoto está online" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Aviso: se desabilitado e o host remoto\n" "não estiver disponível, isto poderá resultar em alguns\n" "erros estranhos." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Verificar se o host remoto suporta todos os comandos necessários" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Aviso: se desabilitado e o host remoto\n" "não suporta todos os comandos necessários,\n" "isto poderá resultar em alguns erros estranhos." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Restaurar Configuração" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Editar user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Novo perfil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Renomear perfil" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Você não escolheu um arquivo de chave privada para SSH.\n" "Você gostaria de gerar um novo par de chaves públicas/privadas sem senha?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Arquivo de chave privada \"{file}\" não existe." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Você gostaria de copiar sua chave SSH pública para o\n" "host remoto para habilitar login sem senha?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "A autenticidade do host {host} não pode ser estabelecida.\n" "\n" "digital da chave {keytype} é:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" "Por favor verifique esta digital! Você gostaria de adicionar para seu " "arquivo 'known_hosts'?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Excluir padrão" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Excluir arquivo" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Excluir pasta" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Incluir arquivo" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Incluir pasta" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Deseja realmente mudar a pasta de snapshots ?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Falha em criar nova chave SSH em {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Caminho completo do snapshot: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "ativado" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "desativado" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Restaurar Configurações" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Por favor navegue para o snapshot do qual você quer restaurar a configuração de {appName}. O caminho pode se parecer com esse:\n" "{samplePath}\n" "\n" "Se seus snapshots estão em um drive remoto ou criptografados, você precisa manualmente montá-los primeiro. Se você usar Modo SSH você também pode precisar configurar login de chave pública para o host remoto.\n" "Verifique 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Nenhum arquivo de configuração encontrado" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "script de user-callback não tem linha de shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang no script do user-callback não é executável." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Use %1 e %2 para os parâmetros de caminho" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Apenas snapshots diferentes" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Listar apenas snapshots iguais a: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Verificação profunda (mais preciso, mas lento)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Excluir" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Selecionar Tudo" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Comparar" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Ir para" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Opções" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Você não pode comparar um snapshot com ele mesmo." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Comando não encontrado" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Isso não pode ser revogado!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "AVISO" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Excluir {path} de snapshots futuros?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " e adicionar seu usuário para grupo 'fuse'" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s não é membro do grupo 'fuse'.\n" #~ " Execute 'sudo adduser %(user)s fuse'. Para aplicar as alterações, execute um logout e logue novamente.\n" #~ "Veja o manual em 'man backintime' para instruções adicionais." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s não encontrado em ssh_known_hosts." #~ msgid "&Snapshot" #~ msgstr "&Snapshot" #~ msgid "&View" #~ msgstr "&Exibir" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Alterações & Erros" #~ msgid "Config File Help" #~ msgstr "Ajuda sobre o Arquivo de Configuração" #~ msgid "Diff" #~ msgstr "Diferença" #~ msgid "Diff Options" #~ msgstr "Opções do Diff" #~ msgid "Error:" #~ msgstr "Erro:" #~ msgid "Every 10 minutes" #~ msgstr "A cada 10 minutos" #~ msgid "Every 12 hours" #~ msgstr "A cada 12 horas" #~ msgid "Every 30 minutes" #~ msgstr "A cada 30 minutos" #~ msgid "Every 4 hours" #~ msgstr "A cada 4 horas" #~ msgid "Every 5 minutes" #~ msgstr "A cada 5 minutos" #~ msgid "Every 6 hours" #~ msgstr "A cada 6 horas" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Backup completo do sistema consegue apenas criar um snapshot para ser restaurado para o(s) mesmo(s) disco(s) físico(s) com o mesmo particionamento da fonte; restaurar para novos discos físicos ou os mesmos discos com particionamento diferent irá resultar em um sistema potencionalmente quebrado e não-utilizável.\n" #~ "\n" #~ "Backup completo do sistema irá sobrepor algumas configurações que podem ter sido customizadas. Continuar?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Colisão de hash ocorreu em hash_id %s. Incrementando o valor global " #~ "hash_collision e tentando de novo." #~ msgid "List only different snapshots" #~ msgstr "Listar apenas snapshots diferentes" #~ msgid "Local encrypted" #~ msgstr "Local encriptado" #~ msgid "Modify for Full System Backup" #~ msgstr "Modificar para Backup do Sistema Completo" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "A autenticação sem password para %(user)s@%(host)s falhou. Veja 'man " #~ "backintime' para instruções adicionais." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "Ping %s falhou. O host está inativo ou o endereço está errado." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Perfil: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Restaurar '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Restaurar '%s' para ..." #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Restaurar o arquivo ou pasta selecionado.\n" #~ "Se este botão estiver cinza, é provável que \"{now}\" está selecionado na lista de snapshots à esquerda." #~ msgid "Run 'ionice':" #~ msgstr "Executar 'ionice':" #~ msgid "Run 'nice':" #~ msgstr "Executar 'nice':" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Executar 'rsync' com 'nocache':" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Executar 'rsync' com 'nocache':" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Configurações" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Snapshot: %s" #~ msgid "View the current disk contents" #~ msgstr "Visualizar o conteúdo do disco atual" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Ver o snapshot feito em {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "COM ERROS !" #~ msgid "Working..." #~ msgstr "Trabalhando..." #~ msgid "You can't include backup folder!" #~ msgstr "Você não pode incluir uma pasta de backup !" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Você não pode incluir uma sub-pasta de backup !" #~ msgid "You can't remove the last profile!" #~ msgstr "Você não pode remover o último perfil!" backintime-1.4.3/common/po/ro.po000066400000000000000000001425731455673541400165450ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-19 16:30+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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Avertisment" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Profil principal" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Local" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Cheie privată SSH" #: common/config.py:304 msgid "encrypted" msgstr "criptat" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Criptare" #: common/config.py:310 msgid "SSH encrypted" msgstr "Criptat SSH" #: common/config.py:317 msgid "Default" msgstr "Implicit" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: „{name}”" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Dosarul de instantanee nu este valid!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Trebuie să selectați cel puțin un dosar pentru a copia de rezervă!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Dosarul copiei de rezervă nu poate fi inclus." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Sub-dosarul copiei de rezervă nu poate fi inclus." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Opțiune nevalidă. {path} nu este un dosar." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Gazda/Utilizatorul/ID-ul de profil nu trebuie să fie liber." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Copiază legăturile (dereferențiază legăturile simbolice)" #: common/config.py:498 msgid "Expert Options" msgstr "Opțiuni expert" #: common/config.py:502 #, 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 monatată pe " "sshfs. sshfs nu suportă legăturile tari. Utilizați în schimb modul „SSH”." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Nu s-a putut scrie un nou crontab." #: common/config.py:1707 #, 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:1722 #, 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:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilul „{name}” există deja." #: common/configfile.py:736 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 password" msgstr "Confirmați parola" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Parola nu se potrivește." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Ia instantaneu" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Nu se poate demonta {mountprocess} de la {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} nu a fost găsit. Instalați de ex. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Punctul de montare {} nu este liber." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil „{profile}”: Introduceți parola pentru {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "EȘUAT" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Restabilește permisiunile" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Terminat" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Se amână copierea de rezervă în timp ce se utilizează bateria" #: common/snapshots.py:770 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:772 #, 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:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Nu s-a putut realiza instantaneul {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Se finalizează" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Nu se poate crea dosarul" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Se salvează fișierul de configurație…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Se salvează permisiunile…" #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Nu se poate elimina dosarul" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Se ia instantaneul" #: common/snapshots.py:1254 msgid "Success" msgstr "Succes" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, 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:1268 msgid "See 'man rsync' for more details" msgstr "Consultați „man rsync” pentru mai multe detalii" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Nimic nu s-a schimbat, nu este necesar un instantaneu nou" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Nu se poate redenumi {new_path} la {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Eliminare inteligentă" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Se elimină instantaneele vechi" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Se încearcă păstrarea minimului de spațiu liber" #: common/snapshots.py:1737 #, 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:2821 qt/app.py:1616 msgid "Now" msgstr "Acum" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Nu se poate monta {sshfs}" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Cifrul {cipher} a eșuat pentru {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Calea de la distanță există dar nu este un director." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Calea de la distanță nu este inscriptibilă." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Calea de la distanță nu este executabilă." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Nu s-a putut crea calea de la distanță." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Gazda de la distanță {host} nu suportă {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Consultați „man backintime” pentru instrucțiuni suplimentare" #: common/sshtools.py:956 #, 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:977 #, 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:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Introduceți parola pentru „{user}”" #: qt/app.py:167 msgid "Shortcuts" msgstr "Scurtături" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Adaugă la includeri" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Adaugă la excluderi" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} nu este configurat. Doriți să restaurați o configurație " "precedentă?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Nu se poate găsi dosarul de instantanee.\n" "Dacă este pe o unitate detașabilă, conectați-o și apoi apăsați OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Ia un instantaneu" #: qt/app.py:455 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:458 msgid "Take a snapshot (checksum mode)" msgstr "Ia un instantaneu (modul checksum)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Utilizează checksum-uri pentru detectarea modificărilor fișierelor." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Suspendă procesul de instantaneu" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Reia procesul de instantaneu" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Oprește procesul de instantaneu" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Reîmprospătează lista de instantanee" #: qt/app.py:480 msgid "Name snapshot" msgstr "Denumește instantaneul" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Elimină instantaneul" #: qt/app.py:488 msgid "View snapshot log" msgstr "Vizualizează jurnalul instantaneului" #: qt/app.py:492 msgid "View last log" msgstr "Vizualizează ultima înregistrare în jurnal" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Gestionează profilurile…" #: qt/app.py:500 msgid "Shutdown" msgstr "Oprește" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Oprește sistemul după finalizarea instantaneului." #: qt/app.py:504 msgid "Setup language…" msgstr "Configurează limba…" #: qt/app.py:508 msgid "Exit" msgstr "Ieșire" #: qt/app.py:512 msgid "Help" msgstr "Ajutor" #: qt/app.py:516 msgid "Profiles config file" msgstr "Fișierul de configurare al profilului" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Pagină web" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Jurnal de modificări" #: qt/app.py:525 msgid "FAQ" msgstr "Întrebări frecvente" #: qt/app.py:528 msgid "Ask a question" msgstr "Pune o întrebare" #: qt/app.py:531 msgid "Report a bug" msgstr "Raportează o defecțiune" #: qt/app.py:534 msgid "Translation" msgstr "Traducere" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Despre" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Restaurează" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Restaurează la …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Sus" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Arată fișierele ascunse" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Compară instantaneele…" #: qt/app.py:627 msgid "&Backup" msgstr "&Copie de rezervă" #: qt/app.py:638 msgid "&Restore" msgstr "&Restaurează" #: qt/app.py:644 msgid "&Help" msgstr "&Ajutor" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Dacă închideți această fereastră, Back In Time nu vă va putea opri sistemul când instantaneul este finalizat.\n" "Sigur doriți să închideți?" #: qt/app.py:905 msgid "Working:" msgstr "Se lucrează:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Terminat, nicio copie de rezervă necesară" #: qt/app.py:962 msgid "Working" msgstr "Se lucrează" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Eroare" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Trimis" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Viteză" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Timp rămas" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Home" #: qt/app.py:1067 msgid "Backup folders" msgstr "Dosare copie de rezervă" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Nume instantaneu" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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.\n" "Dacă nu mai aveți nevoie de ele, puteți să le eliminați cu {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Elimină elementele mai noi din dosarul original." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Restaurează fișierele sau dosarele selectate la destinația originală și\n" "șterge fișiere sau dosare care nu se află în instantaneu.\n" "Fiți extrem de atent pentru că acest lucru va\n" "șterge fișiere și dosare care au fost\n" "excluse în timpul realizării instantaneului." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "AVERTISMENT: Ștergerea fișierelor din root-ul sistemului de fișiere vă poate" " distruge întregul sistem!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Instantaneu" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Restaurează {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Restaurează {path} la …" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Autori" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Traduceri" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licență" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Configurează limba" #: qt/languagedialog.py:87 msgid "System default" msgstr "Implicit de sistem" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Utilizează limba sistemului de operare." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Tradus: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "platforma de traducere" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Traducerea ta" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Ultima vizualizare a jurnalului" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Vizualizare jurnal de instantaneu" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Instantanee" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtrează" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Toate" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Modificări" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Erori" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informații" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Eroare, [I] Informații, [C] Modificare" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "decodează căile" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Copiază" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Decodează" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Doriți să excludeți aceasta?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Întrebare" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Vizualizează ultima înregistrare" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Pornește {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Se lucrează…" #: qt/qttools.py:370 msgid "Today" msgstr "Astăzi" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Ieri" #: qt/qttools.py:386 msgid "This week" msgstr "Săptămâna aceasta" #: qt/qttools.py:393 msgid "Last week" msgstr "Săptămâna trecută" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Ultima verificare la {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Arată jurnalul complet" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Gestionează profilurile" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Editează" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Adaugă" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Elimină" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Generale" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Mod" #: qt/settingsdialog.py:152 #, 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”." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Unde să se salveze instantaneele" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Dosar" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Configurări SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Gazdă" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Utilizator" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Cale" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Cifru" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Cheie privată" #: qt/settingsdialog.py:236 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:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" "Crează o nouă cheie SSH fără parolă (nu este permis dacă un fișier cheie " "privată este deja selectat)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Parola" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Salvează parola la inelul de chei" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Avansat" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Calea completă a instantaneului" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Program" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Dezactivat" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "La fiecare pornire/restart" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "La fiecare oră" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 msgid "Custom hours" msgstr "Ore personalizate" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Zilnic" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "În mod repetat (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Când unitatea este conectată (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Săptămânal" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Lunar" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Anual" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Zi" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Zi de lucru" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Oră" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Ore" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "La fiecare" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Oră (Ore)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Zi (Zile)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Săptămână (Săptămâni)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Lună (Luni)" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Include" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Include fișierele și dosarele" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Adaugă un fișier" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Adaugă un dosar" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Exclude" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Wildcard-urile ({example1}) vor fi ignorate cu modul „SSH criptat”.\n" "Sunt permise doar asteriscuri simple sau duble ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Exclude modele, fișiere sau dosare" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Foarte recomandat" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Adaugă implicit" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Exclude fișiere mai mari decât: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Excludeți fișierele mai mari decât valoarea în %(prefix)s.\n" "Cu „Modul rsync complet” dezactivat, acest lucru va afecta doar fișierele noi,\n" "deoarece pentru rsync aceasta este o opțiune de transfer, nu o opțiune de excludere.\n" "Deci, fișierele mari care au fost copiate de rezervă înainte vor rămâne în instantanee\n" "chiar dacă s-au modificat." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "Eliminare &automată" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Mai vechi de" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "An (Ani)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Dacă spațiul liber este mai mic decât" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Dacă inode-urile libere sunt mai puține decât" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Eliminare inteligentă:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Rulează pe fundal pe gazda de la distanță." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EXPERIMENTAL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Păstrează toate instantaneele pentru ultimele" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "zi(zile)." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Păstrează un instantaneu pe zi pentru ultimele" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Păstrează un instantaneu pe săptămână pentru ultimele" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "săptămână(săptămâni)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Păstrează un instantaneu pe lună pentru ultimele" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "lună(luni)." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Păstrează un instantaneu pe an pentru toți anii." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Nu elimina instantaneurile denumite." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Opțiuni" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Activează înștiințările" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Dezactivează instantaneele când se utilizează bateria" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Starea alimentării nu este disponibilă din sistem" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Rulează doar un instantaneu la un moment dat" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Alte instantanee vor fi blocate până la finalizarea instantaneului curent.\n" "Aceasta este o opțiune globală. Deci, va afecta toate profilurile pentru acest utilizator.\n" "Dar trebuie să activați acest lucru și pentru toți ceilalți utilizatori." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Copiază de rezervă fișierele înlocuite la restaurare" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Continuă la erori (păstrează instantanee incomplete)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Utilizează checksum pentru a detecta modificările" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Nivel de jurnalizare" #: qt/settingsdialog.py:805 msgid "None" msgstr "Nimic" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Opțiuni e&xpert" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Rulează „rsync” cu „{cmd}”:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "ca sarcină cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "pe gazda de la distanță" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "când se ia un instantaneu manual" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Instalați „nocache” pentru a activa această opțiune)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "pe mașina locală" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Redirecționează stdout către /dev/null în sarcinile cron." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Redirecționează stderr către /dev/null în sarcinile cron." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Limitează utilizarea lățimii de bandă a rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KO/sec" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Păstrează ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Păstrează atributele extinse (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Copiază legăturile nesigure (funcționează doar cu legăturile absolute)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Opțiunile trebuie citate, de ex. {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Lipește opțiunile adiționale la rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Prefix de rulat înaintea fiecărei comenzi pe gazda de la distanță.\n" "Variabilele trebuie să fie escapate cu \\$FOO.\n" "Acest lucru nu atinge rsync. Deci, pentru a adăuga un prefix\n" "pentru rsync utilizați „%(cbRsyncOptions)s” cu\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "implicit" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Adaugă un prefix la comenzile SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Verifică dacă gazda de la distanță este conectată" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Avertisment: dacă este dezactivat și gazda de la distanță\n" "nu este disponibilă, acest lucru ar putea duce la unele\n" "erori ciudate." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Verifică dacă gazda de la distanță suportă toate comenzile necesare" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Avertisment: dacă este dezactivat și gazda de la distanță\n" "nu suportă toate comenzile necesare,\n" "acest lucru ar putea duce la unele erori ciudate." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Restaurează configurația" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Editează user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Profil nou" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Redenumește profilul" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Nu ați ales un fișier cheie privată pentru SSH.\n" "Doriți să generați o nouă pereche de chei publice/private fără parolă?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Fișierul cheie privată „{file}” nu există." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Doriți să copiați cheia publică SSH la\n" "gazda de la distanță pentru a activa autentificarea fără parolă?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Autenticitatea gazdei {host} nu poate fi stabilită.\n" "\n" "Amprenta cheii {keytype} este:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Exclude modelul" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Exclude fișierul" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Exclude dosarul" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Include fișierul" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Include dosarul" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Sigur doriți să modificați dosarul de instantanee?" #: qt/settingsdialog.py:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Calea completă a instantaneului: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "activat" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "dezactivat" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Restaurează configurările" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Navigați la instantaneul din care doriți să restaurați configurația lui {appName}. Calea poate arăta astfel:\n" "{samplePath}\n" "\n" "Dacă instantaneele sunt pe o unitate de la distanță sau dacă sunt criptate, mai întâi trebuie să le montați manual. Dacă utilizați Modul SSH, poate fi necesar să configurați autentificarea cu cheia publică la gazda de la distanță.\n" "Aruncați o privire la „man backintime”." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Nu s-a găsit nicio configurație" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "scriptul user-callback nu are nicio linie shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang în scriptul user-callback nu este executabilă." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Utilizați %1 și %2 pentru parametrii căii" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Doar instantaneele care diferă" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Listează doar instantaneele egale cu: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Verificare profundă (mai precis, dar încet)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Șterge" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Selectează toate" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Compară" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Navighează la" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Opțiuni" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Nu puteți compara un instantaneu cu el însuși." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Comanda nu a fost gasită" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Acest lucru nu poate fi revocat!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "AVERTISMENT" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Excludeți {path} din instantaneele viitoare?" #~ msgid "&Snapshot" #~ msgstr "&Replica" #~ msgid "&View" #~ msgstr "&Vizualizeaza" #~ msgid "..." #~ msgstr "..." #~ msgid "Config File Help" #~ msgstr "Ajutor configuratie" #~ msgid "Diff" #~ msgstr "Diff" #~ msgid "Diff Options" #~ msgstr "Optiuni pentru diff" #~ msgid "Every 10 minutes" #~ msgstr "La fiecare 10 minute" #~ msgid "Every 5 minutes" #~ msgstr "La fiecare 5 minute" #~ msgid "Local encrypted" #~ msgstr "Criptat Local" #~ msgid "Modify for Full System Backup" #~ msgstr "Modificati pentru Copia Completa a Sistemului" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Restaureaza fisierul sau directorul selectat.\n" #~ "In cazul in care acest buton este gri, cel mai probabil {now} este selectat in lista din stanga." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Setări" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "VIzualizeaza continutul curent al disk-ului" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Vizualizeaza snapshot-ul facut la {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "CU ERORI !" #~ msgid "Working..." #~ msgstr "Se lucrează..." #~ msgid "You can't include backup folder!" #~ msgstr "Nu poți include directorul de backup !" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Nu poti include subfolder-ul de backup!" #~ msgid "You can't remove the last profile!" #~ msgstr "Nu puteți șterge ultimul profil!" backintime-1.4.3/common/po/ru.po000066400000000000000000001767151455673541400165600ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-21 15:15+0000\n" "Last-Translator: Application-Maker \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Предупреждение" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Основной профиль" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Локально" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH приватный ключ" #: common/config.py:304 msgid "encrypted" msgstr "зашифрованный" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Шифрование" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH зашифровано" #: common/config.py:317 msgid "Default" msgstr "По-умолчанию" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Профиль: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Каталог снимков задан неверно!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Вы должны выбрать хотя бы одну папку для резервного копирования!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Папка резервного копирования не должна быть включена." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Содержимое папки резервного копирования не должно быть включено." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} - не папка." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Сервер/Пользователь/Профиль-ID не должны быть пустыми." #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Не удаётся произвести запись в: {path}\n" "Вы уверены, что имеете право на запись?" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Копировать ссылки (разыменование символических ссылок)" #: common/config.py:498 msgid "Expert Options" msgstr "Расширенные настройки" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Невозможно найти crontab\n" "Вы уверены, что cron установлен?\n" "Если не установлен, то вы должны отключить автоматическое резервное копирование." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Не удалось записать новый crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Расписание udev неработает в режиме {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Профиль \"{name}\" уже существует." #: common/configfile.py:736 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 password" msgstr "Пожалуйста, подтвердите пароль" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Пароль не подходит." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" "encfs версии ≤ 1.7.2 имеют баг с опцией --reverse. Пожалуйста, обновите " "encfs." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Сделать снимок" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Не удалось размонтировать {mountprocess} из {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "%(proc)s не надено. Пожалуйста установить нап. %(install_command)s" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Точка монтирования {} не пустая." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Профиль '{profile}': Введите пароль для {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "НЕУСПЕШНО" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Восстановить права" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Готово" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Отсрочка резервного копирования при работе от батареи" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Невозможно найти каталог снимков.\n" "Если он находится на съёмном диске, пожалуйста, подключите его." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Ждать %s секунду." msgstr[1] "Ждать %s секунды." msgstr[2] "Ждать %s секунд." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Не удалось создать снимок {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Окончание" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Невозможно создать каталог" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Сохранение файла конфигурации…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Сохранение разрешений…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Найдены остатки {snapshot_id}, с которыми можно продолжить." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" "Удаление остатков папки {snapshot_id}, оставшиеся после последнего запуска" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Невозможно удалить каталог" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Создание снимка" #: common/snapshots.py:1254 msgid "Success" msgstr "Успешно" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" "Частичный перенос из-за исчезновения исходных файлов (см. «man rsync»)" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "rsync завершился c кодом {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Подробнее см. «man rsync»" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Неудачные коды выхода из rsync - это номера сигналов, см. 'kill -l' и 'man " "kill'" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Ничего не изменилось, нет необходимости в резервной копии" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Невозможно переименовать {new_path} в {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Умное удаление" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Удалить старые резервные копии" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Стараться сохранять минимальное свободное место" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Попытка сохранить минимум {perc} свободных инодов" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Сейчас" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Не удалось смонтировать {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-агент не найден. Убедитесь, что он установлен." #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Не удалось разблокировать приватный ключ ssh. Неверный пароль или пароль не " "подходит для cron." #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Пароль {cipher} не верен для {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Удаленный путь существует, но не является каталогом." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Удаленный путь недоступен для записи." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Удаленный путь не доступен для записи." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Не удалось создать удаленный путь." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Удаленный узел {host} не поддерживает{command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Дополнительные инструкции см. в man backintime" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Команды проверки на хосте {host} вернули неизвестную ошибку" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Удаленный хост {host} не поддерживает жесткие ссылки" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "Скопировать открытый ssh-ключ \"{pubkey}\" на удаленный хост \"{host}\"" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Пожалуйста, подтвердите пароль для \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Ярлыки" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Этой папки не существует\n" "в выбранном снимке." #: qt/app.py:252 msgid "Add to Include" msgstr "Включить" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Добавить в исключения" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} не настроен. Вы хотите восстановить предыдущую конфигурацию?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Невозможно найти каталог снимков.\n" "Если он находится на съёмном диске, пожалуйста, подключите его и нажмите ОК." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Сделать снимок" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" "Используйте время модификации и размер для обнаружения изменений файла." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Сделать снимок (режим контрольной суммы)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Использовать контрольную сумму для обнаружения изменений." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Приостановить процесс создания снимка" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Продолжить процесс создания снимка" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Остановить процесс создания снимка" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Обновить список моментальных снимков" #: qt/app.py:480 msgid "Name snapshot" msgstr "Имя снимка" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Удалить снимок" #: qt/app.py:488 msgid "View snapshot log" msgstr "Смотреть log снимка" #: qt/app.py:492 msgid "View last log" msgstr "Смотреть последний log" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Управление профилями…" #: qt/app.py:500 msgid "Shutdown" msgstr "Выключить" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Выключить систему после завершения создания снимка." #: qt/app.py:504 #, fuzzy msgid "Setup language…" msgstr "Изменить язык…" #: qt/app.py:508 msgid "Exit" msgstr "Выйти" #: qt/app.py:512 msgid "Help" msgstr "Справка" #: qt/app.py:516 msgid "Profiles config file" msgstr "config файл профиля" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Сайт" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Список изменений" #: qt/app.py:525 msgid "FAQ" msgstr "Часто задаваемые вопросы" #: qt/app.py:528 msgid "Ask a question" msgstr "Задать вопрос" #: qt/app.py:531 msgid "Report a bug" msgstr "Сообщить об ошибке" #: qt/app.py:534 msgid "Translation" msgstr "Перевод" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "О программе" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Восстановить" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" "Восстановление выбранных файлов или каталогов в исходное место назначения." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Восстановить в…" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" "Восстановление выбранных файлов или каталогов в новое место назначения." #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Восстановление текущей показанной папки и всего ее содержимого в исходное " "место назначения." #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Восстановление текущей показанной папки и всего ее содержимого в новое место" " назначения." #: qt/app.py:560 msgid "Up" msgstr "Вверх" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Показать скрытые файлы" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Сравнить снимки…" #: qt/app.py:627 msgid "&Backup" msgstr "&Резервное копирование" #: qt/app.py:638 msgid "&Restore" msgstr "&Восстановление" #: qt/app.py:644 msgid "&Help" msgstr "&Справка" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Если вы закроете это окно - Back In Time не сможет выключить систему после завершения создания снимка.\n" "Вы действительно хотите закрыть окно?" #: qt/app.py:905 msgid "Working:" msgstr "Работаю:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Всё сделано, сохранять ничего не надо" #: qt/app.py:962 msgid "Working" msgstr "Работаю" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Ошибка" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Отправлено" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Скорость" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Приблизительно оставшееся время" #: qt/app.py:1050 msgid "Global" msgstr "Общее" #: qt/app.py:1051 msgid "Root" msgstr "Корневой каталог" #: qt/app.py:1052 msgid "Home" msgstr "Домашний каталог" #: qt/app.py:1067 msgid "Backup folders" msgstr "Резервные директории" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Название резервной копии" #: qt/app.py:1202 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:1294 #, fuzzy, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Создать резервные копии с последующим {suffix} перед\n" "перезаписью или удалением локальных файлов." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "Более новые версии файлов перед восстановлением будут переименованы с трейлингом {suffix}.\n" "Если они вам больше не нужны, то можете удалить их с помощью команды {cmd}" #: qt/app.py:1314 #, 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:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "Удалять более новые файлы в исходной папке." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Восстановление выбранных файлов/папок в исходное место назначения и\n" "удаление файлов/папок, которых нет в снимке.\n" "Будьте предельно осторожны, потому что при этом\n" "будут удалены файлы/папки,\n" "исключенные при создании снимка." #: qt/app.py:1361 #, 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:1370 #, 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Вы уверены, что хотите удалить все новые файлы в {path}?" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "Вы уверены, что хотите удалить все новые файлы в исходной папке?" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "ВНИМАНИЕ: удаление файлов в корне файловой системы может привести к поломке " "всей системы!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Снимок" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Восстановить {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Восстановить {path} в…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "Смена языка произойдет только после перезапуска Back In Time." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Разработчики" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Переводы" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Лицензия" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Изменить язык" #: qt/languagedialog.py:87 msgid "System default" msgstr "Язык системы" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Использовать язык операционных систеы." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Переведено: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "платформы для перевода" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Ваш перевод" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Просмотр последнего лога" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Просмотр журнала снимка" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Профиль" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Резервные копии" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Фильтр" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Все" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Изменения" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Ошибки" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Информация" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Ошибка, [I] Информация, [C] Изменение" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "декодирование путей" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Копия" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Декодирование" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Хотите ли Вы исключить это?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Вопрос" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Просмотреть последний журнал" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Запустить {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Работаю…" #: qt/qttools.py:370 msgid "Today" msgstr "Сегодня" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Вчера" #: qt/qttools.py:386 msgid "This week" msgstr "Эта неделя" #: qt/qttools.py:393 msgid "Last week" msgstr "На прошлой неделе" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Это НЕ снимок, а живой вид ваших локальных файлов" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "Последняя проверка {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Показать весь лог" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Управление профилями" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Изменить" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Добавить" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Удалить" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Общие" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Режим" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Место для сохранения резервных копий" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Папка" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Найстроки SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Хост" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Порт" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Пользователь" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Путь" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "шифр" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Приватный ключ" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Выберите существующий файл приватного ключа (обычно с именем \"id_rsa\")" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" "Создать новый SSH-ключ без пароля (не допускается, если файл приватного " "ключа уже выбран)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Пароль" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Сохранить пароль в связку ключей" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "Закешировать пароль для Cron (Внимание: root может прочитать пароль)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Дополнительно" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Полный путь к снимку" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Расписание" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Отключено" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "При каждой загрузке системы" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Каждую {n} минуту" msgstr[1] "Каждые {n} минут" msgstr[2] "Каждые {n} минут" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Каждый час" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Каждый {n} час" msgstr[1] "Каждые {n} часа" msgstr[2] "Каждые {n} часов" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Другой период (в часах)" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Каждый день" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Периодически (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "При подключении носителя (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Каждую неделю" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Каждый месяц" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Ежегодно" #: qt/settingsdialog.py:383 msgid "Day" msgstr "День" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "День недели" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Час" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Часа" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Многократный запуск программы Back In Time. Это удобно если компьютер " "работает нерегулярно." #: qt/settingsdialog.py:442 msgid "Every" msgstr "Каждые" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Час(ов)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Дней (Дня)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Недель" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Месяцев" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Включить" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Включить файлы и папки" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Добавить файл" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Добавить каталог" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Исключить" #: qt/settingsdialog.py:528 #, fuzzy, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Подстановочные знаки ({example1}) будут игнорироваться в режиме 'SSH шифрование'.\n" "Допускаются только одинарные или двойные звездочки ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Исключить шаблоны, файлы или папки" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Настоятельно рекомендуется" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Добавить по умолчанию" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Исключить файлы размером более: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Исключить файлы, превышающие значение в %(prefix)s.\n" "При отключенном 'Full rsync mode' это будет влиять только на новые файлы\n" "поскольку для rsync это опция передачи, а не исключения.\n" "Таким образом, большие файлы, резервные копии которых были созданы ранее, останутся в снимках\n" "даже если они изменились." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&Автоудаление" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Старше чем" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Лет (Года)" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Если свободного места меньше, чем" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Если inode меньше, чем" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Умное удаление:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Запустить в фоновом режиме на удаленном хосте." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "ЭКСПЕРИМЕНТАЛЬНО" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Хранить все копии за последние" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "дней." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Хранить дневную копию за последние" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Хранить недельную копию за последние" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "неделя(ль)." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Хранить месячную копию за последние" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "месяц(ев)." #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "Хранить один снимок в год за все время." #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Не удалять переименованные снимки." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Параметры" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Включить уведомления" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Отключить создание резервных копий при работе от батареи" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Статус питания недоступен" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Одновременный запуск только одного снимка" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Другие снимки будут блокироваться до тех пор, пока не завершится текущий снимок.\n" "Это глобальная опция. Поэтому она будет влиять на все профили данного пользователя.\n" "Но необходимо активировать ее и для всех остальных пользователей." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Резервное копирование замененных файлов при восстановлении" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Игнорировать ошибки (сохранять незавершенные копии)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Использовать контрольную сумму для обнаружения изменений" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "Сделать новый снимок независимо от наличия изменений." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "Уровень логирования" #: qt/settingsdialog.py:805 msgid "None" msgstr "Нет" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Р&асширенные параметры" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Изменяйте эти параметры, только если вы точно знаете, что делаете." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Запустить 'rsync' с '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "как задача cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "на удаленном сервере" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "при создании снимков вручную" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Чтобы включить эту опцию, установите 'nocache')" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "на локальной машине" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Перенаправление stdout в /dev/null в cronjobs." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Перенаправление stderr в /dev/null в заданиях cronjobs." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Ограничение использования полосы пропускания rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "КБ/сек" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Сохранять ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Сохранять дополнительные атрибуты (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" "Копировать небезопасные ссылки (работает только с абсолютными ссылками)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Параметры должны быть заключены в кавычки, например: {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Вставьте дополнительные опции в rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Префикс для запуска перед каждой командой на удаленном хосте.\n" "Переменные должны быть экранированы с помощью \\$FOO.\n" "Это не касается rsync. Поэтому для добавления префикса\n" "для rsync используйте \"%(cbRsyncOptions)s\" с\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "по умолчанию" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Добавить префикс для SSH команд" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Проверка наличия удаленного узла в сети" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Предупреждение: если отключено и удаленный хост\n" "недоступен, это может привести к возникновению\n" "странных ошибок." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Проверьте, поддерживает ли удаленный хост все необходимые команды" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Внимание: если команда отключена и удаленный хост\n" "не поддерживает все необходимые команды,\n" "это может привести к возникновению странных ошибок." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Восстановить конфигурацию" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Редактирование обратного вызова пользователя" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Новый профиль" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Переименовать профиль" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Вы действительно хотите удалить профиль \"{name}\"?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Вы не выбрали файл приватного ключа для SSH.\n" "Хотите ли вы сгенерировать новую пару открытый/приватный ключ без пароля?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Файл приватного ключа \"{file}\" не существует." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Вы хотите скопировать свой открытый SSH-ключ на удаленный \n" "узел, чтобы обеспечить вход в систему без пароля?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Подлинность хоста {host} не может быть установлена.\n" "\n" "Отпечаток ключа {keytype} является:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" "Пожалуйста, проверьте этот отпечаток! Вы хотите добавить его в файл " "'known_hosts'?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Исключать папку по шаблону" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Исключая файл" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Исключить каталог" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Включить файл" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Включить каталог" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Вы уверены, что хотите изменить каталог для хранения резервных копий?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Не удалось создать новый SSH-ключ в {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Путь полного снимка: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "включено" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "отключено" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Восстановить настройки" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Перейдите к снимку, из которого необходимо восстановить конфигурацию {appName}. Путь может иметь вид:\n" "{samplePath}\n" "\n" "Если снимки находятся на удаленном диске или зашифрованы - их необходимо сначала смонтировать вручную. Если вы используете режим SSH, то также может потребоваться настройка входа с открытым ключом на удаленный хост.\n" "Посмотрите 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Не найдено конфигурации" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "в скрипте user-callback отсутствует строка shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" "Shebang в пользовательском сценарии обратного вызова не является " "исполняемым." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "Параметры сравнения снимков" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Комманда" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters" msgstr "Параметры" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Использовать %1 и %2 для комманды" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Только различающиеся снимки" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Перечислить только одинаковые снимки: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Глубокая проверка (более точная, но медленная)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Удалить" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Выделить всё" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Сравнение" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Перейти к" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Параметры" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Нельзя сравнивать резервную копию саму с собой." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Команда не найдена" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Вы действительно хотите удалить файл {file} в снимке {snapshot_id}?" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Вы действительно хотите удалить \"{file}\" в {count} снимках?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Это не может быть отменено!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ПРЕДУПРЕЖДЕНИЕ" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Исключить {path} из будущих снимков?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " и добавить пользователя в группу 'fuse'" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s не является членом группы 'fuse'.\n" #~ " Запустите 'sudo adduser %(user)s fuse'. Чтобы применить изменения, выйдите и войдите снова.\n" #~ "Выполните 'man backintime' для дальнейших инструкций." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s не найдено в ssh_known_hosts." #~ msgid "&Snapshot" #~ msgstr "&Снимок" #~ msgid "&View" #~ msgstr "&Вид" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Изменения и ошибки" #~ msgid "Config File Help" #~ msgstr "Справка о файле конфигурации" #~ msgid "Diff" #~ msgstr "Различия" #~ msgid "Diff Options" #~ msgstr "Параметры сравнения" #~ msgid "Error:" #~ msgstr "Ошибка:" #~ msgid "Every 10 minutes" #~ msgstr "Каждые 10 минут" #~ msgid "Every 12 hours" #~ msgstr "Каждые 12 часов" #~ msgid "Every 30 minutes" #~ msgstr "Каждые 30 минут" #~ msgid "Every 4 hours" #~ msgstr "Каждые 4 часа" #~ msgid "Every 5 minutes" #~ msgstr "Каждые 5 минут" #~ msgid "Every 6 hours" #~ msgstr "Каждые 6 часов" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "При полном резервном копировании можно создать только снимок для восстановления на тот же физический диск (диски) с той же разметкой диска, что и в исходной системе; восстановление на новые физические диски или на те же диски с другой разметкой приведет к созданию потенциально неисправной и непригодной для использования системы.\n" #~ "\n" #~ "Полное резервное копирование системы приведет к отмене некоторых настроек, которые могли быть изменены. Продолжить?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "В hash_id %s возникла хеш-коллизия. Увеличьте глобальную переменую " #~ "hash_collision и попробуйте снова." #~ msgid "Key File" #~ msgstr "Файл ключа" #~ msgid "List only different snapshots" #~ msgstr "Показать только различающиеся снимки состояния" #~ msgid "Local encrypted" #~ msgstr "Локальное шифрование" #~ msgid "Modify for Full System Backup" #~ msgstr "Изменение для полного резервного копирования системы" #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "" #~ "Отсутсвует подключение к %s. Сервер не отвечает или неправильный адрес." #, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Профиль: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Восстановить '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Восстановить '%s' в ..." #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Восстановить выбранный файл или папку.\n" #~ "Если эта кнопка выделена серым цветом, то, скорее всего, в левом списке снимков выбран \"{now}\"." #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Запустить 'rsync' как 'nocache':" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Запустить 'rsync' как 'nocache':" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Настройки" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Резервная копия: %s" #~ msgid "View the current disk contents" #~ msgstr "Просмотр текущего диска" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Просмотреть снимок, созданный на {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "С ОШИБКАМИ!" #~ msgid "Working..." #~ msgstr "Выполняется..." #~ msgid "You can't include backup folder!" #~ msgstr "Вы не можете добавить папку для резервного копирования!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Вы не можете включить резервную вложенную папку!" #~ msgid "You can't remove the last profile!" #~ msgstr "Невозможно удалить последний профиль!" backintime-1.4.3/common/po/sk.po000066400000000000000000001210211455673541400165230ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-18 16:24+0000\n" "Last-Translator: ntnlabs \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.3.1\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:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Varovanie" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Hlavný profil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokálny" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH privátny kľúč" #: common/config.py:304 msgid "encrypted" msgstr "šifrovaný" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Šifrovanie" #: common/config.py:310 msgid "SSH encrypted" msgstr "šifrovanie" #: common/config.py:317 msgid "Default" msgstr "Predvolené" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Neplatný priečinok na snímky!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "Priečinok Zálohy nemože byť zahrnutý." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Pod-priečinok Zálohy nemože byť zahrnutý." #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} nie je priečinok." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Hosť/Používateľ/Profil musia byť vyplnené." #: common/config.py:467 common/config.py:514 #, fuzzy, 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:484 #, fuzzy, 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:493 #, fuzzy, 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:497 qt/settingsdialog.py:1004 #, fuzzy msgid "Copy links (dereference symbolic links)" msgstr "Kopírovať odkazy (znefunkční symbolické odkazy)" #: common/config.py:498 msgid "Expert Options" msgstr "Pokročilé nastavenia" #: common/config.py:502 #, 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:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 #, fuzzy msgid "Failed to write new crontab." msgstr "Zapisanie nového crontabu zlyhalo." #: common/config.py:1707 #, 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:1722 #, 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:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" už existuje." #: common/configfile.py:736 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 #, fuzzy 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 password" msgstr "Prosím, potvrďte heslo" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Heslo sa nezhoduje." #: common/encfstools.py:178 #, fuzzy 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Urobiť snímku" #: common/mount.py:608 #, fuzzy, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Nemožem odpojiť {mountprocess} z {mountpoint}." #: common/mount.py:695 #, fuzzy msgid "{} not found. Please install e.g. {}" msgstr "{} nenájdene. Prosím nainštaluje e.g. {}" #: common/mount.py:716 #, fuzzy msgid "Mountpoint {} not empty." msgstr "Priečinok na pripojenie {} nieje prázdny." #: common/password.py:240 #, fuzzy, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}': Zadajte heslo pre {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "ZLYHANIE" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Obnoviť práva" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Hotovo" #: common/snapshots.py:708 #, fuzzy msgid "Deferring backup while on battery" msgstr "Zálohovanie odložené pri behu na batériu" #: common/snapshots.py:770 #, fuzzy 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:772 #, 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:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Zlyhalo vytváranie snímky {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Dokončovanie" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Nemožno vytvoriť priečinok" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Uložiť konfiguračný súbor…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Ukladám práva…" #: common/snapshots.py:1150 #, 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:1169 #, fuzzy, 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:1179 #, fuzzy msgid "Can't remove folder" msgstr "Nemožno odstrániť priečinok" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Robím snímok" #: common/snapshots.py:1254 #, fuzzy msgid "Success" msgstr "Hotovo" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 #, fuzzy 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:1259 #, fuzzy, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' ukončený s kódom {exit_code}" #: common/snapshots.py:1268 #, fuzzy msgid "See 'man rsync' for more details" msgstr "Pozri 'man rsync' pre viac detailov" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Nie je možné premenovať {new_path} na {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Šikovné odstránenie" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Odstraňujem staré snímky" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Pracujem na minimálnom voľnom mieste" #: common/snapshots.py:1737 #, 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:2821 qt/app.py:1616 msgid "Now" msgstr "Teraz" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Nemožno vytvoriť priečinok." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "" #: qt/app.py:167 msgid "Shortcuts" msgstr "Skratky" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "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:453 #, fuzzy msgid "Take a snapshot" msgstr "Urobiť snímku" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "Použiť kontrolný súčet pre zistenie zmien." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Obnoviť zoznam snímok" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Urobiť snímku" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Odstrániť snímku" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Zobraziť záznam snímok" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Zobraziť posledný log" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Hlavný profil" #: qt/app.py:500 msgid "Shutdown" msgstr "" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Koniec" #: qt/app.py:512 msgid "Help" msgstr "Pomocník" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Uložiť konfiguračný súbor" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Webová stránka" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "Často kladené otázky (FAQ)" #: qt/app.py:528 msgid "Ask a question" msgstr "" #: qt/app.py:531 msgid "Report a bug" msgstr "" #: qt/app.py:534 msgid "Translation" msgstr "" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "O programe" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Obnoviť" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Obnoviť" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "Hore" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Ukázať skryté súbory" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Urobiť snímku" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "&Obnoviť" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "&Pomocník" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "Pracujem:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Hotovo, záloha nie je potrebná" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Prebieha" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Chyba" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "Globálne" #: qt/app.py:1051 msgid "Root" msgstr "Koreňový priečinok" #: qt/app.py:1052 msgid "Home" msgstr "Domovský priečinok" #: qt/app.py:1067 msgid "Backup folders" msgstr "Priečinky zálohy" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Názov snímky" #: qt/app.py:1202 #, fuzzy 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "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:1370 #, fuzzy 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Snímky" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Obnoviť {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Obnoviť {path}" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Preložené na: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Váš preklad" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Snímky" #: qt/logviewdialog.py:94 #, fuzzy msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Všetko" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Zmeny" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Chyby" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Informácie" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Chyba, [I] Informácia, [C] Zmena" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Zobraziť posledný log" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Spustiť {appname}" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Prebieha" #: qt/qttools.py:370 msgid "Today" msgstr "Dnes" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Včera" #: qt/qttools.py:386 msgid "This week" msgstr "Tento týždeň" #: qt/qttools.py:393 msgid "Last week" msgstr "Minulý týždeň" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "Posledná kontrola {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Hlavný profil" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Zmeň" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Odstráň" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "&Hlavné" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Kam sa uložia snímky" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "Server" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 #, fuzzy msgid "User" msgstr "Používateľ" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Detailné nastavenie" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Plánovanie" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Zakázané" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Pri každom štarte/reštarte" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, 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:363 msgid "Every hour" msgstr "" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, 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:372 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Každý deň" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Každý týždeň" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Každý mesiac" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Každý rok" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Dni" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "Hodina" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "Hodina" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Každých" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dni" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Týždne" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Zahrnúť" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Zahrňuje súbory a priečinky" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Pridať súbor" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Pridať priečinok" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Vylúčiť" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Nazahrňovať vzory, súbory alebo priečinky" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "Vysoko odporúčané" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "&Automatické odstránenie" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Staršie ako" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Roky" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Ak je voľné miesto menšie než" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Ak je voľné miesto menšie než" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Šikovné odstránenie" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "Počet dní" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "Týždne" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Neodstraňovať pomenované snímky" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "&Možnosti" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Povoliť upozornenia" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Nevytvárať snímky pokiaľ systém beží na batérie" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Nie je možné zistiť stav napájania" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Pokračovať pri chybách (zachovať nekompletné snímky)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Použiť kontrolný súčet pre zistenie zmien" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 #, fuzzy msgid "Log Level" msgstr "Úroveň záznamu" #: qt/settingsdialog.py:805 msgid "None" msgstr "Žiadne" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "Možnosti e&xpertov" #: qt/settingsdialog.py:830 #, 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Zachovať ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Zachovať rozšírené atribúty (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopírovať nebezpečné odkazy (funguje len s absolútnymi odkazmi)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nový profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Premenovať profil" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Určite chcete vymazať profil \"{name}\" ?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Vylúčiť vzor" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Vylúčiť súbor" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Vylúčiť priečinok" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Zahrnúť súbor" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Zahrnúť priečinok" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Ste si istí, že chcete zmeniť priečinok snímok?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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 #, fuzzy msgid "Parameters" msgstr "Parametre" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Použiť %1 a %2 parametre cesty" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Hĺbková kontrola (presnejšia, ale pomalšia)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Vymazať" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Porovnať" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Ísť na" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Možnosti" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Nemôžete porovnávať snímku s ňou samou." #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "Príkaz nenájdený" #: qt/snapshotsdialog.py:369 #, 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:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "UPOZORNENIE" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Snímky" #~ msgid "..." #~ msgstr "..." #~ msgid "Changes & Errors" #~ msgstr "Zmeny a chyby" #~ msgid "Diff" #~ msgstr "Rozdiel" #~ msgid "Diff Options" #~ msgstr "Možnosti rozdielov" #~ msgid "Error:" #~ msgstr "Chyba:" #~ msgid "Every 10 minutes" #~ msgstr "Každých 10 minút" #~ msgid "Every 5 minutes" #~ msgstr "Každých 5 minút" #~ msgid "List only different snapshots" #~ msgstr "Zoznam iba rozdielnych snímok" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #~ msgid "Settings" #~ msgstr "Nastavenia" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Snímka: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Zobraziť aktuálny obsah disku" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Zobraziť snímku vytvorenú {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "S CHYBAMI!" #~ msgid "Working..." #~ msgstr "Prebieha..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Nemožno zahrnúť priečinok zálohy !" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Nemožno zahrnúť podpriečinok zálohy !" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Nemôžete odstrániť posledný profil !" backintime-1.4.3/common/po/sl.po000066400000000000000000001417731455673541400165440ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-20 19:14+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.3.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:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Opozorilo" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Glavni profil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokalno" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Privatni ključ SSH" #: common/config.py:304 msgid "encrypted" msgstr "šifrirano" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Šifriranje" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH šifrirano" #: common/config.py:317 msgid "Default" msgstr "Privzeto" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Mapa s posnetki ni veljavna!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "Mape za varnostne kopije ni mogoče vključiti." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Podmape za varnostne kopije ni mogoče vključiti." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Neveljavna možnost. {path} ni mapa." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID ne sme biti prazen." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopiraj povezave (dereferenciraj simbolne povezave)" #: common/config.py:498 msgid "Expert Options" msgstr "Napredne možnosti" #: common/config.py:502 #, 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 sshfs priklopjen v skupno rabo. sshfs " "ne podpira trdih povezav. Namesto tega uporabite način 'SSH'." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Crontab ni mogoče najti.\n" "Ste prepričani, da je cron naložen?\n" "V primeru, da ni, morate onemogočiti vse samodejne varnostne kopije." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Novega crontaba ni mogoče zapisati." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Razpored udev ne deluje z načinom {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profil \"{name}\" že obstaja." #: common/configfile.py:736 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 password" msgstr "Prosim potrdite geslo" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Geslo se ne ujema." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Ustvari posnetek" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "{mountprocess} ni bilo mogoče odklopiti iz {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} ni najden. Namestite npr. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Mountpoint {} ni prazen." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}': Vnesite geslo za {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "SPODLETELO" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Obnovi dovoljenja" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Končano" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Odložitev varnostnega kopiranja med delovanjem na bateriji" #: common/snapshots.py:770 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:772 #, 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:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Posnetek {snapshot_id} ni uspel." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Zaključevanje" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Mape ni mogoče ustvariti" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Shranjevanje konfiguracijske datoteke…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Shranjevanje dovoljenj…" #: common/snapshots.py:1150 #, 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:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Odstranjevanje ostankov mape {snapshot_id} od zadnjega zagona" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Mape ni mogoče odstraniti" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Ustvarjanje posnetka" #: common/snapshots.py:1254 msgid "Success" msgstr "Uspeh" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Delni prenos zaradi izginulih izvornih datotek (glej 'man rsync')" #: common/snapshots.py:1259 #, 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:1268 msgid "See 'man rsync' for more details" msgstr "Za več informacij glej 'man rsync'" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Nič se ni spremenilo, nov posnetek ni potreben" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{new_path} ni mogoče preimenovati v {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Pametno odstranjevanje" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Odstranjevanje starih posnetkov" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Poskus ohranjanja minimalnega prostora" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Poskus ohranjanja najmanj {perc} prostih inodov" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Zdaj" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs} ni bilo mogoče namestiti" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Šifra {cipher} ni uspela za {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Oddaljena pot obstaja, ampak ni mapa." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Oddaljena pot ni zapisljiva." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Oddaljena pot ni izvršljiva." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Oddaljene poti ni bilo mogoče ustvariti." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Oddaljeni gostitelj {host} ne podpira {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Za več navodil, glej 'man backintime'" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Oddaljeni gostitelj {host} ne podpira trdih povezav" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Prosim vnesite geslo za \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Bližnjice" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Dodaj v Vključi" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Dodaj v Izključi" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} ni konfiguriran. Ali želite obnoviti prejšnjo konfiguracijo?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Mapa s posnetki ni na razpolago.\n" "Če je na izmenljivem pogonu, ga priključite in pritisnite OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Ustvari posnetek" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "Za zaznavanje datotečnih sprememb uporabite čas in velikost." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Ustvari posnetek (način checksum)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Za zaznavanje datotečnih sprememb uporabite checksum." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Začasno ustavi postopek posnetka" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Nadaljuj postopek posnetka" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Ustavi postopek posnetka" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Osveži seznam posnetkov" #: qt/app.py:480 msgid "Name snapshot" msgstr "Imenuj posnetek" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Odstrani posnetek" #: qt/app.py:488 msgid "View snapshot log" msgstr "Ogled dnevnika posnetkov" #: qt/app.py:492 msgid "View last log" msgstr "Ogled zadnjega dnevnika" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Upravljanje profilov…" #: qt/app.py:500 msgid "Shutdown" msgstr "Zaustavi" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Zaustavite sistem, ko je posnetek končan." #: qt/app.py:504 msgid "Setup language…" msgstr "Nastavitev jezika…" #: qt/app.py:508 msgid "Exit" msgstr "Izhod" #: qt/app.py:512 msgid "Help" msgstr "Pomoč" #: qt/app.py:516 msgid "Profiles config file" msgstr "Konfiguracijska datoteka profilov" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Spletna stran" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Dnevnik sprememb" #: qt/app.py:525 msgid "FAQ" msgstr "Pogosta vprašanja" #: qt/app.py:528 msgid "Ask a question" msgstr "Postavite vprašanje" #: qt/app.py:531 msgid "Report a bug" msgstr "Prijavite napako" #: qt/app.py:534 msgid "Translation" msgstr "Prevod" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "O programu" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Obnovi" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "Obnovi izbrane datoteke ali mape v prvotni cilj." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Obnovi v …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "Obnovi izbrane datoteke ali mape v nov cilj." #: qt/app.py:552 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:557 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:560 msgid "Up" msgstr "Gor" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Pokaži skrite datoteke" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Primerjava posnetkov…" #: qt/app.py:627 msgid "&Backup" msgstr "&VarnostnaKopija" #: qt/app.py:638 msgid "&Restore" msgstr "&Obnovi" #: qt/app.py:644 msgid "&Help" msgstr "&Pomoč" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Če zaprete to okno, Back In Time ne bo mogel ugasniti vašega sistema, ko se posnetek stanja zaključi.\n" "Ali res želite zapreti okno?" #: qt/app.py:905 msgid "Working:" msgstr "V obdelavi:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Narejeno, varnostna kopija ni potrebna" #: qt/app.py:962 msgid "Working" msgstr "V obdelavi" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Napaka" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Poslano" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Hitrost" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Predvideni čas" #: qt/app.py:1050 msgid "Global" msgstr "Splošno" #: qt/app.py:1051 msgid "Root" msgstr "Koren" #: qt/app.py:1052 msgid "Home" msgstr "Domov" #: qt/app.py:1067 msgid "Backup folders" msgstr "Mape za varnostno kopijo" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Ime posnetka" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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}.\n" "Če jih ne potrebujete več, jih lahko odstranite z ukazom {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Odstrani novejše datoteke v prvotni mapi." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Obnovite izbrane datoteke ali mape na privotno ciljno lokacijo in\n" "odstranite datoteke ali mape, ki niso v posnetku.\n" "Bodite zelo previdni, saj bo to\n" "odstranilo datoteke in mape, ki so bile\n" "izključene med ustvarjanjem posnetka." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "POZOR: Brisanje datotek v korenu datotečnega sistema lahko pokvari ves " "sistem!!!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Posnetek" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Obnovi {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Obnovi {path} v …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "Nastavitve jezika se uveljavijo po ponovnem zagonu Back In Time." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Avtorji" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Prevodi" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licenca" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Nastavitev jezika" #: qt/languagedialog.py:87 msgid "System default" msgstr "Sistemsko privzeto" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Uporabi jezik operacijskega sistema." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Prevedeno: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "prevajalna platforma" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Vaš prevod" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Ogled zadnjega dnevnika" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Ogled dnevnika posnetkov" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Posnetki" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Vse" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Spremembe" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Napake" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Informacije" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Napake, [I] Informacije, [C] Spremembe" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "dešifriranje poti" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopiraj" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Dešifriraj" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Ali želite to izključiti?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Vprašanje" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Ogled zadnjega dnevnika" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Zaženi {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "V obdelavi…" #: qt/qttools.py:370 msgid "Today" msgstr "Danes" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Včeraj" #: qt/qttools.py:386 msgid "This week" msgstr "Ta teden" #: qt/qttools.py:393 msgid "Last week" msgstr "Prejšnji teden" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Zadnj pregled {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Prikaži celoten dnevnik" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Upravljanje profilov" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Uredi" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Dodaj" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Odstrani" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Splošno" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Način" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Kam naj se shranijo posnetki" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Mapa" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Nastavitve SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Gostitelj" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Vrata" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Uporabnik" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Pot" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Šifrirna metoda" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Zasebni ključ" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Geslo" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Shrani geslo v zbirko ključev" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Napredno" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Cela pot posnetka" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Časovni razpored" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Onemogočeno" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Ob vsakem zagonu/ponovnem zagonu" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "Vsako uro" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 msgid "Custom hours" msgstr "Ure po meri" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Vsak dan" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Ponavljajoče (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Ko se priključi disk (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Vsak teden" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Vsak mesec" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Vsako leto" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Dan" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Dan v tednu" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Ura" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Ur" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Vsakih" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Ura/ur" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dan/dni" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Teden/tednov" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Mesec/mesecev" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Vključitev" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Vključi datoteke in mape" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Dodaj datoteko" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Dodaj mapo" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Izključitev" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "V načinu 'SSH šifrirano' bodo nadomestni znaki ({example1}) zanemarjeni.\n" "Dovoljene so le enojne ali dvojne zvezdice ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Izključi vzorce, datoteke ali mape" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Zelo priporočljivo" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Dodaj privzeto" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Izključi datoteke večje kot: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Izvzemi datoteke, večje od vrednosti v %(prefix)s.\n" "Če je 'Full rsync mode' izklopljen, bo to vplivalo le na nove\n" "datoteke, ker je za rsync to prenosna opcija, ne izvzemanje.\n" "Tako bodo velike datoteke, ki so bile varnostno kopiranje že prej,\n" "ostale v posnetkih, tudi če so se spremenile." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "Samo-&odstrani" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Starejše kot" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Leto/Let" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Če je prostega prostora manj od" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Če je prostih inodeov manj od" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Pametno odstranjevanje:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Zagon v ozadju na oddaljenem gostitelju." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "EKSPERIMENTALNO" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Obdrži vse posnetke za zadnjih" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "dan/dni." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Obdrži en posnetek na dan za zadnjih" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Obdrži en posnetek na teden za zadnjih" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "teden/tednov." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Obdrži en posnetek na mesec za zadnjih" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "mesec/mesecev." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Obdrži en posnetek na leto za vsa leta." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Ne odstranjuj posnetkov z imenom." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Možnosti" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Omogoči prikaz obvestil" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Med porabo baterije onemogoči zajemanje posnetkov" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Stanje o porabi energije ni na voljo" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Poženi le en zajem posnetka naenkrat" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Drugi zajemi posnetka bodo blokirani do konca trenutnega zajema.\n" "To je globalna opcija, zato bo vplivala na vse profile tega uporabnika.\n" "Toda to možnost morate vklopiti tudi za druge uporabnike." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Pri obnovitvi naredi kopijo zamenjanih datotek" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Nadaljuj klub napakam (obdrži nepopolne posnetke)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Za zaznavanje sprememb uporabite checksum" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "Ustvari nov posnetek, tudi če ni bilo sprememb." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "Nivo poročanja" #: qt/settingsdialog.py:805 msgid "None" msgstr "Brez" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "&Napredne nastavitve" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Poženi 'rsync' z ukazom '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "kot nalogo cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "na oddaljenem gostitelju" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "pri ročnem ustvarjanju posnetka" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Da lahko omogočite to možnost morate namestiti 'nocache')" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "na lokalnem računalniku" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "V periodičnih opravilih preusmeri stdout na /dev/null." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "V periodičnih opravilih preusmeri stderr na /dev/null." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Omeji pasovno širino za rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Ohrani ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Ohrani razširjene atribute (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopiraj ne varne povezave (deluje samo z absolutnimi povezavami)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Možnosti morajo biti v narekovajih, npr. {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Prilepite dodatne možnosti za rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Predpona za zagon pred vsakim ukazom na oddaljenem gostitelju.\n" "Spremenljivke je treba ubežati z \\$FOO.\n" "To se ne dotika rsync. Za dodajanje predpone\n" "za rsync uporabite \"%(cbRsyncOptions)s\" z\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "privzeto" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Dodaj predpono ukazom SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Preveri ali je oddaljeni gostitelj povezan" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Opozorilo: v primeru, da je ta možnost onemogočena\n" "in oddaljeni gostitelj ni dosegljiv\n" "se lahko pojavijo nenavadne napake." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Preveri ali oddaljeni gostitelj podpira vse potrebne ukaze" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Opozorilo: v primeru, da je ta možnost onemogočena\n" "in oddaljeni gostitelj ne podpira vseh potrebnih ukazov,\n" "se lahko pojavijo nenavadne napake." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Obnovi konfiguracijo" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Uredi user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Nov profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Preimenuj profil" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Niste izbrali datoteke zasebnega ključa za SSH.\n" "Ali želite ustvariti nov par javnih/zasebnih ključev brez gesla?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Datoteka z zasebnim ključem \"{file}\" ne obstaja." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Ali bi želeli kopirati svoj javni SSH ključ na\n" "oddaljenega gostitelja za vključitev brezgeselne prijave?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Pristnosti gostitelja {host} ni bilo mogoče vspostaviti.\n" "\n" "Prstni odtis ključa {keytype} je:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Izključitveni vzorec" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Izključi datoteko" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Izključi mapo" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Vključi datoteko" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Vključi mapo" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Ali res želite zamenjati mapo s posnetki?" #: qt/settingsdialog.py:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Cela pot posnetka: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "omogočeno" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "onemogočeno" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Obnovi nastavitve" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Pomaknite se do posnetka, iz katerega želite obnoviti konfiguracijo {appName}. Pot je lahko videti tako:\n" "{samplePath}\n" "\n" "Če so vaši posnetki na oddaljenem disku ali če so šifrirani, jih morate najprej ročno namestiti. Če uporabljate način SSH, boste morda morali nastaviti tudi prijavo z javnim ključem v oddaljenega gostitelja.\n" "Glej 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Konfiguracija ni najdena" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "user-callback skripta nima shebang (#!/bin/sh) vrstice." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang v user-callback skripti ni zagonska datoteka." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Uporabi %1 in %2 kot vrednosti za pot" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Samo različni posnetki" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Prikaži samo enake posnetke za: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Globinsko preverjanje (bolj natančno, a počasno)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Izbriši" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Izberi vse" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Primerjaj" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Pojdi na" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Nastavitve" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Posnetka ni mogoče primerjati s samim sabo." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Ukaza ni mogoče najti" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Tega ni mogoče razveljaviti!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "POZOR" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Želite izključiti {path} iz prihodnjih posnetkov?" #~ msgid "&Snapshot" #~ msgstr "Po&snetek" #~ msgid "&View" #~ msgstr "Ogled" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Config File Help" #~ msgstr "Pomoč za konfiguracijsko datoteko" #~ msgid "Diff" #~ msgstr "Primerjava" #~ msgid "Diff Options" #~ msgstr "Možnosti za primerjavo" #~ msgid "Every 10 minutes" #~ msgstr "Vsakih 10 minut" #~ msgid "Every 12 hours" #~ msgstr "Vsakih 12 ur" #~ msgid "Every 30 minutes" #~ msgstr "Vsakih 30 minut" #~ msgid "Every 4 hours" #~ msgstr "Vsake 4 ure" #~ msgid "Every 5 minutes" #~ msgstr "Vsakih 5 minut" #~ msgid "Every 6 hours" #~ msgstr "Vsakih 6 ur" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Vsesistemsko varnostno kopiranje lahko zajame posnetek stanja, ki ga je mogoče obnoviti zgolj na istem fizičnem disku oz. diskih, z enakimi particijami kot izvor. Obnavljanje na drugačnih diskih ali istih diskih z drugače deljenimi particijami lahko privede do okvarjenega in neuporabnega sistema.\n" #~ "\n" #~ "Vsesistemsko varnostno kopiranje bo prepisalo nekatere prilagojene nastavitve. Nadaljujem?" #~ msgid "Local encrypted" #~ msgstr "Lokalno šifrirano" #~ msgid "Modify for Full System Backup" #~ msgstr "Spremeni vsesistemsko varnostno kopijo" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Obnovi izbrano datoteko ali mapo.\n" #~ "Če je ta gumb zatemnjen, je to najverjetneje zato, ker je na levem seznamu posnetkov izbrano »{now}«." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Nastavitve" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Prikaži trenutno vsebino diska" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Prikaži posnetek {timestamp}" #~ msgid "Working..." #~ msgstr "Počakajte ..." #~ msgid "You can't include backup folder!" #~ msgstr "Ne morete vključiti mape v kateri je varnostna kopija!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Ne morete vključiti podmape v kateri je varnostna kopija!" #~ msgid "You can't remove the last profile!" #~ msgstr "Zadnjega profila ne morete odstraniti!" backintime-1.4.3/common/po/sr.po000066400000000000000000001300551455673541400165410ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-18 04:06+0000\n" "Last-Translator: Lucky-2411 \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Upozorenje" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Glavni profil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokalno" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH privatan ključ" #: common/config.py:304 msgid "encrypted" msgstr "enkriptovano" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Enkripcija" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH enkriptovano" #: common/config.py:317 msgid "Default" msgstr "Podrazumevano" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Direktorijum za snimak nije važeći!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "Direktorijum za rezervne kopije ne moze biti uključen." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Pod-direktorijum za rezervne kopije ne moze biti uključen." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Nevažeća postavka. {path} nije direktorijum." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/Korisnik/ID-Profila moraju biti popunjeni." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopiraj linkove (prati simbolične linkove)" #: common/config.py:498 msgid "Expert Options" msgstr "Napredne Postavke" #: common/config.py:502 #, 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:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Ne mogu da pronađem crontab.\n" "Da li si siguran da je crontab inastaliran?\n" "Ako nije, treba da onemogućiš automatsko kreiranje rezervnih kopija." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Неуспешно чување новог crontab-а." #: common/config.py:1707 #, 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 Service " "'{dbus_interface}' nije dostupan" #: common/config.py:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Профил \"{name}\" већ постоји." #: common/configfile.py:736 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 password" msgstr "Молимо потврдите лозинку" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Направи снимак" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Профил '{profile}': Унесите лозинку за {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "НЕУСПЕШНО" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Поврати дозволе" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Готово" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Одлагање backup-а док је уређај на батерији" #: common/snapshots.py:770 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Ne mogu da pronađem direktorijum za snimke.\n" "Ako je na izmenljivom medijumu, molim vas da ga priključite." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Чекамо %s секунд." msgstr[1] "Чекамо %s секунде." msgstr[2] "Чекамо %s секунди." #: common/snapshots.py:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Неуспешно чување снимка {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Завршавање" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Неуспешно креирање фолдера" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Чување конфигурационог фајла..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Чување дозвола..." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Пронађен преостали {snapshot_id} који може бити настављен." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Уклањање преосталог {snapshot_id} фолдера од прошлог покретања" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Неуспешно уклањање фолдера" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Прављење снимка" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Нишата није промењено, није потребан нови снимак" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Неуспешно преименовање {new_path} у {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Паметно уклањање" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Уклањање старог снимка" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Покушавамо сачувати минимални слободан простор" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Покушавамо остварити минимум {perc} слободних inode-a" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Сада" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Неуспешно монтирање {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Неуспешно креирање фолдера." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "Копирај јавни ssh кључ \"{pubkey}\" на удаљени хост \"{host}\"" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Молим вас унесите лозинку за \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Пречице" #: qt/app.py:187 #, fuzzy msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Овај фолдер не постоји\n" "у тренутно изабраном снимку." #: qt/app.py:252 msgid "Add to Include" msgstr "Додај у листу инклузија" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Додај у Изузетке" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Ne mogu da pronađem direktorijum sa snimcima.\n" "Ako se nalazi na izmenjivom mediju, molim da ga priključite i pritisnete OK." #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "Направи снимак" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 #, fuzzy msgid "Take a snapshot (checksum mode)" msgstr "Направи снимак са контролним бројем" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "Користи контролни број за примећивање промена." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Паузирај процес снимања" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Настави процес снимања" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Заустави процес снимања" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Обнови листу снимака" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Направи снимак" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Уклони снимак" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Погледај извештај снимака" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Прикажи последњи извештај" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Главни профил" #: qt/app.py:500 msgid "Shutdown" msgstr "Искључивање" #: qt/app.py:502 #, fuzzy msgid "Shut down system after snapshot has finished." msgstr "Искључи систем након завршетка снимања." #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Излаз" #: qt/app.py:512 msgid "Help" msgstr "Помоћ" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Чување конфигурационог фајла" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Веб сајт" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Извештај промена" #: qt/app.py:525 msgid "FAQ" msgstr "Честа питања" #: qt/app.py:528 msgid "Ask a question" msgstr "Постави питање" #: qt/app.py:531 msgid "Report a bug" msgstr "Пријави грешку" #: qt/app.py:534 msgid "Translation" msgstr "" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "О апликацији" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Поврати" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "Повратак одабраних фајлова или фолдера на њихову првобитну локацију." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Поврати на…" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "Повратак одабраних фајлова или фолдера на нову локацију." #: qt/app.py:552 #, fuzzy msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "Повратак тренутно приказаног фолдера и његовог садржаја на њихову првобитну " "локацију." #: qt/app.py:557 #, fuzzy msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "Повратак тренутно приказаног фолдера и његовог садржаја на нову локацију." #: qt/app.py:560 msgid "Up" msgstr "Горе" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Прикажи скривене фајлове" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Направи снимак" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "Повратак" #: qt/app.py:644 msgid "&Help" msgstr "Помоћ" #: qt/app.py:761 #, fuzzy msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Ако затворите овај прозор Back In Time неће моћи да угаси ваш систем када се снимање буде завршило.\n" "Да ли сигурно желите да га затворите?" #: qt/app.py:905 msgid "Working:" msgstr "Радим:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Завршено, снимање није потребно" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Radim" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Грешка" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "Глобално" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Home" #: qt/app.py:1067 msgid "Backup folders" msgstr "Direktorijumi za rezervne kopije" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Име Снимка" #: qt/app.py:1202 #, 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 ste sigurni da želite da uklonite snimak" msgstr[1] "Da li ste sigurni da želite da uklonite snimak" msgstr[2] "Da li ste sigurni da želite da uklonite snimak" #: qt/app.py:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "Неуспешно чување снимка {snapshot_id} !!!" msgstr[1] "Неуспешно чување снимка {snapshot_id} !!!" msgstr[2] "Неуспешно чување снимка {snapshot_id} !!!" #: qt/app.py:1370 #, 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Snimci" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Povrati {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Povrati {path}" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Приказ Последњег Дневника" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Приказ Дневника Снимка" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Снимци" #: qt/logviewdialog.py:94 #, fuzzy msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Све" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Измене" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Грешке" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Информације" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Грешка, [I] Информација, [C] Измена" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "декодирај путање" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Копирај" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Декодирај" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Да ли желите додати ово као изузетак?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Питање" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Прикажи последњи извештај" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Radim…" #: qt/qttools.py:370 msgid "Today" msgstr "Данас" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Јуче" #: qt/qttools.py:386 msgid "This week" msgstr "Ове недеље" #: qt/qttools.py:393 msgid "Last week" msgstr "Претходна седмица" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Ово НИЈЕ снимак, већ ужив приказ ваших локалних фајлова" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Прикажи целокупни Дневник" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Главни профил" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Uredi" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "Opšte" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Gde da čuvam snimke" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "Računar" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 #, fuzzy msgid "User" msgstr "Korisnik" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Napredno" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Plan rada" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Онемогућено" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "При сваком покретању/рестарту" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Сваких {n} минута" msgstr[1] "Сваких {n} минута" msgstr[2] "Сваких {n} минута" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Свакх сат времена" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Сваких {n} сати" msgstr[1] "Сваких {n} сати" msgstr[2] "Сваких {n} сати" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Произвољни Сати" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Сваког Дана" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Учестало (anacron)" #: qt/settingsdialog.py:375 #, fuzzy msgid "When drive gets connected (udev)" msgstr "Када се диск повеже (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Сваке Недеље" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Сваког Месеца" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Сваке Године" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Dana" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "Čas" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "Čas" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "Сваких" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Сат(и)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Дан(а)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Седмица" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Месец(и)" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "Uključi" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Uključi fajlove i direktorijume" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Dodaj fajl" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Dodaj direktorijum" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "Izostavi" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Izostavi šablone, fajlove, ili direktorijume" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "Toplo preporučeno" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "Automatsko uklanjanje" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Stariji od" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Година" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Ako je slobodan prostor manji od" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Ako je slobodan prostor manji od" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Паметно уклањање" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 #, fuzzy msgid "EXPERIMENTAL" msgstr "ЕКСПЕРИМЕНТАЛНО" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "Дан(а)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "Седмица" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "Месец(и)" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Nemoj ukloniti imenovane snimke" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "Opcije" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Omogući obaveštenja" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Onemogući snimke kada se računar napaja iz baterije" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Informacija o status napajanja nije dostupna od sistema" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Nastavi nakon greške (zadrži nekompletan snimak)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Користи контролни број за примећивање промена" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 #, fuzzy msgid "Log Level" msgstr "Nivo detalja u izveštaju" #: qt/settingsdialog.py:805 msgid "None" msgstr "Nijedan" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "Napredne opcije" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Izmeni ove opcije samo ako znaš šta radiš." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Sačuvaj ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Sačuvaj proširene atribute (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopiraj nebezbedni link (funkcioniše samo sa apsolutnim linkovima)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Novi profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Preimenuj profil" #: qt/settingsdialog.py:1142 #, fuzzy, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Izostavi šablon" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Izostavi fajl" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Izostavi direktorijum" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Uključi fajl" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Uključi direktorijum" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "Da li stvarno želiš da promeniš direktorijum za snimkea?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "" #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 #, fuzzy msgid "Command" msgstr "Naredba" #: qt/snapshotsdialog.py:62 #, fuzzy msgid "Parameters" msgstr "Parametri" #: qt/snapshotsdialog.py:66 msgid "Use %1 and %2 for path parameters" msgstr "Користи %1 и %2 као параметре за путање" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Излистај само снимке једнаке следећим: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Дубока провера (прецизнија али спора)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Избриши" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Одабери Све" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Иди На" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "Opcije" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "Није могуће упоредити снимак са самим собом." #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "Naredba nije pronađena" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Неуспешно чување снимка {snapshot_id} !!!" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #~ msgid "&Snapshot" #~ msgstr "Снимак" #~ msgid "&View" #~ msgstr "Приказ" #~ msgid "..." #~ msgstr "..." #~ msgid "Changes & Errors" #~ msgstr "Izmene i greške" #~ msgid "Config File Help" #~ msgstr "Помоћ са конфигурационим фајлом" #~ msgid "Diff" #~ msgstr "Diff" #~ msgid "Diff Options" #~ msgstr "Diff Опције" #~ msgid "Error:" #~ msgstr "Greška:" #~ msgid "Every 10 minutes" #~ msgstr "Svakih 10 minuta" #~ msgid "Every 5 minutes" #~ msgstr "Svakih 5 minuta" #~ msgid "List only different snapshots" #~ msgstr "Izlistaj samo različite snimke" #~ msgid "Local encrypted" #~ msgstr "Локално енкриптован" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Подешавања" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Snimak: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Pogledaj trenutni sadržaj diska" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Pogledaj snimak načinjen {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "SA GREŠKAMA !" #~ msgid "Working..." #~ msgstr "Radim..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Nije moguće uključiti direktorijum za rezervne kopije!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Nije moguće uključiti poddirektorijum za rezervne kopije!" #~ msgid "You can't remove the last profile!" #~ msgstr "Није могуће уклонити последњи профил!" backintime-1.4.3/common/po/sv.po000066400000000000000000001524441455673541400165530ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-27 04:41+0000\n" "Last-Translator: Thomas_H \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Varning" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Huvudprofil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Lokal" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Privat SSH-nyckel" #: common/config.py:304 msgid "encrypted" msgstr "krypterad" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Kryptering" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH krypterad" #: common/config.py:317 msgid "Default" msgstr "Standard" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 #, fuzzy msgid "Snapshots folder is not valid!" msgstr "Mappen för ögonblicksbilder är ogiltig!" #: common/config.py:361 #, 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:378 msgid "Backup folder cannot be included." msgstr "Mappen för säkerhetskopior kan inte inkluderas." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Undermapp till mappen för säkerhetskopior kan inte inkluderas." #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} är inte en mapp." #: common/config.py:457 #, 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:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Kopiera länkar (följ symboliska länkar)" #: common/config.py:498 msgid "Expert Options" msgstr "Expertinställningar" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Misslyckades med att skriva ny crontab." #: common/config.py:1707 #, 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:1722 #, 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:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Profilen \"{name}\" finns redan." #: common/configfile.py:736 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 password" msgstr "Bekräfta lösenord" #: common/encfstools.py:160 #, fuzzy msgid "Password doesn't match." msgstr "Lösenord matchar inte." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Ta ögonblicksbild" #: common/mount.py:608 #, fuzzy, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Kan inte avmontera {mountprocess} från {mountpoint}." #: common/mount.py:695 #, fuzzy msgid "{} not found. Please install e.g. {}" msgstr "%(proc)s hittades inte. Vänligen installera t.ex. %(install_command)s" #: common/mount.py:716 #, fuzzy msgid "Mountpoint {} not empty." msgstr "monteringspunkten %s är inte tom." #: common/password.py:240 #, fuzzy, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil \"{profile}\": Ange lösenord för {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "MISSLYCKADES" #: common/snapshots.py:539 common/snapshots.py:601 #, fuzzy msgid "Restore permissions" msgstr "Återställ behörigheter" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Färdig" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Uppskjuter säkerhetskopiering vid batteridrift" #: common/snapshots.py:770 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:772 #, 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:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Misslyckades att skapa ögonblicksbild {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Färdigställer" #: common/snapshots.py:949 #, fuzzy msgid "Can't create folder" msgstr "Kan inte skapa mapp" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Sparar konfigurationsfil…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Sparar behörigheter…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Hittade överbliven \"{snapshot_id}\" som kan återupptas." #: common/snapshots.py:1169 #, 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:1179 #, fuzzy msgid "Can't remove folder" msgstr "Kan inte ta bort mapp" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Tar ögonblicksbild" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "\"rsync\" avslutades med felkod {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Se \"man rsync\" för fler detaljer" #: common/snapshots.py:1272 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:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Inget har förändrats, ingen ny ögonblicksbild behövs" #: common/snapshots.py:1318 #, 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:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Smart borttagning" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Tar bort gamla ögonblicksbilder" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Försöker att behålla minimum ledigt utrymme" #: common/snapshots.py:1737 #, 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:2821 qt/app.py:1616 msgid "Now" msgstr "Nu" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Kan inte montera {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent hittades inte. Kontrollera att den är installerad." #: common/sshtools.py:418 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:506 #, fuzzy, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Chiffer {cipher} misslyckades för {host}." #: common/sshtools.py:653 #, fuzzy msgid "Remote path exists but is not a directory." msgstr "Fjärrsökväg finns men är inte en katalog." #: common/sshtools.py:658 #, fuzzy msgid "Remote path is not writable." msgstr "Fjärrsökväg är inte skrivbar." #: common/sshtools.py:663 #, fuzzy msgid "Remote path is not executable." msgstr "Fjärrsökväg är inte körbar." #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Kunde inte skapa fjärrsökväg." #: common/sshtools.py:948 #, fuzzy, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Fjärrvärd {host} stöder inte {command}" #: common/sshtools.py:952 common/sshtools.py:961 #, fuzzy msgid "Look at 'man backintime' for further instructions" msgstr "Se \"man backintime\" för ytterligare instruktioner" #: common/sshtools.py:956 #, 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:977 #, 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:1060 #, 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:1062 #, fuzzy, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Ange lösenord för \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "Genvägar" #: qt/app.py:187 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:252 msgid "Add to Include" msgstr "Lägg till i inkludera" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Lägg till i exkludera" #: qt/app.py:339 #, fuzzy, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} är inte konfigurerad. Vill du återställa till en tidigare " "konfiguration?" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "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:453 #, fuzzy msgid "Take a snapshot" msgstr "Ta ögonblicksbild" #: qt/app.py:455 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:458 msgid "Take a snapshot (checksum mode)" msgstr "Ta ögonblicksbild (med checksumma)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Använd checksumma för att detektera ändringar." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Pausa process för ögonblicksbilder" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Återuppta process för ögonblicksbilder" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Stoppa process för ögonblicksbilder" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Uppdatera listan över ögonblicksbilder" #: qt/app.py:480 msgid "Name snapshot" msgstr "Namnge ögonblicksbild" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Ta bort ögonblicksbild" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Visa logg för ögonblicksbild" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Visa senaste logg" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Hantera profiler…" #: qt/app.py:500 msgid "Shutdown" msgstr "Stäng av" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Stäng av systemet när ögonblicksbild har slutförts." #: qt/app.py:504 msgid "Setup language…" msgstr "Välj språk…" #: qt/app.py:508 msgid "Exit" msgstr "Avsluta" #: qt/app.py:512 msgid "Help" msgstr "Hjälp" #: qt/app.py:516 msgid "Profiles config file" msgstr "Konfigurationsfil för profiler" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Webbsida" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Ändringslogg" #: qt/app.py:525 msgid "FAQ" msgstr "Vanliga frågor" #: qt/app.py:528 msgid "Ask a question" msgstr "Ställ en fråga" #: qt/app.py:531 msgid "Report a bug" msgstr "Rapportera ett fel" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "Översättningar" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Om" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Återskapa" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Återskapa till…" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Upp" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Visa dolda filer" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Jämför ögonblicksbilder…" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "Å&terskapa" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "&Hjälp" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" 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:905 msgid "Working:" msgstr "Arbetar:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Färdig, ingen säkerhetskopiering behövdes" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "Arbetar" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Fel" #: qt/app.py:994 qt/qtsystrayicon.py:202 #, fuzzy msgid "Sent" msgstr "Skickat" #: qt/app.py:995 qt/qtsystrayicon.py:203 #, fuzzy msgid "Speed" msgstr "Hastighet" #: qt/app.py:996 qt/qtsystrayicon.py:204 #, fuzzy msgid "ETA" msgstr "Tid kvar" #: qt/app.py:1050 msgid "Global" msgstr "Global" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Hem" #: qt/app.py:1067 msgid "Backup folders" msgstr "Mappar för säkerhetskopior" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Namn på ögonblicksbild" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, fuzzy, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Ta bort nyare filer i originalmappen." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "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:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "VARNING: Att ta bort filer i filsystemets rot kan förstöra hela ditt " "system!!!" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "Ögonblicksbilder" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Återskapa {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Återskapa {path} till…" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Utvecklare" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Översättningar" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Licens" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Välj språk" #: qt/languagedialog.py:87 msgid "System default" msgstr "Standardval för systemet" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Använd operativsystemets språk." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Översatt: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "översättningsplattformen" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Din översättning" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Visning av senaste logg" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Visning av logg för ögonblicksbild" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Ögonblicksbilder" #: qt/logviewdialog.py:94 #, fuzzy msgid "Filter" msgstr "Filter" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Alla" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Ändringar" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Fel" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Information" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Fel, [I] Information, [C] Ändring" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "avkoda sökvägar" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopiera" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Avkoda" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Vill du utesluta detta?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Fråga" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Visa senaste logg" #: qt/qtsystrayicon.py:107 #, fuzzy, python-brace-format msgid "Start {appname}" msgstr "Starta {appname}" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Arbetar…" #: qt/qttools.py:370 msgid "Today" msgstr "Idag" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Igår" #: qt/qttools.py:386 msgid "This week" msgstr "Denna vecka" #: qt/qttools.py:393 msgid "Last week" msgstr "Förra veckan" #: qt/qttools.py:539 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:544 #, fuzzy, python-brace-format msgid "Last check {time}" msgstr "Senaste kontroll {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Visa fullständig logg" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Hantera profiler" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Redigera" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Lägg till" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Ta bort" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "&Allmänt" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 #, fuzzy msgid "Mode" msgstr "Läge" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Var ögonblicksbilder ska sparas" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Mapp" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH-inställningar" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 #, fuzzy msgid "Host" msgstr "Värd" #: qt/settingsdialog.py:204 #, fuzzy msgid "Port" msgstr "Port" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Användare" #: qt/settingsdialog.py:214 #, fuzzy msgid "Path" msgstr "Sökväg" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Chiffer" #: qt/settingsdialog.py:226 #, fuzzy msgid "Private Key" msgstr "Privat nyckel" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Lösenord" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Spara lösenord till nyckelring" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Avancerat" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Full sökväg för ögonblicksbild" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Schema" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Inaktiverad" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Vid varje start/omstart" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "Varje timme" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 #, fuzzy msgid "Custom hours" msgstr "Anpassade timmar" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Varje dag" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Upprepad (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "När enhet ansluts (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Varje vecka" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Varje månad" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Varje år" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "Dag" #: qt/settingsdialog.py:394 #, fuzzy msgid "Weekday" msgstr "Veckodag" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "Timme" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "Timmar" #: qt/settingsdialog.py:435 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:442 #, fuzzy msgid "Every" msgstr "Varje" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Timme(-ar)" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Dag(ar)" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Vecka(-or)" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Månad(er)" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "&Inkludera" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Inkludera filer och mappar" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Lägg till fil" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Lägg till mapp" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "&Exkludera" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Jokertecken ({example1}) ignoreras i läget \"SSH krypterat\".\n" "Endast enkel eller dubbel asterisk är tillåten ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Exkludera mönster, filer eller mappar" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Starkt rekommenderade" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Lägg till standard" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Exkludera filer större än: " #: qt/settingsdialog.py:594 #, fuzzy, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." 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:616 #, fuzzy msgid "&Auto-remove" msgstr "Automatisk &borttagning" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "Äldre än" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "År" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "Om ledigt utrymme är mindre än" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "Om lediga inoder är mindre än" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Smart borttagning:" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "Kör i bakgrunden på fjärrvärden." #: qt/settingsdialog.py:690 #, fuzzy msgid "EXPERIMENTAL" msgstr "EXPERIMENTELL" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Behåll alla ögonblicksbilder för de senaste" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "dag(ar)" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Behåll en ögonblicksbild per dag de senaste" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Behåll en ögonblicksbild per vecka för de senaste" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "vecka/veckor." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Behåll en ögonblicksbild per månad de senaste" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "månad(er)." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Behåll en ögonblicksbild per år för alla år." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Ta inte bort namngivna ögonblicksbilder." #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "I&nställningar" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Aktivera aviseringar" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Inaktivera ögonblicksbilder vid batteridrift" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Strömstatus inte tillgängligt från system" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Gör bara en ögonblicksbild i taget" #: qt/settingsdialog.py:763 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "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:772 msgid "Backup replaced files on restore" msgstr "Säkerhetskopiera ersatta filer vid återställning" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Fortsätt vid fel (behåll icke kompletta ögonblicksbilder)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Använd checksummor för att detektera ändringar" #: qt/settingsdialog.py:793 #, 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:800 #, fuzzy msgid "Log Level" msgstr "Loggnivå" #: qt/settingsdialog.py:805 msgid "None" msgstr "Inga" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "E&xpertinställningar" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, fuzzy, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Kör \"rsync\" med \"{cmd}\":" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "som cronjobb" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "på fjärrvärd" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "vid tagning av en manuell ögonblicksbild" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Installera \"nocache\" för att aktivera detta alternativ)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "på lokal maskin" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Omdirigera stdout till /dev/null i cronjobb." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Omdirigera stderr till /dev/null i cronjobb." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Bandbreddsbegränsning för rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/s" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Behåll ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Behåll utökade attribut (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Kopiera osäkra länkar (fungerar endast med absoluta länkar)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Alternativ måste skrivas inom citationstecken t.ex. {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Klistra in ytterligare alternativ till rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Prefix att köra före varje kommando på fjärrvärd.\n" "Variabler behöver 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:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "standard" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Lägg till prefix till SSH-kommandon" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Kontrollera om fjärrvärd är uppkopplad" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "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:1071 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:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "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:1089 msgid "Restore Config" msgstr "Återställ konfiguration" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Redigera user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Ny profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Byt namn på profil" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Du har inte valt en privat SSH-nyckel.\n" "Vill du skapa ett nytt lösenordsfritt offentligt/privat nyckelpar?" #: qt/settingsdialog.py:1469 #, fuzzy, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Privat nyckelfil \"{file}\" finns inte." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "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:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Autenticiteten för värd \"{host}\" kan inte fastställas.\n" "\n" "Fingeravtryck för nyckel av typ {keytype} är:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Exkludera mönster" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Exkludera fil" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Exkludera mapp" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Inkludera fil" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Inkludera mapp" #: qt/settingsdialog.py:1930 #, 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:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Fullständig sökväg för ögonblicksbild: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "aktiverad" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "inaktiverad" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Återställ inställningar" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Gå till ögonblicksbilden från vilken du vill återställa {appName}s konfiguration. Sökvägen kan se ut som: \n" "{samplePath}\n" "\n" "Om dina ögonblicksbilder finns på en fjärrdisk eller om de är krypterade måste du manuellt montera dem först. Om du använder Mode SSH kan du också behöva ställa in inloggningen med offentlig nyckel till fjärrvärden.\n" "Ta en titt på \"man backintime\"." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Ingen konfiguration hittades" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "user-callback skript har ingen shebang (#!/bin/sh) rad." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang i user-callback skript är inte körbart." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Använd %1 och %2 för sökvägsparametrar" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Endast ögonblicksbilder med olikheter" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Lista endast lika ögonblicksbilder till: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Djupkontroll (mer noggrann, men långsam)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Ta bort" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Välj alla" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Jämför" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Gå till" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Alternativ" #: qt/snapshotsdialog.py:330 #, 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:338 msgid "Command not found" msgstr "Kommandot hittades inte" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 #, fuzzy msgid "This cannot be revoked!" msgstr "VARNING: Det här kan inte återkallas!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "VARNING" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Exkludera \"{path}\" från framtida ögonblicksbilder?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " lägg till din användare till gruppen \"fuse\"" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "\"%s\" är en symlink. Det länkade målet kommer inte att säkerhetskopieras tills du inkluderar det också.\n" #~ "Vill du inkludera symlinks-målet istället?" #~ msgid "" #~ "### This log has been decoded with automatic search pattern\n" #~ "### If some paths are not decoded you can manually decode them with:\n" #~ msgstr "" #~ "### Den här loggen har avkodats med automatiskt sökmönster\n" #~ "### Om några sökvägar inte har avkodats kan du manuellt avkoda dem med:\n" #, python-format #~ msgid "" #~ "%(user)s is not member of group 'fuse'.\n" #~ " Run 'sudo adduser %(user)s fuse'. To apply changes logout and login again.\n" #~ "Look at 'man backintime' for further instructions." #~ msgstr "" #~ "%(user)s är inte medlem i gruppen \"fuse\".\n" #~ " Kör \"sudo adduser %(user)s fuse\". För att tillämpa ändringar logga ut och logga in igen.\n" #~ "Se \"man backintime\" för ytterligare instruktioner." #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "%s hittades inte i ssh_known_hosts." #, fuzzy #~ msgid "&Snapshot" #~ msgstr "Ögonblicksbild" #, fuzzy #~ msgid "&View" #~ msgstr "Visa" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "Ändringar & fel" #~ msgid "Config File Help" #~ msgstr "Hjälp för konfigurationsfil" #~ msgid "Create a new SSH key without Password." #~ msgstr "Skapa en ny SSH-nyckel utan lösenord." #~ msgid "Diff" #~ msgstr "Jämförelse" #~ msgid "Diff Options" #~ msgstr "Jämförelsealternativ" #~ msgid "Error:" #~ msgstr "Fel:" #~ msgid "Every 10 minutes" #~ msgstr "Var 10:e minut" #~ msgid "Every 12 hours" #~ msgstr "Var 12:e timme" #~ msgid "Every 30 minutes" #~ msgstr "Var 30:e minut" #~ msgid "Every 4 hours" #~ msgstr "Var 4:e timme" #~ msgid "Every 5 minutes" #~ msgstr "Var 5:e minut" #~ msgid "Every 6 hours" #~ msgstr "Var 6:e timme" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Full systemsäkerhetskopiering kan bara skapa en ögonblicksbild som ska återställas till samma fysiska disk(ar) med samma diskpartitionering som från källan; återställa till nya fysiska skivor eller samma skivor med olika partitionering ger ett potentiellt brutet och oanvändbart system.\n" #~ "\n" #~ "Full systemsäkerhetskopiering åsidosätter några inställningar som kan ha anpassats. Fortsätta?" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "" #~ "Hash-kollision inträffade i hash_id %s. Öka globalvärdet för hash_collision " #~ "och försök igen." #~ msgid "Key File" #~ msgstr "Nyckelfil" #~ msgid "List only different snapshots" #~ msgstr "Lista endast olika ögonblicksbilder" #~ msgid "Local encrypted" #~ msgstr "Lokal krypterad" #~ msgid "Modify for Full System Backup" #~ msgstr "Modifiera för fullständig systemsäkerhetskopiering" #~ msgid "Mountprocess lock timeout" #~ msgstr "Monteringsprocess lås tiden ute" #, python-format #~ msgid "" #~ "Password-less authentication for %(user)s@%(host)s failed. Look at 'man " #~ "backintime' for further instructions." #~ msgstr "" #~ "Lösenordslös autentisering för %(user)s@%(host)s misslyckades. Se \"man " #~ "backintime\" för ytterligare instruktioner." #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "Pinga %s misslyckades. Värd är nere eller fel adress." #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Återställ \"%s\"" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Återställ \"%s\" till..." #, fuzzy, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Återskapa vald fil eller mapp.\n" #~ "Om denna knapp är gråtonad är det högst sannolikt för att \"{now}\" är vald i listan över ögonblicksbilder på vänstra sidan." #~ msgid "Run 'ionice':" #~ msgstr "Kör \"ionice\":" #~ msgid "Run 'nice':" #~ msgstr "Kör \"nice\":" #, fuzzy #~ msgid "Run 'rsync' with 'ionice':" #~ msgstr "Kör \"rsync\" med \"nocache\":" #~ msgid "Run 'rsync' with 'nocache':" #~ msgstr "Kör \"rsync\" med \"nocache\":" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Inställningar" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Ögonblicksbild: %s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Visa det aktuella diskinnehållet" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Visa ögonblicksbilden skapad {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "MED FEL!" #~ msgid "Working..." #~ msgstr "Arbetar..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "Du kan inte inkludera mappen för säkerhetskopior!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "Du kan inte inkludera en undermapp för säkerhetskopior!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "Du kan inte ta bort den senaste profilen!" backintime-1.4.3/common/po/th.po000066400000000000000000001672721455673541400165430ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "คำเตือน" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "โปรไฟล์หลัก" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "ท้องถิ่น" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "คีย์ส่วนตัว SSH" #: common/config.py:304 #, fuzzy msgid "encrypted" msgstr "SSH ที่เข้ารหัสแล้ว" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "การเข้ารหัส" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH ที่เข้ารหัสแล้ว" #: common/config.py:317 msgid "Default" msgstr "ค่าเริ่มต้น" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "โปรไฟล์: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "โฟลเดอร์ Snapshots ไม่ถูกต้อง!" #: common/config.py:361 #, fuzzy msgid "You must select at least one folder to back up!" msgstr "คุณต้องเลือกโฟลเดอร์อย่างน้อยหนึ่งโฟลเดอร์เพื่อทำการสำรองข้อมูล!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} ไม่ใช่โฟลเดอร์" #: common/config.py:457 #, fuzzy msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID ต้องไม่เป็นค่าว่าง" #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "ไม่สามารถเขียนไปยัง: {path} \n" "ได้ คุณแน่ใจหรือว่าคุณมีสิทธิ์ในการเขียน?" #: common/config.py:484 #, 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:493 #, fuzzy, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "คัดลอกลิงก์ (เคลียร์ลิงก์สัญลักษณ์)" #: common/config.py:498 msgid "Expert Options" msgstr "ตัวเลือกขั้นสูง" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "ไม่พบ crontab คุณแน่ใจว่าติดตั้ง\n" " cron แล้วหรือไม่? \n" "หากไม่ได้ติดตั้ง คุณควรปิดใช้งานการสำรองข้อมูลอัตโนมัติทั้งหมด" #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "ไม่สามารถเขียน crontab ใหม่ได้" #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "ตารางงานของ udev ไม่ทำงานกับโหมด {mode}" #: common/config.py:1733 #, fuzzy, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "โปรไฟล์ \"{name}\" มีอยู่แล้ว" #: common/configfile.py:736 #, 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 password" msgstr "โปรดยืนยันรหัสผ่าน" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "สร้างสแนปช็อต" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "โปรไฟล์ '{profile}': ป้อนรหัสผ่านสำหรับ {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "ล้มเหลว" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "เรียกคืนสิทธิ์" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "ทำเสร็จ" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "การเลื่อนการสำรองข้อมูลขณะใช้แบตเตอรี่" #: common/snapshots.py:770 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "ไม่พบโฟลเดอร์สแนปช็อต\n" "หากมันอยู่บนไดรฟ์ที่เปลี่ยนได้กรุณาเสียบเข้ากับเครื่อง" #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "รอ %s วินาที." #: common/snapshots.py:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "ไม่สามารถสร้างสแนปช็อต {snapshot_id} ได้" #: common/snapshots.py:826 msgid "Finalizing" msgstr "กำลังประมวลผลล่าสุด" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "ไม่สามารถสร้างโฟลเดอร์ได้" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "กำลังบันทึกไฟล์กำหนดค่า..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "กำลังบันทึกสิทธิ์การเข้าถึง" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "พบส่วนที่เหลืออยู่ {snapshot_id} ซึ่งสามารถดำเนินการต่อได้" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "พบส่วนที่เหลืออยู่ {snapshot_id} ซึ่งสามารถทำต่อได้" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "ไม่สามารถลบโฟลเดอร์ได้" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "กำลังสร้างสแนปช็อต" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "ไม่มีการเปลี่ยนแปลงใด ๆ ไม่จำเป็นต้องสร้างสแนปช็อตใหม่" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "ไม่สามารถเปลี่ยนชื่อ {new_path} เป็น {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "การลบอย่างฉลาด" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "กำลังลบสแนปช็อตเก่า" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "พยายามรักษาพื้นที่ว่างขั้นต่ำ" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "พยายามรักษาเครื่องหมายเชิงว่างขั้นต่ำที่ {perc}" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "เดี๋ยวนี้" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "ไม่สามารถเชื่อมต่อ {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "ไม่สามารถสร้างโฟลเดอร์ได้" #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "คัดลอก SSH public key \"{pubkey}\" ไปยังโฮสต์ระยะไกล \"{host}\"" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "โปรดป้อนรหัสผ่านสำหรับ \"{user}\"" #: qt/app.py:167 msgid "Shortcuts" msgstr "ทางลัด" #: qt/app.py:187 #, fuzzy msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "โฟลเดอร์นี้ไม่มีอยู่\n" "ในสแนปช็อตที่เลือกในปัจจุบัน" #: qt/app.py:252 msgid "Add to Include" msgstr "เพิ่มในรายการรว" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "เพิ่มไปยังรายการยกเว้น" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} ไม่ได้รับการกำหนดค่า คุณต้องการคืนค่าการกำหนดค่าก่อนหน้าหรือไม่?" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "ไม่พบโฟลเดอร์สแนปช็อต\n" "หากอยู่บนไดรฟ์ที่เปลี่ยนได้ โปรดเสียบเข้าแล้วกด OK" #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "สร้างสแนปช็อต" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 #, fuzzy msgid "Take a snapshot (checksum mode)" msgstr "ทำสแนปช็อตพร้อมแสดงผลการตรวจสอบสำหรับ checksum" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "ใช้ checksum เพื่อตรวจจับการเปลี่ยนแปลง" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "หยุดกระบวนการสร้างสแนปช็อตชั่วคราว" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "ดำเนินกระบวนการสร้างสแนปช็อตต่อ" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "หยุดกระบวนการสร้างสแนปช็อต" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "รีเฟรชรายการสแนปช็อต" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "สร้างสแนปช็อต" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "ลบสแนปช็อต" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "ดูบันทึกสแนปช็อต" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "ดูบันทึกล่าสุด" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "โปรไฟล์หลัก" #: qt/app.py:500 msgid "Shutdown" msgstr "ปิดเครื่อง" #: qt/app.py:502 #, fuzzy msgid "Shut down system after snapshot has finished." msgstr "ปิดเครื่องหลังสแนปช็อตเสร็จสิ้น" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "ออก" #: qt/app.py:512 msgid "Help" msgstr "ช่วยเหลือ" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "กำลังบันทึกไฟล์กำหนดค่า..." #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "เว็บไซต์" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "บันทึกการเปลี่ยนแปลง" #: qt/app.py:525 msgid "FAQ" msgstr "FAQ" #: qt/app.py:528 msgid "Ask a question" msgstr "แปลว่า \"ถามคำถาม" #: qt/app.py:531 msgid "Report a bug" msgstr "รายงานข้อบกพร่อง" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "การแปล" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "เกี่ยวกับ" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "คืน" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "เรียกคืนไฟล์หรือโฟลเดอร์ที่เลือกไปยังตำแหน่งเดิม" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "กู้คืนไปยัง …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "เรียกคืนไฟล์หรือโฟลเดอร์ที่เลือกไปยังตำแหน่งใหม่" #: qt/app.py:552 #, fuzzy msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" "คืนค่าโฟลเดอร์ที่แสดงอยู่ในปัจจุบันพร้อมทั้งเนื้อหาทั้งหมดไปยังตำแหน่งเดิม" #: qt/app.py:557 #, fuzzy msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" "คืนค่าโฟลเดอร์ที่แสดงอยู่ในปัจจุบันพร้อมทั้งเนื้อหาทั้งหมดไปยังตำแหน่งใหม่" #: qt/app.py:560 msgid "Up" msgstr "ขึ้น" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "แสดงแฟ้มที่ซ่อน" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "สร้างสแนปช็อต" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "&คืนค่า" #: qt/app.py:644 msgid "&Help" msgstr "&ช่วยเหลือ" #: qt/app.py:761 #, fuzzy msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "หากคุณปิดหน้าต่างนี้ โปรแกรม Back In Time จะไม่สามารถปิดเครื่องของคุณเมื่อสแนปช็อตเสร็จสิ้นแล้วได้\n" "คุณต้องการปิดหรือไม่?" #: qt/app.py:905 msgid "Working:" msgstr "กำลังทำงาน中:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "ทำเสร็จ, ไม่จำเป็นต้องสำรอง" #: qt/app.py:962 msgid "Working" msgstr "กำลังทำงาน" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "ข้อผิดพลาด" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "ได้ส่งแล้ว" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "ความเร็ว" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "ETA" #: qt/app.py:1050 msgid "Global" msgstr "ทั่วโลก" #: qt/app.py:1051 msgid "Root" msgstr "รูท" #: qt/app.py:1052 msgid "Home" msgstr "โฮม" #: qt/app.py:1067 msgid "Backup folders" msgstr "โฟลเดอร์สำรองข้อมูล" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "ชื่อสแนปช็อต" #: qt/app.py:1202 #, fuzzy 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:1294 #, fuzzy, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "สำรองข้อมูลไฟล์ท้องถิ่นก่อนเขียนทับหรือลบ\n" "ลบพร้อมคำติดท้าย {suffix}." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "เวอร์ชันใหม่ของไฟล์จะถูกเปลี่ยนชื่อด้วยคำติดท้าย {suffix} ก่อนที่จะถูกคืนค่า\n" "หากคุณไม่ต้องการใช้งานอีกต่อไป คุณสามารถลบไฟล์เหล่านี้ด้วย {cmd}" #: qt/app.py:1314 #, 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:1347 #, fuzzy msgid "Remove newer elements in original folder." msgstr "ลบไฟล์ที่เป็นเวอร์ชันใหม่กว่าในโฟลเดอร์เดิม" #: qt/app.py:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "คืนค่าไฟล์หรือโฟลเดอร์ที่เลือกไปยังตำแหน่งเดิมและ\n" "ลบไฟล์/โฟลเดอร์ที่ไม่อยู่ในสแนปช็อต\n" "การดำเนินการนี้จะลบไฟล์/โฟลเดอร์ที่ถูกยกเว้นในขณะที่สแนปช็อตถูกสร้างขึ้น!\n" "โปรดระมัดระวังอย่างยิ่ง!!!" #: qt/app.py:1361 #, 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] "" "คุณต้องการคืนค่าไฟล์เหล่านี้ใช่หรือไม่\n" "ไปยังโฟลเดอร์ใหม่ {path}" #: qt/app.py:1370 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "คุณต้องการคืนค่าไฟล์เหล่านี้ใช่หรือไม่" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "คุณแน่ใจว่าต้องการลบไฟล์เวอร์ชันใหม่ทั้งหมดใน {path} หรือไม่?" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" "คุณแน่ใจว่าต้องการลบไฟล์เวอร์ชันใหม่ทั้งหมดในโฟลเดอร์เดิมของคุณหรือไม่?" #: qt/app.py:1393 #, fuzzy msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "คำเตือน: การลบไฟล์ในระบบไฟล์รูทอาจทำให้ระบบของคุณเสียหายได้ทั้งหมด!!!" #: qt/app.py:1623 msgid "Snapshot" msgstr "สแนปช็อต" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "คืนค่า {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "คืนค่า {path} ไปยัง…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "ผู้เขียน" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "การแปล" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "ใบอนุญาต" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 #, fuzzy msgid "System default" msgstr "เพิ่มค่าเริ่มต้น" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 #, fuzzy msgid "translation platform" msgstr "การแปล" #: qt/languagedialog.py:232 #, fuzzy msgid "Your translation" msgstr "การแปล" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "มุมมองบันทึกล่าสุด" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "มุมมองบันทึกสแนปช็อต" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "โปรไฟล์" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "สแนปช็อต" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "ตัวกรอง" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "ทั้งหมด" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "การเปลี่ยนแปลง" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "ข้อผิดพลาด" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "ข้อมูล" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] ข้อผิดพลาด, [I] ข้อมูล, [C] การเปลี่ยนแปลง" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "ถอดรหัสเส้นทาง" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "คัดลอก" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "ถอดรหัส" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "คุณต้องการยกเว้นสิ่งนี้หรือไม่?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "คำถาม" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "ดูบันทึกล่าสุด" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "กำลังทำงาน" #: qt/qttools.py:370 msgid "Today" msgstr "วันนี้" #: qt/qttools.py:377 msgid "Yesterday" msgstr "เมื่อวาน" #: qt/qttools.py:386 msgid "This week" msgstr "สัดาห์นี้" #: qt/qttools.py:393 msgid "Last week" msgstr "สัปดาห์ที่แล้ว" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "นี่ไม่ใช่สแนปช็อต แต่เป็นมุมมองสดของไฟล์ท้องถิ่นของคุณ" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "ตรวจสอบล่าสุดเมื่อ {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "แสดงบันทึกทั้งหมด" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "โปรไฟล์หลัก" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "แก้ไข" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "เพิ่ม" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "ลบ" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&ทั่วไป" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "โหมด" #: qt/settingsdialog.py:152 #, 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\"" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "สถานที่ที่จะบันทึกสแนปช็อต" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "โฟลเดอร์" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "การตั้งค่า SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "โฮสต์" #: qt/settingsdialog.py:204 msgid "Port" msgstr "พอร์ต" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "ผู้ใช้งาน" #: qt/settingsdialog.py:214 msgid "Path" msgstr "เส้นทาง" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "วิธีการเข้ารหัส" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "กุญแจส่วนตัว" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "เลือกไฟล์กุญแจส่วนตัวที่มีอยู่ (ซึ่งมักมีชื่อว่า \"id_rsa\")" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" "สร้างคีย์ SSH ใหม่โดยไม่มีรหัสผ่าน " "(ไม่อนุญาตหากไฟล์กุญแจส่วนตัวถูกเลือกแล้ว)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "รหัสผ่าน" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "บันทึกรหัสผ่านใน Keyring" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" "เก็บรหัสผ่านในแคชสำหรับ Cron (ปัญหาด้านความปลอดภัย: root " "สามารถอ่านรหัสผ่านได้)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "ขั้นสูง" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "เส้นทางสแนปช็อตทั้งหมด" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "กำหนดเวลาตามกำหนดการ" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "ถูกปิดใช้งานแล้ว" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "ทุกครั้งที่เปิดเครื่อง/เริ่มรีบูต" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "ทุก {n} นาที" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "ทุกชั่วโมง" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "ทุก {n} ชั่วโมง" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "ชั่วโมงที่กำหนดเอง" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "ทุกวัน" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "เป็นรูปแบบที่ทำซ้ำตามเวลาที่กำหนด (ระบบ anacron)" #: qt/settingsdialog.py:375 #, fuzzy msgid "When drive gets connected (udev)" msgstr "เมื่อไดรฟ์เชื่อมต่อกับระบบ (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "ทุกสัปดาห์" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "ทุกเดือน" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "ทุกปี" #: qt/settingsdialog.py:383 msgid "Day" msgstr "วัน" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "วันในสัปดาห์" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "ชั่วโมง" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "ชั่วโมง" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "ให้ Back In Time ทำงานซ้ำอย่างต่อเนื่อง " "ซึ่งเป็นประโยชน์หากเครื่องคอมพิวเตอร์ไม่ทำงานอย่างเป็นประจำ" #: qt/settingsdialog.py:442 msgid "Every" msgstr "ทุกๆ" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "ชั่วโมง" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "วัน" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "สัปดาห์" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "เดือน" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&รวมเข้ากับ" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "รวมไฟล์และโฟลเดอร์" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "เพิ่มไฟล์" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "เพิ่มโฟลเดอร์" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&ยกเว้น" #: qt/settingsdialog.py:528 #, fuzzy, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "ไวล์หลายรูปแบบ ({example1}) จะถูกละเว้นไปในโหมด 'SSH ที่เข้ารหัส' \n" "เฉพาะเครื่องหมายดอกจันที่แยกออกเท่านั้นที่อนุญาต ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "ระบุรูปแบบที่ต้องการยกเว้น, ไฟล์ หรือ โฟลเดอร์ที่ต้องการยกเว้น" #: qt/settingsdialog.py:556 #, fuzzy msgid "Highly recommended" msgstr "แนะนำอย่างมาก" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "เพิ่มค่าเริ่มต้น" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "ยกเว้นไฟล์ที่มีขนาดใหญ่กว่า: " #: qt/settingsdialog.py:594 #, fuzzy, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "ยกเว้นไฟล์ที่มีขนาดใหญ่กว่าค่าที่ระบุใน %(prefix)s\n" "เมื่อ 'โหมดการสำรองข้อมูลแบบ rsync เต็มรูปแบบ' ถูกปิดใช้งาน สิ่งนี้จะมีผลกับไฟล์ใหม่เท่านั้น\n" "เนื่องจากสำหรับ rsync นี้เป็นตัวเลือกการถ่ายโอน ไม่ใช่ตัวเลือกในการยกเว้น\n" "ดังนั้นไฟล์ขนาดใหญ่ที่ได้รับการสำรองข้อมูลมาก่อนหน้านี้จะยังคงอยู่ในสแนปช็อต\n" "แม้ว่าไฟล์เหล่านั้นจะเปลี่ยนแปลงลง" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&ลบโดยอัตโนมัติ" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "เก่ากว่า" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "ปี" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "หากพื้นที่ว่างน้อยกว่า" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "หากพื้นที่ว่างของไอโนด์น้อยกว่า" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "การลบอย่างฉลาด" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "เรียกใช้ในพื้นหลังบนโฮสต์ระยะไกล" #: qt/settingsdialog.py:690 #, fuzzy msgid "EXPERIMENTAL" msgstr "ทดลอง" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "เก็บรักษาสแนปช็อตทั้งหมดสำหรับช่วงเวลาล่าสุด" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "วัน" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "เก็บรักษาสแนปช็อตหนึ่งต่อวันสำหรับช่วงเวลาล่าสุด" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "เก็บรักษาสแนปช็อตหนึ่งต่อสัปดาห์สำหรับช่วงเวลาล่าสุด" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "สัปดาห์" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "เก็บรักษาสแนปช็อตหนึ่งต่อเดือนสำหรับช่วงเวลาล่าสุด" #: qt/settingsdialog.py:721 #, fuzzy msgid "month(s)." msgstr "เดือน" #: qt/settingsdialog.py:724 #, fuzzy msgid "Keep one snapshot per year for all years." msgstr "เก็บรักษาสแนปช็อตหนึ่งต่อปีสำหรับทุกปี" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "ไม่ลบสแนปช็อตที่มีชื่อ" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "& ตัวเลือก" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "เปิดใช้งานการแจ้งเตือน" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "ปิดใช้งานการสร้างสแนปช็อตเมื่อใช้งานบนแบตเตอรี่" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "ไม่สามารถรับสถานะพลังงานจากระบบได้" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "เรียกใช้สแนปช็อตเพียงหนึ่งตัวในเวลาเดียวกันเท่านั้น" #: qt/settingsdialog.py:763 #, fuzzy msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "สแนปช็อตอื่น ๆ จะถูกระบบปิดกั้นจนกว่าสแนปช็อตปัจจุบันจะเสร็จสมบูรณ์\n" "นี่เป็นตัวเลือกที่มีผลทั่วโลก ดังนั้นจะมีผลต่อโปรไฟล์ทั้งหมดสำหรับผู้ใช้รายนี้\n" "แต่คุณจำเป็นต้องเปิดใช้งานตัวเลือกนี้สำหรับผู้ใช้อื่น ๆ ด้วย" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "สำรองข้อมูลไฟล์ที่ถูกแทนที่เมื่อกู้คืน" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "ดำเนินการต่อเมื่อเกิดข้อผิดพลาด (เก็บสแนปช็อตที่ไม่สมบูรณ์)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "ใช้ checksum เพื่อตรวจจับการเปลี่ยนแปลง" #: qt/settingsdialog.py:793 #, fuzzy msgid "Take a new snapshot whether there were changes or not." msgstr "สร้างสแนปช็อตใหม่โดยไม่ว่าจะมีการเปลี่ยนแปลงหรือไม่ก็ตาม" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "ระดับบันทึก" #: qt/settingsdialog.py:805 msgid "None" msgstr "ไม่มี" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "&ตัวเลือกขั้นสูง" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "เปลี่ยนตัวเลือกเหล่านี้เท่านั้นหากคุณรู้ว่าต้องการทำอะไรจริงๆ" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "รัน 'rsync' ด้วย '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "เป็นงาน cron" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "บนโฮสต์ระยะไกล" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "เมื่อสร้างสแนปช็อตด้วยตนเอง" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(โปรดติดตั้ง 'nocache' เพื่อเปิดใช้งานตัวเลือกนี้)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "บนเครื่องใช้งานท้องถิ่น" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "เปลี่ยนเส้นทาง stdout เป็น /dev/null ใน cronjobs" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "เปลี่ยนเส้นทาง stderr เป็น /dev/null ใน cronjobs" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "จำกัดการใช้แบนด์วิดธ์ของ rsync" #: qt/settingsdialog.py:918 #, fuzzy msgid "KB/sec" msgstr "KB/sec" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "สงวน ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "สงวนแอตทริบิวต์ขยาย(xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "คัดลอกลิงก์ที่ไม่ปลอดภัย (ทำงานเฉพาะลิงก์ที่เป็นแบบสมบูรณ์)" #: qt/settingsdialog.py:1024 #, fuzzy, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "ตัวเลือกจะต้องอยู่ในเครื่องหมายคำพูด เช่น {example}" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "วางตัวเลือกเพิ่มเติมให้กับ rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "คำนำหน้าที่จะเรียกใช้ก่อนทุกคำสั่งบนเครื่องโฮสต์ระยะไกล\n" "ตัวแปรต้องถูกหนีเอสด้วย $FOO\n" "การตั้งค่านี้ไม่มีผลต่อ rsync ดังนั้นในการเพิ่มคำนำหน้าสำหรับ rsync ให้ใช้ \"%(cbRsyncOptions)s\" พร้อมกับ\n" " %(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "เพิ่มคำนำหน้าให้กับคำสั่ง SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "คุณแน่ใจว่าต้องการลบไฟล์เวอร์ชันใหม่ทั้งหมดใน {name} หรือไม่?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "คุณแน่ใจว่าต้องการลบสแนปช็อตหรือไม่" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "เส้นทางสแนปช็อตทั้งหมด: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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:66 msgid "Use %1 and %2 for path parameters" msgstr "ใช้ %1 และ %2 สำหรับพารามิเตอร์เส้นทาง" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "เฉพาะสแนปช็อตที่แตกต่างเท่านั้น" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "รายการเฉพาะสแนปช็อตที่เท่ากับ: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "ตรวจสอบลึก (มากขึ้นในความแม่นยำ แต่ช้ากว่า)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "ลบ" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "เลือกทั้งหมด" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "ไปยัง" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "& ตัวเลือก" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "คุณไม่สามารถเปรียบเทียบสแนปช็อตกับตัวเองได้" #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "ไม่พบคำสั่ง" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "คุณต้องการลบ \"{file}\" ในสแนปช็อต \"{snapshot_id}\" จริงหรือไม่?" #: qt/snapshotsdialog.py:375 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "คุณต้องการลบ \"{file}\" ในสแนปช็อต {count} รายการ จริงหรือไม่?" #: qt/snapshotsdialog.py:380 #, fuzzy msgid "This cannot be revoked!" msgstr "ไม่สามารถเพิกถอนสิ่งนี้ได้!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "คำเตือน" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "ยกเว้น \"{path}\" จากสแนปช็อตในอนาคตหรือไม่?" #~ msgid "&Snapshot" #~ msgstr "&สแนปช็อต" #~ msgid "&View" #~ msgstr "&ดู" #~ msgid "Config File Help" #~ msgstr "ช่วยเหลือเกี่ยวกับไฟล์กำหนดค่า" #~ msgid "Diff" #~ msgstr "เปรียบเทียบ" #~ msgid "Diff Options" #~ msgstr "ตัวเลือกการเปรียบเทียบ" #~ msgid "Every 10 minutes" #~ msgstr "ทุก 10 นาที" #~ msgid "Every 5 minutes" #~ msgstr "ทุก 5 นาที" #~ msgid "Local encrypted" #~ msgstr "การเข้ารหัสท้องถิ่น" #~ msgid "Modify for Full System Backup" #~ msgstr "ปรับแก้เพื่อการสำรองข้อมูลระบบทั้งหมด" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "โปรไฟล์: \"{name}\"" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "คืนค่าไฟล์หรือโฟลเดอร์ที่เลือกไว้\n" #~ "หากปุ่มนี้แสดงสีเทาออกมา นั่นอาจเป็นเพราะ \"{now}\" ถูกเลือกในรายการสแนปช็อตด้านซ้าย" #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "การตั้งค่า" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "ดูเนื้อหาของดิสก์ปัจจุบัน" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "ดูสแนปช็อตที่ทำไว้ที่ {timestamp}" #~ msgid "You can't include backup folder!" #~ msgstr "คุณไม่สามารถรวมโฟลเดอร์สำรองข้อมูลได้!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "คุณไม่สามารถรวมโฟลเดอร์ย่อยของการสำรองข้อมูลได้!" #~ msgid "You can't remove the last profile!" #~ msgstr "คุณไม่สามารถลบโปรไฟล์สุดท้ายได้!" backintime-1.4.3/common/po/tr.po000066400000000000000000001455001455673541400165430ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-20 07:26+0000\n" "Last-Translator: anilaras \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Uyarı" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Ana profil" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Yerel" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH Gizli Anahtarı" #: common/config.py:304 msgid "encrypted" msgstr "şifrelenmiş" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Şifreleme" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH şifrelenmiş" #: common/config.py:317 msgid "Default" msgstr "Varsayılan" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Profil: \"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Anlık görüntü dizini geçersiz!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "Yedekleme klasörü dahil edilemez." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Yedekleme alt klasörü dahil edilemez." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Hatalı seçenek. {path} bir klasör değil." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Sunucu/Kullanıcı/Profil Kimliği boş olamaz." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Linkleri kopyala (sembolik linklerin hedef dosyalarını kopyalar)" #: common/config.py:498 msgid "Expert Options" msgstr "Uzman Seçenekleri" #: common/config.py:502 #, 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 sshfs'ye bağlı bir paylaşım. sshfs sabit " "bağlantıları desteklemez. Lütfen bunun yerine 'SSH' kipini kullanın." #: common/config.py:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Crontab bulunamadı.\n" "Cron'un yüklü olduğundan emin misiniz?\n" "Eğer değilseniz tüm otomatik yedeklemeleri devre dışı bırakmanız gerekir." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Yeni crontab yazımı başarısız oldu." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Udev zamanlama kipi {mode} ile çalışmıyor" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\" profili zaten var." #: common/configfile.py:736 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 Et" #: common/encfstools.py:156 msgid "Please confirm password" msgstr "Lütfen parolayı onaylayın" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Parola eşleşmiyor." #: common/encfstools.py:178 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." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Anlık görüntü al" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "{mountprocess} , {mountpoint} den ayrılamıyor." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} bulunamadı. Lütfen yükleyin ör. {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Bağlantı noktası {} boş değil." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Profil '{profile}': {mode} kipi için parola giriniz: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "BAŞARISIZ" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "İzinleri geri yükle" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Tamamlandı" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Pille çalışırken yedeklemeyi erteleme" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "%s saniye bekliyor." msgstr[1] "%s saniye bekliyor." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "{snapshot_id} anlık görüntüsü alınamadı." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Sonlandırılıyor" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Klasör oluşturulamadı" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Yapılandırma dosyası kaydediliyor…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "İzinler kaydediliyor…" #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Klasör kaldırılamıyor" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Anlık görüntü al" #: common/snapshots.py:1254 msgid "Success" msgstr "Başarılı" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 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:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "'rsync' sonlandı, çıkış kodu: {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Daha fazla ayrıntı için bakınız; 'man rsync'" #: common/snapshots.py:1272 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:1287 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:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "{new_path} yolu {path} olarak yeniden adlandırılamadı" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Akıllı kaldırma" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Eski anlık görüntüleri kaldırma" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "En az boş alan korunmaya çalışılıyor" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "En az {perc} boş düğümü korumaya çalışıyor" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Şimdi" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "{sshfs} bağlanamadı" #: common/sshtools.py:275 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:418 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:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "{host} için {cipher} şifresi başarısız oldu." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Uzak yol mevcut ancak bir dizin değil." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Uzak yol yazılabilir değil." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Uzak yol çalıştırılabilir değil." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Uzak klasör oluşturulamadı." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Uzak bilgisayar {host} desteklemediği komut: {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Daha fazla yönerge için 'man backintime'a bakın" #: common/sshtools.py:956 #, 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:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Uzak sunucu {host} sabit bağlantıları desteklemiyor" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "\"{pubkey}\" genel ssh anahtarını uzak \"{host}\" ana makineye kopyala" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "\"{user}\" kullanıcısı için parola giriniz" #: qt/app.py:167 msgid "Shortcuts" msgstr "Kısayollar" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Bu klasör,\n" "geçerli seçili anlık görüntüde yok." #: qt/app.py:252 msgid "Add to Include" msgstr "Dahil Et'e Ekle" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Dışla'ya Ekle" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} yapılandırılmamış. Önceki bir yapılandırmayı geri yüklemek ister " "misin?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Anlık görüntü klasörü bulunamadı.\n" "Eğer çıkarılabilir bir sürücüde ise lütfen takın ve Tamam'a tıklayın." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Anlık görüntü al" #: qt/app.py:455 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:458 msgid "Take a snapshot (checksum mode)" msgstr "Anlık görüntü kipi (sağlama toplamı kipi)" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Dosya değişikliklerini algılamak için sağlama toplamlarını kullan." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Anlık görüntü sürecini duraklat" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Anlık görüntü sürecini sürdür" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Anlık görüntü sürecini durdur" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Anlık görüntü listesini yenile" #: qt/app.py:480 msgid "Name snapshot" msgstr "Anlık görüntüyü adlandır" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Anlık görüntüyü sil" #: qt/app.py:488 msgid "View snapshot log" msgstr "Anlık görüntü günlüğünü görüntüle" #: qt/app.py:492 msgid "View last log" msgstr "Son günlüğü görüntüle" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Profilleri yönet…" #: qt/app.py:500 msgid "Shutdown" msgstr "Kapat" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Anlık görüntü tamamlandığında sistemi kapat." #: qt/app.py:504 msgid "Setup language…" msgstr "Kurulum dili…" #: qt/app.py:508 msgid "Exit" msgstr "Çıkış" #: qt/app.py:512 msgid "Help" msgstr "Yardım" #: qt/app.py:516 msgid "Profiles config file" msgstr "Profiller yapılandırma dosyası" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Web Sitesi" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Değişiklik Günlüğü" #: qt/app.py:525 msgid "FAQ" msgstr "SSS" #: qt/app.py:528 msgid "Ask a question" msgstr "Soru Sor" #: qt/app.py:531 msgid "Report a bug" msgstr "Hata Bildir" #: qt/app.py:534 msgid "Translation" msgstr "Çeviri" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Hakkında" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Geri Yükle" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Şuraya geri yükle …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Yukarı" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Gizli dosyaları göster" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Anlık görüntüleri karşılaştır…" #: qt/app.py:627 msgid "&Backup" msgstr "&Yedek" #: qt/app.py:638 msgid "&Restore" msgstr "&Geri Yükle" #: qt/app.py:644 msgid "&Help" msgstr "Y&ardım" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Bu pencereyi kapatırsanız Back In Time, anlık görüntü bittiğinde sisteminizi kapatamaz.\n" "Gerçekten kapatmak istiyor musunuz?" #: qt/app.py:905 msgid "Working:" msgstr "Çalışıyor:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Tamamlandı, yedekleme gerekmiyor" #: qt/app.py:962 msgid "Working" msgstr "Çalışıyor" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Hata" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Gönder" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Hız" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "ETA" #: qt/app.py:1050 msgid "Global" msgstr "Küresel" #: qt/app.py:1051 msgid "Root" msgstr "Kök Dizini" #: qt/app.py:1052 msgid "Home" msgstr "Ev" #: qt/app.py:1067 msgid "Backup folders" msgstr "Yedekleme klasörleri" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Anlık Görüntü Adı" #: qt/app.py:1202 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:1294 #, 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:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "Dosyaların daha yeni sürümleri, geri yüklemeden önce sonunda {suffix} olacak şekilde yeniden adlandırılacaktır.\n" "Artık bunlara ihtiyacınız yoksa {cmd} ile kaldırabilirsiniz" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Özgün klasördeki daha yeni ögeleri kaldır." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Seçilen dosya ya da klasörleri özgün hedefe geri yükle\n" "ve anlık görüntüde bulunmayan dosya ya da klasörleri sil.\n" "Son derece dikkatli olun çünkü bu, \n" "anlık görüntünün alınması sırasında dışlalan dosya ve klasörlerin\n" "silinmesine neden olacaktır." #: qt/app.py:1361 #, 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:1370 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:1385 #, 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:1388 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:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" "UYARI: dosya sistemi kök dizinindeki dosyaları silmek tüm sisteminizi " "bozabilir!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Anlık görüntü" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "{path} geri yükle" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "{path} geri yükle, hedef …" #: qt/app.py:1819 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/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Yazanlar" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Çeviriler" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Lisans" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Kurulum dili" #: qt/languagedialog.py:87 msgid "System default" msgstr "Sistem öntanımlısı" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "İşletim sistemi dilini kullanın." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Çevrilen: {percent}" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "çeviri platformu" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Sizin çeviriniz" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Son Günlük Görünüm" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Anlıık Görüntü Günlük Görünümü" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Profil" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Anlık görüntüler" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Filtrele" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Tümü" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Değişiklikler" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Hatalar" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Bilgi" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Hata, [I] Bilgi, [C] Değiştir" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "kod çözümleme yolları" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Kopyala" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Kod Çözümle" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Bunu dışlamak istiyor musun?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Soru" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Son Günlüğü Görüntüle" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "{appname}ʼı Başlat" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Çalışıyor…" #: qt/qttools.py:370 msgid "Today" msgstr "Bugün" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Dün" #: qt/qttools.py:386 msgid "This week" msgstr "Bu hafta" #: qt/qttools.py:393 msgid "Last week" msgstr "Geçen hafta" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Son denetim {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Tüm Günlüğü Göster" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Profilleri yönet" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Düzenle" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Ekle" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Kaldır" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Genel" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Kip" #: qt/settingsdialog.py:152 #, 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." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Anlık görüntülerin nereye kaydedileceği" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Klasör" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH Ayarları" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Ana Makine" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Bağlantı Noktası" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Kullanıcı" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Yol" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Şifre" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Gizli Anahtar" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Parola" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Parolayı Anahtarlığa Kaydet" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Gelişmiş" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Tam anlık görüntü yolu" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Zamanlama" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Devre dışı" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Her Başlatıldığında/Yeniden Başlatıldığında" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, 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:363 msgid "Every hour" msgstr "Saatte Bir" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, 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:372 msgid "Custom hours" msgstr "Özel saatler" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Her gün" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Tekrar Tekrar (anakron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Sürücü bağlandığında (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Her hafta" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Her ay" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Her yıl" #: qt/settingsdialog.py:383 msgid "Day" msgstr "Gün" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "Haftanın günü" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Saat" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Saat" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Her" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Saat" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Gün" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Hafta" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Ay" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Dahil Et" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Dosyaları ve klasörleri dahil et" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Dosya Ekle" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Klasör Ekle" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "Dışl&a" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Joker karakterler ({example1}) 'SSH şifreli' kipinde yok sayılır.\n" "Yalnızca tek veya çift yıldızlara izin verilir ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Dışlama deseni, dosyalar veya klasörler" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Şiddetle tavsiye edilir" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Öntanımlı Ekle" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Şundan büyük dosyaları dışla: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "%(prefix)s içindeki değerden büyük dosyaları dışla.\n" "'Tam rsync kipi' devre dışı bırakıldığında, bu yalnızca yeni dosyaları etkiler\n" "çünkü rsync için bu bir dışlama seçeneği değil, bir aktarım seçeneğidir.\n" "Böylece daha önce yedeklenmiş büyük dosyalar,\n" "değişseler bile anlık görüntülerde kalır." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&Kendiliğinden Kaldır" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Daha eski" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Yıl" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Eğer boş alan daha az ise" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Eğer boş düğüm daha az ise" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Akıllı kaldırma:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Uzak makinede arka planda çalıştır." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "DENEYSEL" #: qt/settingsdialog.py:696 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:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "gün" #: qt/settingsdialog.py:703 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:710 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:714 msgid "week(s)." msgstr "hafta" #: qt/settingsdialog.py:717 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:721 msgid "month(s)." msgstr "ay" #: qt/settingsdialog.py:724 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:733 msgid "Don't remove named snapshots." msgstr "Adlandırılmış anlık görüntüleri kaldırma." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Seçenekler" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Bildirimleri etkinleştir" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Pille çalışırken anlık görüntüleri devre dışı bırak" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Güç durumu sistemden alınamıyor" #: qt/settingsdialog.py:761 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:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Geçerli anlık görüntü tamamlanana kadar diğer anlık görüntüler engellenir.\n" "Bu küresel bir seçenektir. Böylece bu kullanıcı için tüm profilleri etkileyecektir.\n" "Ancak bunu diğer tüm kullanıcılar için de etkinleştirmeniz gerekir." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Geri yüklemede değiştirilen dosyaları yedekle" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Hatalarda devam et (eksik anlık görüntüleri tut)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Değişiklikleri algılamak için sağlama toplamı kullan" #: qt/settingsdialog.py:793 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:800 msgid "Log Level" msgstr "Günlük Düzeyi" #: qt/settingsdialog.py:805 msgid "None" msgstr "Yok" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "U&zman Seçenekleri" #: qt/settingsdialog.py:830 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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "'rsync' komutunu '{cmd}' ile çalıştır:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "zamanlanmış görev olarak" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "uzak makinede" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "elle anlık görüntü alınırken" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Bu seçeneği etkinleştirmek için lütfen 'nocache' kurun)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "yerel makinede" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Zamanlanmış görevlerde stdout'u /dev/null'a yönlendir." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Zamanlanmış görevlerde stderr'ı /dev/null'a yönlendir." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "rsync bant kullanımını sınırla" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/sn" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "ACL koru" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Genişletilmiş öznitelikleri koru (xattr)" #: qt/settingsdialog.py:991 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:1024 #, 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:1027 msgid "Paste additional options to rsync" msgstr "Ek seçenekleri rsyncʼa yapıştır" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Uzak makinedeki her komuttan önce çalıştırılacak önek.\n" "Değişkenlerin \\$FOO ile kaçışı gerekir.\n" "Bu rsync'e dokunmaz. \n" "Bu nedenle, rsync'e bir önek eklemek için\n" "\"%(cbRsyncOptions)s\" ile %(rsync_options_value)s kullanın\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "öntanımlı" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "SSH komutlarına ön ek ekle" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Uzak makinenin çevrim içi olduğunu denetle" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Uyarı: Devre dışı bırakılırsa ve\n" "uzak makine kullanılamıyorsa,\n" "bu bazı tuhaf hatalara yol açabilir." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Uzak makinenin tüm gerekli komutları desteklediğini denetle" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Uyarı: Devre dışı bırakılırsa ve uzak makine\n" "gerekli tüm komutları desteklemiyorsa,\n" "bu bazı garip hatalara yol açabilir." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Yapılandırma Geri Yükle" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Kullanıcı Geri Çağrısını Düzenle" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Yeni profil" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Profili yeniden adlandır" #: qt/settingsdialog.py:1142 #, 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:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "SSH için özel anahtar dosyası seçmediniz.\n" "Yeni parolasız genel/özel anahtar çifti oluşturmak ister misiniz?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Özel anahtar dosyası \"{file}\" yok." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Parolasız oturum açmayı etkinleştirmek için\n" "genel SSH anahtarınızı uzak makineye kopyalamak ister misiniz?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "{host} makinenin özgünlüğü belirlenemiyor.\n" "\n" "{keytype} anahtar parmak izi:" #: qt/settingsdialog.py:1659 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! Bunu 'known_hosts' dosyanıza eklemek " "ister misiniz?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Dışlama deseni" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Dosyayı dışla" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Klasörü dışla" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Dosyayı dahil et" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Klasörü dahil et" #: qt/settingsdialog.py:1930 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:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "{path} yolunda yeni SSH anahtarı oluşturulamadı" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Tam anlık görüntü yolu: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "etkin" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "devre dışı" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Ayarları Geri Yükle" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Lütfen {appName} yapılandırmasını geri yüklemek istediğiniz anlık görüntüye gidin. Yol şöyle görünebilir:\n" "{samplePath}\n" "\n" "Anlık görüntüleriniz uzak bir sürücüdeyse veya şifrelenmişlerse, önce bunları elle bağlamanız gerekir. SSH kipini kullanıyorsanız, uzak makine için genel anahtarla oturum açmayı da ayarlamanız gerekebilir.\n" "'man backintime'a bir göz atın." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Yapılandırma bulunamadı" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "kullanıcı geri çağrı betiğinde shebang (#!/bin/sh) satırı yok." #: qt/settingsdialog.py:2466 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." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Yol parametreleri olarak %1 ve %2 kullan" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Yalnızca fark anlık görüntüleri" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Yalnızca şuna eşit anlık görüntüleri listele: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Derin denetim (daha doğru, ancak yavaş)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Sil" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Tümünü Seç" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Karşılaştır" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Git" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Seçenekler" #: qt/snapshotsdialog.py:330 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:338 msgid "Command not found" msgstr "Komut bulunamadı" #: qt/snapshotsdialog.py:369 #, 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:375 #, 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:380 msgid "This cannot be revoked!" msgstr "Bu işlem geri alınamaz!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "UYARI" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "{path} yolu gelecek anlık görüntülerden dışlansın mı?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " ve kullanıcınızı 'fuse' grubuna ekleyin" #, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "\"%s\" bir sembolik bağlantı. Bağlantılı hedef, siz onu dahil edene kadar yedeklenmeyecek.\n" #~ "Bunun yerine sembolik bağlantının hedefini dahil etmek ister misiniz?" #~ msgid "&Snapshot" #~ msgstr "%Anlık görüntüler" #~ msgid "&View" #~ msgstr "&Görünüm" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Config File Help" #~ msgstr "Yapılandırma Dosyası Yardım" #~ msgid "Diff" #~ msgstr "Fark" #~ msgid "Diff Options" #~ msgstr "Fark Seçenekleri" #~ msgid "Error:" #~ msgstr "Hata:" #~ msgid "Every 10 minutes" #~ msgstr "Her 10 dakikada bir" #~ msgid "Every 12 hours" #~ msgstr "12 saatte bir" #~ msgid "Every 30 minutes" #~ msgstr "30 dakikada bir" #~ msgid "Every 4 hours" #~ msgstr "4 saatte bir" #~ msgid "Every 5 minutes" #~ msgstr "Her 5 dakikada bir" #~ msgid "Every 6 hours" #~ msgstr "6 saatte bir" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Tam sistem yedeklemesi, yalnızca kaynakla aynı disk bölümlemesi ile aynı fiziksel disklere geri yüklenecek bir anlık görüntü oluşturabilir; yeni fiziksel disklere veya farklı bölümlere sahip aynı disklere geri yükleme yapmak, bozuk ve kullanılamaz bir sistem ortaya çıkarmaya meyillidir.\n" #~ "\n" #~ "Tam sistem yedeklemesi, özelleştirilmiş olabilecek bazı ayarları geçersiz kılar. Devam etmek istiyor musun?" #~ msgid "Local encrypted" #~ msgstr "Yerel şifrelenmiş" #~ msgid "Modify for Full System Backup" #~ msgstr "Tam Sistem Yedekleme İçin Düzenle" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Profil: \"{name}\"" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Seçilen dosya veya klasörü geri yükle.\n" #~ "Bu düğme devre dışıysa, bunun nedeni büyük olasılıkla soldaki anlık görüntüler listesinde \"{now}\" seçili olmasıdır." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Ayarlar" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Şu andaki disk içeriğini görüntüle" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "{timestamp} oluşturulan anlık görüntüyü görüntüle" #~ msgid "WITH ERRORS !" #~ msgstr "HATALARLA!" #~ msgid "Working..." #~ msgstr "Çalışıyor..." #~ msgid "You can't include backup folder!" #~ msgstr "Yedekleme klasörünü dahil edemezsiniz!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Yedekleme alt klasörünü dahil edemezsiniz!" #~ msgid "You can't remove the last profile!" #~ msgstr "Son profili kaldıramazsınız!" backintime-1.4.3/common/po/uk.po000066400000000000000000001656021455673541400165420ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2023-12-15 08:56+0000\n" "Last-Translator: SomeTr \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.2.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Попередження" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Основний профіль" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Локальний" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Приватний ключ SSH" #: common/config.py:304 msgid "encrypted" msgstr "зашифрований" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Шифрування" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH зашифрований" #: common/config.py:317 msgid "Default" msgstr "За замовчуванням" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Профіль: «{name}»" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Неправильна тека!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "Потрібно обрати хоча б одну теку для резервування!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "Не можна включати теку резервної копії." #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "Не можна включати підтеку резервної копії." #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "Неправильний параметр. {path} не є текою." #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Поля «Сервер», «Користувач» і «Профіль» не можуть бути порожніми." #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "Неможливо записати в: {path}\n" "Ви впевнені, що маєте право на запис?" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "Копіювати посилання (слідувати за символьними посиланнями)" #: common/config.py:498 msgid "Expert Options" msgstr "Розширені налаштування" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "Не вдається знайти crontab.\n" "Ви впевнені, що cron встановлено?\n" "Якщо ні, Вам варто вимкнути все автоматичне резервування." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Не вдалося записати новий crontab." #: common/config.py:1707 #, 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:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "Розклад udev не працює з режимом {mode}" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Профіль «{name}» вже існує." #: common/configfile.py:736 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 password" msgstr "Підтвердіть пароль" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "Пароль не збігається." #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" "encfs версії 1.7.2 і раніших містить помилку в параметрі --reverse. Будь " "ласка, оновіть encfs." #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Зробити копію" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "Не вдається розмонтувати {mountprocess} з {mountpoint}." #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "{} не знайдено. Встановіть, наприклад, {}" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "Точка монтування {} не порожня." #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Профіль «{profile}»: Введіть пароль для {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "НЕВДАЧА" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Відновлення дозволів" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Виконано" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Вимкнення резервування при роботі від батареї" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "Не вдається знайти теку резервних копій.\n" "Якщо вона знаходиться на знімному диску, підключіть його." #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Очікування %s секунда." msgstr[1] "Очікування %s секунди." msgstr[2] "Очікування %s секунд." #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Не вдалося зробити копію {snapshot_id}." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Завершення" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Не вдається створити теку" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "Збереження файлу конфігурації…" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "Збереження дозволів…" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "Незавершену копію {snapshot_id} можна продовжити." #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "Видалення незавершеної копії {snapshot_id} від попереднього запуску" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "Не вдається видалити теку" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Створення резервної копії" #: common/snapshots.py:1254 msgid "Success" msgstr "Успішно" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "Переміщено частково, оскільки вихідні файли зникли (див. «man rsync»)" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "«rsync» завершився з кодом виходу {exit_code}" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "Докладніше див. «man rsync»" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" "Від'ємні значення кодів виходу rsync є сигнальними номерами, див. «kill -l» " "і «man kill»" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Немає змін, резервування не потрібне" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "Не вдається перейменувати {new_path} в {path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Розумне видалення" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Видалення старих копій" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "Спроба зберегти мінімум вільного місця" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "Спроба зберегти мінімум {perc} вільних айнодів" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "Зараз" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "Не вдається змонтувати {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent не знайдено. Переконайтеся, що його встановлено." #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" "Не вдалося розблокувати приватний ключ SSH. Пароль неправильний або " "недоступний для cron." #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "Помилка шифру {cipher} для сервера {host}." #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "Віддалений шлях існує, але це не тека." #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "Неможливо записати у віддалений шлях." #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "Віддалений шлях неможливо виконати." #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "Не вдається створити віддалений шлях." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "Сервер {host} не підтримує {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "Дивіться «man backintime» для подальших інструкцій" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "Перевірка команд на сервері {host} повернула невідому помилку" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "Сервер {host} не підтримує жорсткі посилання" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "Копіювати публічний ключ SSH «{pubkey}» на сервер «{host}»" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Будь ласка, введіть пароль для «{user}»" #: qt/app.py:167 msgid "Shortcuts" msgstr "Скорочення" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" "Такої теки немає\n" "в обраній резервній копії." #: qt/app.py:252 msgid "Add to Include" msgstr "Включити" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Виключити" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "{appName} не налаштовано. Відновити попередні налаштування?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "Не вдається знайти теку резервних копій.\n" "Якщо вона знаходиться на знімному диску, підключіть його і натисніть OK." #: qt/app.py:453 msgid "Take a snapshot" msgstr "Зробити копію" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "Виявляти зміни за датою зміни і розміром." #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "Зробити копію з контрольними сумами" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "Виявляти зміни за контрольними сумами." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Зупинити створення копії" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Продовжити створення копії" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Скасувати створення копії" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "Оновити список копій" #: qt/app.py:480 msgid "Name snapshot" msgstr "Назвати копію" #: qt/app.py:484 msgid "Remove snapshot" msgstr "Видалити копію" #: qt/app.py:488 msgid "View snapshot log" msgstr "Переглянути журнал копій" #: qt/app.py:492 msgid "View last log" msgstr "Переглянути останній журнал" #: qt/app.py:496 msgid "Manage profiles…" msgstr "Керувати профілями…" #: qt/app.py:500 msgid "Shutdown" msgstr "Вимкнення" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "Вимкнути систему після завершення резервування." #: qt/app.py:504 msgid "Setup language…" msgstr "Змінити мову…" #: qt/app.py:508 msgid "Exit" msgstr "Вийти" #: qt/app.py:512 msgid "Help" msgstr "Допомога" #: qt/app.py:516 msgid "Profiles config file" msgstr "Файл конфігурації профілів" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Вебсайт" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Історія змін" #: qt/app.py:525 msgid "FAQ" msgstr "Питання та відповіді" #: qt/app.py:528 msgid "Ask a question" msgstr "Задати запитання" #: qt/app.py:531 msgid "Report a bug" msgstr "Повідомити про помилку" #: qt/app.py:534 msgid "Translation" msgstr "Переклад" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Про програму" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Відновити" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "Відновити обрані файли чи теки до їхнього початкового розташування." #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "Відновити до …" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "Відновити обрані файли чи теки до вказаного розташування." #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "Відновити поточну теку і весь її вміст до початкового розташування." #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "Відновити поточну теку і весь її вміст до вказаного розташування." #: qt/app.py:560 msgid "Up" msgstr "Вгору" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Показати приховані файли" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "Порівняти копії…" #: qt/app.py:627 msgid "&Backup" msgstr "&Резервування" #: qt/app.py:638 msgid "&Restore" msgstr "&Відновлення" #: qt/app.py:644 msgid "&Help" msgstr "&Допомога" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "Якщо закрити це вікно, Back In Time не зможе вимкнути Вашу систему після закінчення резервування.\n" "Ви справді хочете закрити вікно?" #: qt/app.py:905 msgid "Working:" msgstr "Виконання:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Виконано, резервування не потрібне" #: qt/app.py:962 msgid "Working" msgstr "Виконання" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Помилка" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Надіслано" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Швидкість" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Залишилося" #: qt/app.py:1050 msgid "Global" msgstr "Загальні" #: qt/app.py:1051 msgid "Root" msgstr "Root" #: qt/app.py:1052 msgid "Home" msgstr "Home" #: qt/app.py:1067 msgid "Backup folders" msgstr "Теки резервування" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Назва копії" #: qt/app.py:1202 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "Робити копію елементів перед їх перезаписом\n" "або видаленням, додаючи до назви {suffix}." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" "Після відновлення новіші версії файлів будуть названі з додаванням {suffix}.\n" "Якщо вони Вам не потрібні, можна видалити їх, використовуючи {cmd}" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "Видалити новіші елементи у початковому розташуванні." #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Відновити обрані файли чи теки до початкового розташування\n" "і видалити файли/теки, яких немає у резервній копії.\n" "Будьте обережні!\n" "Це видалить файли і теки, які були\n" "виключені під час резервування." #: qt/app.py:1361 #, 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:1370 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:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "Бажаєте видалити всі новіші файли в {path}?" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "Бажаєте видалити всі новіші файли в початковому розташуванні?" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "УВАГА: видалення файлів у root може зламати всю Вашу систему!" #: qt/app.py:1623 msgid "Snapshot" msgstr "Копія" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "Відновити {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "Відновити {path} до …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" "Налаштування мови набувають чинності лише після перезапуску Back In Time." #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Автори" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Переклади" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Ліцензія" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "Змінити мову" #: qt/languagedialog.py:87 msgid "System default" msgstr "Як у системі" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "Використовувати мову операційної системи." #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "Перекладено: {percent}" #: qt/languagedialog.py:184 #, 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 українською мовою.\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:216 msgid "translation platform" msgstr "платформу для перекладу" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "Ваш переклад" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Останній журнал" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Журнал копій" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Профіль" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Резервні копії" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Фільтр" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Все" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Зміни" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Помилки" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "Інформація" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Помилка, [I] Інформація, [C] Зміна" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "декодувати шляхи" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Копіювати" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Декодувати" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Бажаєте виключити це?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Питання" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Переглянути останній журнал" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Запуск {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "Виконання…" #: qt/qttools.py:370 msgid "Today" msgstr "Сьогодні" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Вчора" #: qt/qttools.py:386 msgid "This week" msgstr "Цього тижня" #: qt/qttools.py:393 msgid "Last week" msgstr "Попереднього тижня" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "Це НЕ резервна копія, а Ваші файли на комп'ютері" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "Остання перевірка {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Показати повний журнал" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "Керувати профілями" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Редагувати" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Додати" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Видалити" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Загальне" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Режим" #: qt/settingsdialog.py:152 #, 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»." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Де зберігати резервні копії" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Тека" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Налаштування SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Сервер" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Порт" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Користувач" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Шлях" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Шифр" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Приватний ключ" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "Оберіть файл приватного ключа (зазвичай має назву «id_rsa»)" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" "Створити новий ключ SSH без пароля (недоступно, якщо Ви вже обрали файл " "приватного ключа)" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Пароль" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Зберегти пароль у зв'язці ключів" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "Кешувати пароль для Cron (Увага: root може читати пароль)" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "Додатково" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Повний шлях до копії" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Розклад" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Вимкнено" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "При кожному запуску/перезапуску" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Кожну {n} хвилину" msgstr[1] "Кожні {n} хвилини" msgstr[2] "Кожні {n} хвилин" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Щогодини" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Кожну {n} годину" msgstr[1] "Кожні {n} години" msgstr[2] "Кожні {n} годин" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "Вказати години" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "Щодня" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Повторно (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Коли диск приєднано (udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "Щотижня" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "Щомісяця" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "Щороку" #: qt/settingsdialog.py:383 msgid "Day" msgstr "День" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "День" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "Година" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "Години" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" "Запускати Back In Time повторно. Корисно, якщо комп'ютер не працює постійно." #: qt/settingsdialog.py:442 msgid "Every" msgstr "Кожні" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "годин" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "днів" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "тижнів" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "місяців" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "В&ключити" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Включити файли і теки" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Додати файл" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Додати теку" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "Вик&лючити" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "У режимі «SSH зашифрований» символи підстановки ({example1}) ігноруються.\n" "Можна використовувати одну або дві зірочки ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Виключити шаблони, файли або теки" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Настійно рекомендується" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Додати за замовчуванням" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "Виключити файли більші ніж: " #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" "Виключає файли більші ніж %(prefix)s.\n" "Якщо «Full rsync mode» вимкнено, це вплине лише на нові файли,\n" "оскільки для rsync це переміщення, а не виключення.\n" "Тому великі файли, зарезервовані раніше, залишаться в копіях,\n" "навіть якщо вони змінилися." #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&Автоматичне видалення" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Старіше ніж" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "років" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Якщо вільного місця менше ніж" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Якщо вільних айнодів менше ніж" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "Розумне видалення:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "Виконувати у фоні на сервері." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "ЕКСПЕРИМЕНТАЛЬНО" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Зберігати всі копії за останні" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "днів." #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "Зберігати одну копію за день за останні" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "Зберігати одну копію за тиждень за останні" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "тижнів." #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "Зберігати одну копію за місяць за останні" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "місяців." #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "Зберігати одну копію за рік за всі роки." #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "Не видаляти названі резервні копії." #: qt/settingsdialog.py:745 msgid "&Options" msgstr "&Налаштування" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Увімкнути сповіщення" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Вимкнути резервування при роботі від батареї" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "Статус живлення недоступний" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "Створювати одну резервну копію за раз" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "Резервування інших копій буде заблоковано доки триває поточне.\n" "Це глобальне налаштування, отже вплине на всі профілі цього користувача.\n" "Але для інших користувачів його потрібно вмикати окремо." #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "Під час відновлення зберігати копії переміщених файлів" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "Ігнорувати помилки (зберігати незавершені копії)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "Виявляти зміни за контрольними сумами" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "Резервувати незалежно від того, чи були зміни." #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "Рівень ведення журналу" #: qt/settingsdialog.py:805 msgid "None" msgstr "Нічого" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Роз&ширені налаштування" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "Обережно: Змінюйте ці налаштування, тільки якщо Ви знаєте, що робите." #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Запустити «rsync» з «{cmd}»:" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "як cron job" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "на віддаленому сервері" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "під час резервування вручну" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "(Встановіть «nocache», щоб увімкнути)" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "на локальному комп'ютері" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Перенаправити stdout на /dev/null в cronjobs." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Перенаправити stderr на /dev/null в cronjobs." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Обмежити пропускну здатність" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "Кб/с" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Зберігати ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Зберігати додаткові атрибути (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "Копіювати ненадійні посилання (працює лише з абсолютними посиланнями)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "Параметри необхідно брати в лапки, напр. {example}." #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "Додаткові параметри для rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "Префікс для виконання перед кожною командою на сервері.\n" "Екрануйте змінні за допомогою \\$FOO.\n" "Це не стосується rsync. Щоб додати префікс\n" "для rsync, використовуйте «%(cbRsyncOptions)s»\n" "з %(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "за замовчуванням" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Додати префікс до команд SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Перевіряти, чи сервер онлайн" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" "Увага: якщо вимкнено, а сервер\n" "недоступний, це може призвести\n" "до непередбачуваних помилок." #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "Перевіряти, чи підтримує сервер усі необхідні команди" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" "Увага: якщо вимкнено, а сервер не підтримує\n" "всіх необхідних команд, це може\n" "призвести до непередбачуваних помилок." #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "Відновити конфігурацію" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Редаг. user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Новий профіль" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Перейменувати профіль" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "Ви впевнені, що хочете видалити профіль «{name}»?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "Ви не обрали приватний ключ SSH.\n" "Бажаєте згенерувати нову пару публічний/приватний ключ без пароля?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "Приватний ключ «{file}» не існує." #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "Бажаєте скопіювати свій публічний ключ SSH\n" "на сервер, щоб увімкнути безпарольний доступ?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Автентичність сервера {host} не вдалося встановити.\n" "\n" "Відбиток ключа {keytype}:" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" "Перевірте цей відбиток! Бажаєте додати його до свого файлу «known_hosts»?" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "Виключити за шаблоном" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Виключити файл" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Виключити теку" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Включити файл" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Включити теку" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "Ви впевнені, що хочете змінити теку для резервних копій?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "Не вдалося створити новий ключ SSH у {path}" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "Повний шлях до копії: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "увімкнено" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "вимкнено" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Налаштування відновлення" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Оберіть резервну копію, з якої Ви хочете відновити конфігурацію {appName}. Шлях може мати такий вигляд:\n" "{samplePath}\n" "\n" "Якщо резервні копії на віддаленому диску або зашифровані, Вам потрібно спочатку змонтувати їх вручну. В режимі «SSH» також може знадобитися налаштувати доступ із публічним ключем до віддаленого сервера.\n" "Докладніше в «man backintime»." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Не знайдено конфігурації" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "Скрипт user-callback не містить рядка shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang у скрипті user-callback неможливо виконати." #: 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:66 msgid "Use %1 and %2 for path parameters" msgstr "Використовуйте %1 та %2 як параметри шляху" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "Тільки копії, що відрізняються" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Показати лише однакові копії: " #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "Глибока перевірка (більш точна, але повільніша)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "Видалити" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Вибрати все" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "Порівняти" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Перейти до" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "Налаштування" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "Не можна порівнювати резервну копію саму з собою." #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "Команду не знайдено" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "Ви справді хочете видалити {file} з резервної копії {snapshot_id}?" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "Ви справді хочете видалити {file} з {count} резервних копій?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Це не можна буде скасувати!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "ПОПЕРЕДЖЕННЯ" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Виключити {path} з майбутніх резервних копій?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " і додати свого користувача до групи «fuse»" #, fuzzy, python-format #~ msgid "" #~ "\"%s\" is a symlink. The linked target will not be backed up until you include it, too.\n" #~ "Would you like to include the symlinks target instead?" #~ msgstr "" #~ "\"%s\" — посилання. Об'єкт, на який воно вказує, не буде зарезервовано, якщо його не включити.\n" #~ "Включити цей об'єкт у резервну копію?" #~ msgid "&Snapshot" #~ msgstr "&Копія" #~ msgid "&View" #~ msgstr "Пере&гляд" #~ msgid "..." #~ msgstr "…" #~ msgid "Changes & Errors" #~ msgstr "Зміни та помилки" #~ msgid "Config File Help" #~ msgstr "Допомога по файлу конфігурації" #~ msgid "Diff" #~ msgstr "Різн." #~ msgid "Diff Options" #~ msgstr "Параметри різн." #~ msgid "Error:" #~ msgstr "Помилка:" #~ msgid "Every 10 minutes" #~ msgstr "Кожні 10 хвилин" #~ msgid "Every 5 minutes" #~ msgstr "Кожні 5 хвилин" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Повна копія системи створює резервну копію, яку можна відновлювати лише на тому ж фізичному диску (дисках) з тими ж розділами, з якого вона була зроблена; відновлення на інший диск або ж на той самий, але зі зміненими розділами призведе до збоїв у роботі системи.\n" #~ "\n" #~ "Повна копія системи скасує деякі налаштування, які могли бути змінені. Продовжити?" #~ msgid "List only different snapshots" #~ msgstr "Список тільки знімків, що розрізняються" #, fuzzy #~ msgid "Local encrypted" #~ msgstr "Локальний зашифрований" #~ msgid "Modify for Full System Backup" #~ msgstr "Змінити для повного резервування системи" #, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Профіль: {name}" #, python-format #~ msgid "Restore '%s'" #~ msgstr "Відновити '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "Відновлення '%s' до ..." #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Відновити обраний файл або теку.\n" #~ "Ця кнопка неактивна, якщо обрано «{now}» у списку резервних копій ліворуч." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "Налаштування" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "Знімок: %s" #~ msgid "View the current disk contents" #~ msgstr "Перегляд вмісту поточного диска" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Перегляд копії, зробленої {timestamp}" #~ msgid "WITH ERRORS !" #~ msgstr "З ПОМИЛКАМИ!" #~ msgid "Working..." #~ msgstr "Виконання..." #~ msgid "You can't include backup folder!" #~ msgstr "Ви не можете включити теку резервної копії!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Ви не можете включити підтеку резервної копії!" #~ msgid "You can't remove the last profile!" #~ msgstr "Ви не можете видалити останній профіль!" backintime-1.4.3/common/po/vi.po000066400000000000000000001444351455673541400165420ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-27 04:41+0000\n" "Last-Translator: vdquynh \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.3.1\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "Cảnh báo" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "Hồ sơ chính" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "Cục bộ" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "Khóa SSH riêng" #: common/config.py:304 msgid "encrypted" msgstr "mã hóa" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "Mã hóa" #: common/config.py:310 msgid "SSH encrypted" msgstr "Đã mã hóa SSH" #: common/config.py:317 msgid "Default" msgstr "Mặc định" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "Hồ sơ: “{name}”" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "Thư mục snapshot không hợp lệ!" #: common/config.py:361 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:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "“{path}” không phải là một thư mục." #: common/config.py:457 #, fuzzy msgid "Host/User/Profile-ID must not be empty." msgstr "Máy chủ/Người dùng/ ID-Hồ sơ không được để trống." #: common/config.py:467 common/config.py:514 #, 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:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 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:498 msgid "Expert Options" msgstr "Tùy chọn dành cho chuyên gia" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "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." #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "Ghi crontab mới thất bại." #: common/config.py:1707 #, 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:1722 #, 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:1733 #, fuzzy, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "Hồ sơ \"{name}\" đã tồn tại." #: common/configfile.py:736 #, fuzzy msgid "The last profile cannot be removed." msgstr "Lệnh này không thể thu hồi." #: common/encfstools.py:92 #, python-brace-format msgid "Can't mount '{command}'" msgstr "Không thể gắn kết '{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 "" #: common/encfstools.py:156 msgid "Please confirm password" msgstr "Vui lòng xác nhận mật khẩu" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "Chụp snapshot" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "Hồ sơ '{profile}': Nhập mật khẩu cho {mode}: " #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "THẤT BẠI" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "Khôi phục quyền hạn" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "Xong" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "Trì hoãn sao lưu khi đang sử dụng pin" #: common/snapshots.py:770 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:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "Đang chờ %s giây." #: common/snapshots.py:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "Chụp snapshot {snapshot_id} thất bại." #: common/snapshots.py:826 msgid "Finalizing" msgstr "Đang hoàn thiện" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "Không tạo được thư mục" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "Đang lưu tập tin cấu hình..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "Đang lưu quyền hạn..." #: common/snapshots.py:1150 #, 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:1169 #, 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:1179 msgid "Can't remove folder" msgstr "Không thể xóa thư mục" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "Đang chụp snapshot" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "Không có gì thay đổi, không cần snapshot mới" #: common/snapshots.py:1318 #, 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:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "Xóa thông minh" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "Đang xóa các snapshot cũ" #: common/snapshots.py:1698 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:1737 #, 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:2821 qt/app.py:1616 msgid "Now" msgstr "Vừa mới đây" #: common/sshtools.py:215 #, fuzzy, python-brace-format msgid "Can't mount {sshfs}" msgstr "Không thể gắn kết {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 #, fuzzy msgid "Remote path is not executable." msgstr "Shebang trong đoạn script user-callback không được thực thi." #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "Không tạo được thư mục." #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, 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:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "Vui lòng nhập mật khẩu cho “{user}”" #: qt/app.py:167 msgid "Shortcuts" msgstr "Lối tắt" #: qt/app.py:187 #, fuzzy 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:252 msgid "Add to Include" msgstr "Thêm vào Bao gồm" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "Thêm vào Loại trừ" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" "{appName} chưa được cấu hình. Bạn có muốn khôi phục cấu hình trước đó không?" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "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:453 #, fuzzy msgid "Take a snapshot" msgstr "Chụp snapshot" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 #, fuzzy msgid "Take a snapshot (checksum mode)" msgstr "Chụp snapshot với checksum" #: qt/app.py:460 #, fuzzy msgid "Use checksums for file change detection." msgstr "Dùng checksum để phát hiện có thay đổi." #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "Tạm dừng quá trình chụp snapshot" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "Tiếp tục quá trình chụp snapshot" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "Dừng hẳn quá trình chụp snapshot" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "Tải lại danh sách snapshot" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "Chụp snapshot" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "Xóa snapshot" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "Xem nhật ký Snapshot" #: qt/app.py:492 #, fuzzy msgid "View last log" msgstr "Xem nhật ký cuối cùng được lưu" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "Hồ sơ chính" #: qt/app.py:500 msgid "Shutdown" msgstr "Tắt máy" #: qt/app.py:502 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:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "Thoát" #: qt/app.py:512 msgid "Help" msgstr "Trợ giúp" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "Đang lưu tập tin cấu hình" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "Trang web" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "Nhật ký thay đổi" #: qt/app.py:525 msgid "FAQ" msgstr "Câu hỏi thường gặp" #: qt/app.py:528 msgid "Ask a question" msgstr "Đặt câu hỏi" #: qt/app.py:531 msgid "Report a bug" msgstr "Báo lỗi" #: qt/app.py:534 #, fuzzy msgid "Translation" msgstr "Phiên dịch" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "Giới thiệu" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "Khôi phục" #: qt/app.py:542 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:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "Khôi phục đến …" #: qt/app.py:547 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:552 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:557 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:560 msgid "Up" msgstr "Lên" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "Hiện tập tin ẩn" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "Chụp snapshot" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "&Khôi phục" #: qt/app.py:644 msgid "&Help" msgstr "&Trợ giúp" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" 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.\n" "Bạn có thật sự muốn đóng cửa sổ?" #: qt/app.py:905 msgid "Working:" msgstr "Đang chạy:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "Xong, không cần sao lưu" #: qt/app.py:962 msgid "Working" msgstr "Đang chạy" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "Lỗi" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "Đã gửi" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "Tốc độ" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "Còn lại" #: qt/app.py:1050 msgid "Global" msgstr "Toàn cục" #: qt/app.py:1051 msgid "Root" msgstr "Thư mục Root" #: qt/app.py:1052 msgid "Home" msgstr "Thư mục Home" #: qt/app.py:1067 msgid "Backup folders" msgstr "Các thư mục sao lưu" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "Đổi tên snapshot" #: qt/app.py:1202 #, fuzzy 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" #: qt/app.py:1294 #, fuzzy, 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 tập tin cục bộ." #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "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/app.py:1314 #, fuzzy 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:1347 #, fuzzy 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:1348 #, fuzzy msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" "Khôi phục các tập tin và thư mục được chọn đến thư mục đích và\n" "xóa các tập tin / thư mục không có trong snapshot.\n" "Việc này sẽ xóa bỏ các tệp tin / thư mục đã bị loại trừ trong khi chụp snapshot.\n" "Hãy cực kỳ cẩn thận!!!" #: qt/app.py:1361 #, 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] "" "Bạn có thật sự muốn khôi phục các tập tin\n" "đến thư mục mới {path}" #: qt/app.py:1370 #, fuzzy 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 tập tin" #: qt/app.py:1385 #, 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:1388 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:1393 #, fuzzy msgid "" "WARNING: Deleting files in filesystem root could break your whole 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:1623 msgid "Snapshot" msgstr "Snapshot" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "Khôi phục {path}" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "Khôi phục {path} đến …" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "Tác giả" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "Phiên dịch" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "Giấy phép" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 #, fuzzy msgid "System default" msgstr "mặc định" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 #, fuzzy msgid "translation platform" msgstr "Phiên dịch" #: qt/languagedialog.py:232 #, fuzzy msgid "Your translation" msgstr "Phiên dịch" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "Xem nhật ký được lưu lần cuối cùng" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "Xem nhật ký snapshot" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "Hồ sơ" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "Snapshot" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "Bộ lọc" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "Tất cả" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "Có thay đổi" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "Có lỗi" #: qt/logviewdialog.py:110 qt/messagebox.py:71 #, fuzzy msgid "Information" msgstr "Thông tin" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] Lỗi, [I] Thông tin, [C] Thay đổi" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "giải mã đường dẫn" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "Sao chép" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "Giải mã" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "Bạn có muốn loại trừ mục này ra?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "Thắc mắc" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "Xem nhật ký cuối cùng được lưu" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "Bắt đầu {appname}" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "Đang chạy" #: qt/qttools.py:370 msgid "Today" msgstr "Hôm nay" #: qt/qttools.py:377 msgid "Yesterday" msgstr "Hôm qua" #: qt/qttools.py:386 msgid "This week" msgstr "Tuần này" #: qt/qttools.py:393 msgid "Last week" msgstr "Tuần trước" #: qt/qttools.py:539 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:544 #, python-brace-format msgid "Last check {time}" msgstr "Kiểm tra lần cuối: {time}" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "Hiện toàn bộ Nhật ký" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "Hồ sơ chính" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "Sửa" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "Thêm" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "Gỡ bỏ" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&Tổng quan" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "Chế độ" #: qt/settingsdialog.py:152 #, 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\"." #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "Nơi lưu các snapshot" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "Thư mục" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "Cài đặt SSH" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "Máy chủ" #: qt/settingsdialog.py:204 msgid "Port" msgstr "Cổng" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "Người dùng" #: qt/settingsdialog.py:214 msgid "Path" msgstr "Đường dẫn" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "Mã hóa Cipher" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "Khóa riêng" #: qt/settingsdialog.py:236 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:246 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:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "Mật khẩu" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "Lưu mật khẩu vào Keyring" #: qt/settingsdialog.py:291 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:306 msgid "Advanced" msgstr "Nâng cao" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "Đường dẫn snapshot đầy đủ" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "Lịch trình" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "Đang tắt" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "Mỗi lần khởi động máy" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "Mỗi {n} phút" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "Mỗi giờ" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "Mỗi {n} giờ" #: qt/settingsdialog.py:372 #, fuzzy msgid "Custom hours" msgstr "Theo giờ tùy chỉnh" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "Hàng ngày" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "Theo định kỳ (anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "Khi ổ cứng kết nối thành công (udev)" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "Hàng tuần" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "Hàng tháng" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "Hàng năm" #: qt/settingsdialog.py:383 msgid "Day" msgstr "ngày" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "ngày thường" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "giờ" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "giờ" #: qt/settingsdialog.py:435 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:442 msgid "Every" msgstr "Mỗi" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "Giờ" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "Ngày" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "Tuần" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "Tháng" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&Bao gồm" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "Bao gồm các tập tin và thư mục" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "Thêm tập tin" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "Thêm thư mục" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&Ngoại trừ" #: qt/settingsdialog.py:528 #, fuzzy, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" "Ký tự đại diện ({example1}) sẽ bị phớt lờ trong chế độ 'đã mã hóa SSH'.\n" "Chỉ có dấu sao đơn hoặc đôi là được phép ({example2})" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "Loại trừ các pattern, tập tin hoặc thư mục" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "Rất khuyến khích" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "Thêm mặc định" #: qt/settingsdialog.py:592 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:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." 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:616 msgid "&Auto-remove" msgstr "Tự động &xóa" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "Cũ hơn" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "Năm" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "Nếu dung lượng trống còn ít hơn" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "Nếu số inode trống còn ít hơn" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "Xóa thông minh" #: qt/settingsdialog.py:689 #, fuzzy msgid "Run in background on remote host." msgstr "Chạy ngầm trên máy chủ từ xa." #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "THỬ NGHIỆM" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "Giữ tất cả snapshot trong" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "ngày vừa qua" #: qt/settingsdialog.py:703 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:710 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:714 #, fuzzy msgid "week(s)." msgstr "tuần vừa qua" #: qt/settingsdialog.py:717 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:721 #, fuzzy msgid "month(s)." msgstr "tháng vừa qua" #: qt/settingsdialog.py:724 #, fuzzy 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:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "Đừng xóa các bản snapshot đã được đổi tên" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "Tùy &chọn" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "Bật thông báo" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "Tắt chụp snapshot khi đang dùng pin" #: qt/settingsdialog.py:758 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:761 msgid "Run only one snapshot at a time" msgstr "Chỉ chạy từng snapshot một" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "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:772 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:785 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:789 msgid "Use checksum to detect changes" msgstr "Dùng checksum để phát hiện có thay đổi" #: qt/settingsdialog.py:793 #, fuzzy 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:800 msgid "Log Level" msgstr "Ghi lại mức độ" #: qt/settingsdialog.py:805 msgid "None" msgstr "Không có gì" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "Tùy chọn chuyên &gia" #: qt/settingsdialog.py:830 #, fuzzy msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" "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:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "Chạy 'rsync' với '{cmd}':" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "bằng cron job" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "trên máy chủ từ xa" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "khi đang chụp snapshot thủ công" #: qt/settingsdialog.py:876 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:883 msgid "on local machine" msgstr "trên máy cục bộ" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "Chuyển hướng stdout vào /dev/null trong cronjob." #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "Chuyển hướng stderr vào /dev/null trong cronjob." #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "Giới hạn sử dụng băng thông rsync" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/giây" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "Giữ nguyên ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "Giữ nguyên thuộc tính mở rộng (xattr)" #: qt/settingsdialog.py:991 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:1024 #, fuzzy, 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:1027 msgid "Paste additional options to rsync" msgstr "Dán các tùy chọn phụ vào rsync" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" "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:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "mặc định" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "Thêm tiền tố vào các lệnh SSH" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "Kiểm tra liệu máy chủ từ xa có online" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "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:1071 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:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "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:1089 msgid "Restore Config" msgstr "Khôi phục cấu hình" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "Sửa user-callback" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "Hồ sơ mới" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "Đổi tên hồ sơ" #: qt/settingsdialog.py:1142 #, fuzzy, 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:1416 #, 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 "" "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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "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:1469 #, 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:1621 msgid "" "Would you like to copy your public SSH key to the\n" "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:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" "Không thể xác thực máy chủ {host}.\n" "\n" "Vân tay khóa {keytype} là:" #: qt/settingsdialog.py:1659 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:1822 msgid "Exclude pattern" msgstr "Loại trừ pattern" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "Loại trừ tập tin" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "Loại trừ thư mục" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "Bao gồm tập tin" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "Bao gồm thư mục" #: qt/settingsdialog.py:1930 #, fuzzy 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:1955 #, 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:2014 msgid "Full snapshot path: " msgstr "Đường dẫn đầy đủ của snapshot: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "đang bật" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "đang tắt" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "Cài đặt khôi phục" #: qt/settingsdialog.py:2125 #, fuzzy, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" "Vui lòng điều hướng đến bản snapshot mà bạn muốn khôi phục cấu hình của {appName}. Đường dẫn có thể trông như thế này:\n" "{samplePath}\n" "\n" "Nếu bản snapshot của bạn ở trên ổ cứng từ xa hoặc nếu nó đã được mã hóa, bạn cần phải gắn kết nó trước tiên. Nếu bạn dùng chế độ SSH, bạn cũng có thể cần thiết lập khóa công khai đăng nhập vào máy chủ từ xa.\n" "Xem qua 'man backintime'." #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "Không tìm thấy cấu hình" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "Đoạn script user-callback không có dòng shebang (#!/bin/sh)." #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." msgstr "Shebang trong đoạn script user-callback không được thực thi." #: qt/snapshotsdialog.py:51 msgid "Options about comparing snapshots" msgstr "" #: qt/snapshotsdialog.py:58 msgid "Command" msgstr "Lệnh" #: qt/snapshotsdialog.py:62 msgid "Parameters" msgstr "Tham số" #: qt/snapshotsdialog.py:66 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:110 msgid "Differing snapshots only" msgstr "Chỉ những snapshot khác nhau" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "Chỉ những snapshot giống với: " #: qt/snapshotsdialog.py:129 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:150 msgid "Delete" msgstr "Xóa" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "Chọn tất cả" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "Đi tới" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "&Tùy chọn" #: qt/snapshotsdialog.py:330 #, fuzzy 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:338 msgid "Command not found" msgstr "Không tìm thấy lệnh" #: qt/snapshotsdialog.py:369 #, fuzzy, 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:375 #, fuzzy, 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 {count} snapshot?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "Lệnh này không thể thu hồi!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "CẢNH BÁO" #: qt/snapshotsdialog.py:396 #, fuzzy, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "Loại trừ “{path}” ra khỏi các snapshot trong tương lai?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " và thêm người dùng của bạn vào nhóm 'fuse'" #~ msgid "&Snapshot" #~ msgstr "&Snapshot" #~ msgid "&View" #~ msgstr "&Xem" #~ msgid "Config File Help" #~ msgstr "Trợ giúp về tập tin cấu hình" #~ msgid "Diff Options" #~ msgstr "Cài đặt Diff" #~ msgid "" #~ "Full system backup can only create a snapshot to be restored to the same physical disk(s) with the same disk partitioning as from the source; restoring to new physical disks or the same disks with different partitioning will yield a potentially broken and unusable system.\n" #~ "\n" #~ "Full system backup will override some settings that may have been customized. Continue?" #~ msgstr "" #~ "Sao lưu toàn hệ thống chỉ có thể tạo một bản snapshot để khôi phục vào cùng ổ cứng vật lý với cùng phân vùng ổ cứng y hệt từ nguồn; việc khôi phục vào ổ cứng vật lý mới hoặc cùng ổ cứng nhưng khác phân vùng sẽ sản sinh ra một hệ thống bị hư hỏng và không ổn định.\n" #~ "\n" #~ "Sao lưu toàn hệ thống sẽ ghi đè vài cài đặt đã được tùy chỉnh. Tiếp tục?" #~ msgid "Modify for Full System Backup" #~ msgstr "Sửa đổi cho Sao lưu toàn hệ thống" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "Hồ sơ: “{name}”" #, python-brace-format #~ msgid "" #~ "Restore selected file or folder.\n" #~ "If this button is grayed out this is most likely because \"{now}\" is selected in left hand snapshots list." #~ msgstr "" #~ "Khôi phục tập tin hoặc thư mục được chọn.\n" #~ "Nếu nút này bị mờ đi thì khả năng cao là vì \"{now}\" đang được chọn trong danh sách snapshot ở bên trái." #~ msgid "Settings" #~ msgstr "Cài đặt" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "Xem nội dung của ổ cứng hiện tại" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "Xem bản snapshot được tạo ra tại {timestamp}" #~ msgid "Working..." #~ msgstr "Đang chạy..." #~ msgid "You can't include backup folder!" #~ msgstr "Không được phép bao gồm thư mục sao lưu!" #~ msgid "You can't include backup sub-folder!" #~ msgstr "Không được phép bao gồm thư mục sao lưu con!" #~ msgid "You can't remove the last profile!" #~ msgstr "Bạn không thể xóa hồ sơ duy nhất!" backintime-1.4.3/common/po/zh_CN.po000066400000000000000000001222011455673541400171100ustar00rootroot00000000000000# 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-01-30 10:08+0100\n" "PO-Revision-Date: 2024-01-30 00:57+0000\n" "Last-Translator: livvytina \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.3.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "警告" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "主要配置" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "本地" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "SSH 私钥" #: common/config.py:304 msgid "encrypted" msgstr "已加密" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "加密" #: common/config.py:310 msgid "SSH encrypted" msgstr "SSH 加密" #: common/config.py:317 msgid "Default" msgstr "默认" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, python-brace-format msgid "Profile: \"{name}\"" msgstr "配置:\"{name}\"" #: common/config.py:349 msgid "Snapshots folder is not valid!" msgstr "快照文件夹无效!" #: common/config.py:361 msgid "You must select at least one folder to back up!" msgstr "您必须选择至少一个文件夹进行备份!" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "不能包含备份文件夹。" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "不能包含备份子文件夹。" #: common/config.py:448 #, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "无效的选项。{path} 不是一个文件夹。" #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "Host/User/Profile-ID 不能为空。" #: common/config.py:467 common/config.py:514 #, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "无法写入:{path}\n" "您确定有写入权限吗?" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "复制链接(废除符号链接)" #: common/config.py:498 msgid "Expert Options" msgstr "专家选项" #: common/config.py:502 #, 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:1598 msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "无法找到 crontab。\n" "您确定已经安装了 cron?\n" "如果没有安装,您应当禁用所有自动备份。" #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "新增定时任务失败。" #: common/config.py:1707 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\"配置已存在。" #: common/configfile.py:736 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 password" msgstr "确认密码" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "密码不一致。" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "encfs 1.7.2 及更早的版本中 --reverse 选项存在缺陷,请更新系统中安装的 encfs 版本。" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "创建快照" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "挂载点 {} 不为空。" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "" #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "失败" #: common/snapshots.py:539 common/snapshots.py:601 msgid "Restore permissions" msgstr "恢复权限" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "已完成" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "依靠电池供电时,延后备份" #: common/snapshots.py:770 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "未能找到快照文件夹。\n" "如果快照位于外置存储器,请先将存储器接入。" #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "等待 %s 秒。" #: common/snapshots.py:809 #, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "创建快照 {snapshot_id} 失败。" #: common/snapshots.py:826 msgid "Finalizing" msgstr "正在完成" #: common/snapshots.py:949 msgid "Can't create folder" msgstr "无法创建文件夹" #: common/snapshots.py:966 msgid "Saving config file…" msgstr "保存配置文件 …" #: common/snapshots.py:1042 msgid "Saving permissions…" msgstr "保存权限 …" #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 msgid "Can't remove folder" msgstr "无法删除文件夹" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "创建快照" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "请查看“man rsync”以了解更多细节" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "没有改动内容,无需创建新快照" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "无法将{new_path}重命名为{path}" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "智能移除" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "移除旧的快照" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "尝试保留最小可用空间" #: common/snapshots.py:1737 #, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "尝试保留至少 {perc} 个可用的 inodes" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "现在" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "无法挂载 {sshfs}" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "ssh-agent 未找到。请确保已安装该工具。" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "无法解锁 SSH 私钥,可能密码不正确或该密码不适用于 cron。" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "远程路径存在但不是一个目录。" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "远程路径不可写。" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "远程路径不可被执行。" #: common/sshtools.py:668 msgid "Couldn't create remote path." msgstr "无法创建远程路径。" #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "远程主机 {host} 不支持 {command}" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "远程主机 {host} 不支持硬链接" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "复制 ssh 公钥\"{pubkey}\"到远程主机 \"{host}\"" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "请输入“{user}”的密码" #: qt/app.py:167 msgid "Shortcuts" msgstr "快捷方式" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "找不到快照文件夹。\n" "如果使用的是可移动磁盘,请连接到电脑,然后按 “确定”。" #: qt/app.py:453 msgid "Take a snapshot" msgstr "创建快照" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "使用校验码探测文件变更。" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 msgid "Refresh snapshot list" msgstr "刷新快照列表" #: qt/app.py:480 msgid "Name snapshot" msgstr "命名快照" #: qt/app.py:484 msgid "Remove snapshot" msgstr "移除快照" #: qt/app.py:488 msgid "View snapshot log" msgstr "查看快照日志" #: qt/app.py:492 msgid "View last log" msgstr "查看最近的日志" #: qt/app.py:496 msgid "Manage profiles…" msgstr "管理配置…" #: qt/app.py:500 msgid "Shutdown" msgstr "关机" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "快照完成后关闭系统。" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "退出" #: qt/app.py:512 msgid "Help" msgstr "帮助" #: qt/app.py:516 msgid "Profiles config file" msgstr "配置文件" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "网站" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "常见问题" #: qt/app.py:528 msgid "Ask a question" msgstr "提出问题" #: qt/app.py:531 msgid "Report a bug" msgstr "报告 BUG" #: qt/app.py:534 msgid "Translation" msgstr "翻译" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "关于" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "还原" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 msgid "Restore to …" msgstr "还原至…" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "上移" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "显示隐藏文件" #: qt/app.py:566 msgid "Compare snapshots…" msgstr "比较快照…" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 msgid "&Restore" msgstr "&还原" #: qt/app.py:644 msgid "&Help" msgstr "&帮助" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" "如果关闭此窗口,则快照完成后 Back In Time 无法关闭你的系统。\n" "是否真的要关闭?" #: qt/app.py:905 msgid "Working:" msgstr "运行中:" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "已完成,无需备份" #: qt/app.py:962 msgid "Working" msgstr "运行中" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "错误" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "已发送" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "速度" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "预计完成时间" #: qt/app.py:1050 msgid "Global" msgstr "全局" #: qt/app.py:1051 msgid "Root" msgstr "根目录" #: qt/app.py:1052 msgid "Home" msgstr "主目录" #: qt/app.py:1067 msgid "Backup folders" msgstr "备份文件夹" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "快照名" #: qt/app.py:1202 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" "在覆盖或移除本地文件前创建带有后缀 {suffix}\n" "的备份副本。" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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:1370 msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "是否确定还原这些文件?" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "您确定要移除 {path} 的所有新文件吗?" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 msgid "Snapshot" msgstr "快照" #: qt/app.py:1660 #, python-brace-format msgid "Restore {path}" msgstr "还原 {path}" #: qt/app.py:1662 #, python-brace-format msgid "Restore {path} to …" msgstr "还原 {path} 至…" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "作者" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "翻译" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "系统默认" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "翻译平台" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "你的翻译" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 msgid "Profile" msgstr "配置" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "快照" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "过滤器" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "所有的" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "变更" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "错误" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "提示信息" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "[E] 错误,[I] 通知,[C] 变更" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "解码路径" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "复制" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "解码" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "你希望排除它吗?" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "问题" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "查看最近的日志" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "启动 {appname}" #: qt/qtsystrayicon.py:169 msgid "Working…" msgstr "运行中…" #: qt/qttools.py:370 msgid "Today" msgstr "今天" #: qt/qttools.py:377 msgid "Yesterday" msgstr "昨天" #: qt/qttools.py:386 msgid "This week" msgstr "本周" #: qt/qttools.py:393 msgid "Last week" msgstr "上周" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "显示完整日志" #: qt/settingsdialog.py:87 msgid "Manage profiles" msgstr "管理配置" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "编辑" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "新增" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "移除" #: qt/settingsdialog.py:127 msgid "&General" msgstr "&常规" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "模式" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "何处保存快照" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "文件夹" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "SSH 设置" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "主机" #: qt/settingsdialog.py:204 msgid "Port" msgstr "端口" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "用户" #: qt/settingsdialog.py:214 msgid "Path" msgstr "路径" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "私钥" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "选择一个已存在的私钥文件(通常名为 \"id_rsa\")" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "密码" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "保存密码至密钥环" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "高级" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "快照完整路径" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "计划任务" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "已禁用" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "每次启动/重启时" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "每{n}分钟" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "每小时" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "每{n}小时" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "自定义小时" #: qt/settingsdialog.py:373 msgid "Every day" msgstr "每天" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "重复(anacron)" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "当设备连接时(udev)" #: qt/settingsdialog.py:376 msgid "Every week" msgstr "每周" #: qt/settingsdialog.py:377 msgid "Every month" msgstr "每月" #: qt/settingsdialog.py:378 msgid "Every year" msgstr "每年" #: qt/settingsdialog.py:383 msgid "Day" msgstr "日" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "工作日" #: qt/settingsdialog.py:409 msgid "Hour" msgstr "小时" #: qt/settingsdialog.py:424 msgid "Hours" msgstr "小时" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 msgid "Every" msgstr "每" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "小时" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "天" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "周" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "月" #: qt/settingsdialog.py:469 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:484 msgid "&Include" msgstr "&包含" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "包含文件和文件夹" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "添加文件" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "添加文件夹" #: qt/settingsdialog.py:521 msgid "&Exclude" msgstr "&排除" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "不包含模式,文件或文件夹" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "强烈推荐" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 msgid "&Auto-remove" msgstr "&自动移除" #: qt/settingsdialog.py:622 msgid "Older than" msgstr "早于" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "年" #: qt/settingsdialog.py:644 msgid "If free space is less than" msgstr "如果磁盘可用空间少于" #: qt/settingsdialog.py:664 msgid "If free inodes is less than" msgstr "如果磁盘可用节点 inodes 少于" #: qt/settingsdialog.py:678 msgid "Smart remove:" msgstr "智能移除:" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "在远程主机后台运行。" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "实验功能" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "保留最新的所有快照" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 msgid "day(s)." msgstr "天。" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "每天保留一个最新的快照" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "每周保留一个最新的快照" #: qt/settingsdialog.py:714 msgid "week(s)." msgstr "周。" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "每月保留一个最新的快照" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "月。" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "每年保留一个快照。" #: qt/settingsdialog.py:733 msgid "Don't remove named snapshots." msgstr "不要移除已命名的快照。" #: qt/settingsdialog.py:745 msgid "&Options" msgstr "选项(_O)" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "启用通知" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "使用电池时禁用快照" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "无法从系统中获取电源状态" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" "在当前快照完成前会阻塞其他快照。\n" "这是一个全局选项,因此会影响当前用户的所有配置。\n" "而你需要为其他所有用户也开启此选项的。" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "忽略错误并继续(保留不完整的快照)" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "使用校验检测变更" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "无论是否有改变,都创建新快照。" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "日志级别" #: qt/settingsdialog.py:805 msgid "None" msgstr "空" #: qt/settingsdialog.py:825 msgid "E&xpert Options" msgstr "专家选项" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "警告:除非您知道自己在干什么,否则不要变更这些选项。" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "在定时任务中将标准输出重定向到 /dev/null。" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "在定时任务中将标准错误重定向到 /dev/null。" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "限制 rsync 使用带宽" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "KB/秒" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "保留 ACL" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "保留扩展属性 (xattr)" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "复制不安全的链接(仅绝对链接有效)" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "选项必须使用引号,例如:{example}。" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "默认" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "检查远程主机是否在线" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "检查远程主机是否支持所有必要的命令" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "还原配置" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "新建配置" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "重命名配置" #: qt/settingsdialog.py:1142 #, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "是否确定删除配置“{name}”?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" "你还没有为 SSH 选择私钥文件。\n" "你想要生成新的无密码公/私密钥对吗?" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "私钥文件 \"{file}\" 不存在。" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" "你想要将 SSH 公钥复制到\n" "远程主机来使用无密码登录吗?" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "排除模式" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "排除文件" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "排除文件夹" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "包含文件" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "包含文件夹" #: qt/settingsdialog.py:1930 msgid "Are you sure you want to change snapshots folder?" msgstr "确定要改变快照文件夹吗?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "在 {path} 创建 SSH 密钥失败" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "快照完整路径: " #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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:66 msgid "Use %1 and %2 for path parameters" msgstr "用 %1 和 %2 作为路径参数" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "深度检查(更精确,但是较慢)" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "删除" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "选择所有" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "比较" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "转到" #: qt/snapshotsdialog.py:179 msgid "Options" msgstr "选项" #: qt/snapshotsdialog.py:330 msgid "You can't compare a snapshot to itself." msgstr "不能将快照与其自身比较。" #: qt/snapshotsdialog.py:338 msgid "Command not found" msgstr "命令未找到" #: qt/snapshotsdialog.py:369 #, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "你确定要删除快照 \"{snapshot_id}\" 的\"{file}\" 吗?" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "你确定要在 {count} 份快照中都删除的 \"{file}\" 吗?" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "该操作无法撤销!" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "警告" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "在未来的快照中排除 \"{path}\"?" #~ msgid " and add your user to group 'fuse'" #~ msgstr " 把用户加入到 'fuse' 组" #, python-format #~ msgid "%s not found in ssh_known_hosts." #~ msgstr "未在 ssh_known_hosts 中找到 %s。" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "快照" #~ msgid "..." #~ msgstr "..." #~ msgid "3DES-CBC" #~ msgstr "3DES-CBC" #~ msgid "AES128-CBC" #~ msgstr "AES128-CBC" #~ msgid "AES128-CTR" #~ msgstr "AES128-CTR" #~ msgid "AES192-CBC" #~ msgstr "AES192-CBC" #~ msgid "AES192-CTR" #~ msgstr "AES192-CTR" #~ msgid "AES256-CBC" #~ msgstr "AES256-CBC" #~ msgid "AES256-CTR" #~ msgstr "AES256-CTR" #~ msgid "ARCFOUR" #~ msgstr "ARCFOUR" #~ msgid "ARCFOUR128" #~ msgstr "ARCFOUR128" #~ msgid "ARCFOUR256" #~ msgstr "ARCFOUR256" #~ msgid "Blowfish-CBC" #~ msgstr "Blowfish-CBC" #~ msgid "Cast128-CBC" #~ msgstr "Cast128-CBC" #~ msgid "Changes & Errors" #~ msgstr "变更 & 错误" #~ msgid "Diff" #~ msgstr "差异" #~ msgid "Diff Options" #~ msgstr "Diff 选项" #~ msgid "Error:" #~ msgstr "错误:" #~ msgid "Every 10 minutes" #~ msgstr "每10分钟" #~ msgid "Every 12 hours" #~ msgstr "每12小时" #~ msgid "Every 30 minutes" #~ msgstr "每30分钟" #~ msgid "Every 4 hours" #~ msgstr "每4小时" #~ msgid "Every 5 minutes" #~ msgstr "每5分钟" #~ msgid "Every 6 hours" #~ msgstr "每6小时" #, python-format #~ msgid "" #~ "Hash collision occurred in hash_id %s. Incrementing global value " #~ "hash_collision and try again." #~ msgstr "在 hash_id %s 中发现哈希碰撞。请递增 hash_collision 的全局设定值并重试。" #~ msgid "List only different snapshots" #~ msgstr "仅列出不同的快照" #~ msgid "Local encrypted" #~ msgstr "本地加密" #~ msgid "Mountprocess lock timeout" #~ msgstr "解除挂载锁超时" #, python-format #~ msgid "Ping %s failed. Host is down or wrong address." #~ msgstr "Ping %s 失败。可能主机未上线或您输入的地址错误。" #, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "配置:\"{name}\"" #, python-format #~ msgid "Restore '%s'" #~ msgstr "还原 '%s'" #, python-format #~ msgid "Restore '%s' to ..." #~ msgstr "还原 '%s' 至 ..." #~ msgid "SSH" #~ msgstr "SSH" #~ msgid "Settings" #~ msgstr "设置" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "快照:%s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "查看当前磁盘内容" #, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "查看于 {timestamp} 创建的快照" #~ msgid "WITH ERRORS !" #~ msgstr "发生错误!" #~ msgid "Working..." #~ msgstr "正在进行..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "不能包含备份文件夹!" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "不能包含备份文件夹的子文件夹!" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "不能删除唯一的配置!" backintime-1.4.3/common/po/zh_TW.po000066400000000000000000001077511455673541400171570ustar00rootroot00000000000000# 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: https://github.com/bit-team/backintime\n" "POT-Creation-Date: 2024-01-30 10:08+0100\n" "PO-Revision-Date: 2023-10-28 19:13+0000\n" "Last-Translator: SomeTr \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.1\n" "X-Launchpad-Export-Date: 2022-11-06 19:32+0000\n" #: common/config.py:50 qt/settingsdialog.py:151 qt/settingsdialog.py:526 msgid "Warning" msgstr "" #: common/config.py:146 common/config.py:289 msgid "Main profile" msgstr "主要設定檔" #: common/config.py:299 common/config.py:304 msgid "Local" msgstr "" #: common/config.py:301 common/config.py:311 qt/settingsdialog.py:1946 msgid "SSH private key" msgstr "" #: common/config.py:304 msgid "encrypted" msgstr "" #: common/config.py:305 common/config.py:312 msgid "Encryption" msgstr "" #: common/config.py:310 msgid "SSH encrypted" msgstr "" #: common/config.py:317 msgid "Default" msgstr "" #: common/config.py:348 common/config.py:360 common/config.py:377 #: common/config.py:388 #, fuzzy, python-brace-format msgid "Profile: \"{name}\"" msgstr "設定檔:\"{name}\"" #: common/config.py:349 #, fuzzy msgid "Snapshots folder is not valid!" msgstr "快照資料夾不存在!" #: common/config.py:361 #, fuzzy msgid "You must select at least one folder to back up!" msgstr "必須選擇一個資料夾進行備份" #: common/config.py:378 msgid "Backup folder cannot be included." msgstr "" #: common/config.py:390 msgid "Backup sub-folder cannot be included." msgstr "" #: common/config.py:448 #, fuzzy, python-brace-format msgid "Invalid option. {path} is not a folder." msgstr "{path} 並不是資料夾!" #: common/config.py:457 msgid "Host/User/Profile-ID must not be empty." msgstr "" #: common/config.py:467 common/config.py:514 #, fuzzy, python-brace-format msgid "" "Can't write to: {path}\n" "Are you sure you have write access?" msgstr "" "不能寫入:{path}\n" "請確認是否有寫入權限?" #: common/config.py:484 #, 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:493 #, 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:497 qt/settingsdialog.py:1004 msgid "Copy links (dereference symbolic links)" msgstr "" #: common/config.py:498 msgid "Expert Options" msgstr "進階選項" #: common/config.py:502 #, 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:1598 #, fuzzy msgid "" "Can't find crontab.\n" "Are you sure cron is installed?\n" "If not you should disable all automatic backups." msgstr "" "找不到crontab檔案!\n" "請確認cron是否安裝?\n" "如果沒有的話,你應該停用所有自動備份。" #: common/config.py:1604 msgid "Failed to write new crontab." msgstr "" #: common/config.py:1707 #, python-brace-format msgid "" "Could not install Udev rule for profile {profile_id}. DBus Service " "'{dbus_interface}' wasn't available" msgstr "" #: common/config.py:1722 #, python-brace-format msgid "Schedule udev doesn't work with mode {mode}" msgstr "" #: common/config.py:1733 #, 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:691 common/configfile.py:790 #, fuzzy, python-brace-format msgid "Profile \"{name}\" already exists." msgstr "\"{name}\"設定檔已經存在!" #: common/configfile.py:736 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 password" msgstr "" #: common/encfstools.py:160 msgid "Password doesn't match." msgstr "" #: common/encfstools.py:178 msgid "" "encfs version 1.7.2 and before has a bug with option --reverse. Please " "update encfs." msgstr "" #: common/encfstools.py:516 common/snapshots.py:914 msgid "Take snapshot" msgstr "進行快照" #: common/mount.py:608 #, python-brace-format msgid "Can't unmount {mountprocess} from {mountpoint}." msgstr "" #: common/mount.py:695 msgid "{} not found. Please install e.g. {}" msgstr "" #: common/mount.py:716 msgid "Mountpoint {} not empty." msgstr "" #: common/password.py:240 #, python-brace-format msgid "Profile '{profile}': Enter password for {mode}: " msgstr "" #: common/snapshots.py:342 common/snapshots.py:593 msgid "FAILED" msgstr "" #: common/snapshots.py:539 common/snapshots.py:601 #, fuzzy msgid "Restore permissions" msgstr "保留存取權限" #: common/snapshots.py:596 qt/app.py:287 qt/app.py:915 qt/app.py:950 #: qt/qtsystrayicon.py:80 msgid "Done" msgstr "完成" #: common/snapshots.py:708 msgid "Deferring backup while on battery" msgstr "" #: common/snapshots.py:770 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in." msgstr "" "快照資料夾不存在。\n" "若存在於移動儲存設備,請插入裝置" #: common/snapshots.py:772 #, python-format msgid "Waiting %s second." msgid_plural "Waiting %s seconds." msgstr[0] "等待%s秒。" #: common/snapshots.py:809 #, fuzzy, python-brace-format msgid "Failed to take snapshot {snapshot_id}." msgstr "快照 {snapshot_id} 執行失敗!!!" #: common/snapshots.py:826 msgid "Finalizing" msgstr "關閉光碟中" #: common/snapshots.py:949 #, fuzzy msgid "Can't create folder" msgstr "無法新增資料夾" #: common/snapshots.py:966 #, fuzzy msgid "Saving config file…" msgstr "保留組態檔..." #: common/snapshots.py:1042 #, fuzzy msgid "Saving permissions…" msgstr "保留存取權限..." #: common/snapshots.py:1150 #, python-brace-format msgid "Found leftover {snapshot_id} which can be continued." msgstr "" #: common/snapshots.py:1169 #, python-brace-format msgid "Removing leftover {snapshot_id} folder from last run" msgstr "" #: common/snapshots.py:1179 #, fuzzy msgid "Can't remove folder" msgstr "無法刪除資料夾" #: common/snapshots.py:1222 msgid "Taking snapshot" msgstr "進行快照" #: common/snapshots.py:1254 msgid "Success" msgstr "" #: common/snapshots.py:1255 msgid "Partial transfer due to error" msgstr "" #: common/snapshots.py:1256 msgid "Partial transfer due to vanished source files (see 'man rsync')" msgstr "" #: common/snapshots.py:1259 #, python-brace-format msgid "'rsync' ended with exit code {exit_code}" msgstr "" #: common/snapshots.py:1268 msgid "See 'man rsync' for more details" msgstr "" #: common/snapshots.py:1272 msgid "" "Negative rsync exit codes are signal numbers, see 'kill -l' and 'man kill'" msgstr "" #: common/snapshots.py:1287 msgid "Nothing changed, no new snapshot necessary" msgstr "" #: common/snapshots.py:1318 #, python-brace-format msgid "Can't rename {new_path} to {path}" msgstr "" #: common/snapshots.py:1636 common/snapshots.py:1688 msgid "Smart remove" msgstr "智慧移除" #: common/snapshots.py:1663 msgid "Removing old snapshots" msgstr "刪除快照" #: common/snapshots.py:1698 msgid "Trying to keep min free space" msgstr "嘗試保留最小剩餘空間" #: common/snapshots.py:1737 #, fuzzy, python-brace-format msgid "Trying to keep min {perc} free inodes" msgstr "嘗試保留最小剩餘空間" #: common/snapshots.py:2821 qt/app.py:1616 msgid "Now" msgstr "現在" #: common/sshtools.py:215 #, python-brace-format msgid "Can't mount {sshfs}" msgstr "" #: common/sshtools.py:275 msgid "ssh-agent not found. Please make sure it is installed." msgstr "" #: common/sshtools.py:418 msgid "" "Could not unlock ssh private key. Wrong password or password not available " "for cron." msgstr "" #: common/sshtools.py:506 #, python-brace-format msgid "Cipher {cipher} failed for {host}." msgstr "" #: common/sshtools.py:653 msgid "Remote path exists but is not a directory." msgstr "" #: common/sshtools.py:658 msgid "Remote path is not writable." msgstr "" #: common/sshtools.py:663 msgid "Remote path is not executable." msgstr "" #: common/sshtools.py:668 #, fuzzy msgid "Couldn't create remote path." msgstr "無法新增資料夾" #: common/sshtools.py:948 #, python-brace-format msgid "Remote host {host} doesn't support {command}" msgstr "" #: common/sshtools.py:952 common/sshtools.py:961 msgid "Look at 'man backintime' for further instructions" msgstr "" #: common/sshtools.py:956 #, python-brace-format msgid "Check commands on host {host} returned unknown error" msgstr "" #: common/sshtools.py:977 #, python-brace-format msgid "Remote host {host} doesn't support hardlinks" msgstr "" #: common/sshtools.py:1060 #, python-brace-format msgid "Copy public ssh-key \"{pubkey}\" to remote host \"{host}\"" msgstr "" #: common/sshtools.py:1062 #, python-brace-format msgid "Please enter password for \"{user}\"" msgstr "" #: qt/app.py:167 msgid "Shortcuts" msgstr "快速鍵" #: qt/app.py:187 msgid "" "This folder doesn't exist\n" "in the current selected snapshot." msgstr "" #: qt/app.py:252 msgid "Add to Include" msgstr "" #: qt/app.py:254 qt/logviewdialog.py:186 msgid "Add to Exclude" msgstr "" #: qt/app.py:339 #, python-brace-format msgid "" "{appName} is not configured. Would you like to restore a previous " "configuration?" msgstr "" #: qt/app.py:368 #, fuzzy msgid "" "Can't find snapshots folder.\n" "If it is on a removable drive please plug it in and then press OK." msgstr "" "快照資料夾不存在。\n" "若存在於移動儲存設備,請插入裝置並確認" #: qt/app.py:453 #, fuzzy msgid "Take a snapshot" msgstr "進行快照" #: qt/app.py:455 msgid "Use modification time & size for file change detection." msgstr "" #: qt/app.py:458 msgid "Take a snapshot (checksum mode)" msgstr "" #: qt/app.py:460 msgid "Use checksums for file change detection." msgstr "" #: qt/app.py:463 qt/qtsystrayicon.py:85 msgid "Pause snapshot process" msgstr "" #: qt/app.py:468 qt/qtsystrayicon.py:89 msgid "Resume snapshot process" msgstr "" #: qt/app.py:472 qt/qtsystrayicon.py:94 msgid "Stop snapshot process" msgstr "" #: qt/app.py:476 #, fuzzy msgid "Refresh snapshot list" msgstr "快照存放位置" #: qt/app.py:480 #, fuzzy msgid "Name snapshot" msgstr "進行快照" #: qt/app.py:484 #, fuzzy msgid "Remove snapshot" msgstr "刪除快照" #: qt/app.py:488 #, fuzzy msgid "View snapshot log" msgstr "進行快照" #: qt/app.py:492 msgid "View last log" msgstr "" #: qt/app.py:496 #, fuzzy msgid "Manage profiles…" msgstr "主要設定檔" #: qt/app.py:500 msgid "Shutdown" msgstr "" #: qt/app.py:502 msgid "Shut down system after snapshot has finished." msgstr "" #: qt/app.py:504 msgid "Setup language…" msgstr "" #: qt/app.py:508 msgid "Exit" msgstr "離開" #: qt/app.py:512 msgid "Help" msgstr "求助" #: qt/app.py:516 #, fuzzy msgid "Profiles config file" msgstr "保留組態檔" #: qt/app.py:519 qt/languagedialog.py:219 msgid "Website" msgstr "網站" #: qt/app.py:522 qt/app.py:1261 msgid "Changelog" msgstr "" #: qt/app.py:525 msgid "FAQ" msgstr "" #: qt/app.py:528 msgid "Ask a question" msgstr "" #: qt/app.py:531 msgid "Report a bug" msgstr "" #: qt/app.py:534 msgid "Translation" msgstr "" #: qt/app.py:537 qt/app.py:1834 msgid "About" msgstr "關於" #: qt/app.py:540 qt/restoredialog.py:47 qt/settingsdialog.py:2212 #: qt/snapshotsdialog.py:140 qt/snapshotsdialog.py:145 msgid "Restore" msgstr "還原" #: qt/app.py:542 msgid "Restore the selected files or folders to the original destination." msgstr "" #: qt/app.py:545 qt/app.py:1421 qt/app.py:1453 qt/snapshotsdialog.py:142 #, fuzzy msgid "Restore to …" msgstr "還原" #: qt/app.py:547 msgid "Restore the selected files or folders to a new destination." msgstr "" #: qt/app.py:552 msgid "" "Restore the currently shown folder and all its contents to the original " "destination." msgstr "" #: qt/app.py:557 msgid "" "Restore the currently shown folder and all its contents to a new " "destination." msgstr "" #: qt/app.py:560 msgid "Up" msgstr "向上" #: qt/app.py:563 qt/settingsdialog.py:2168 msgid "Show hidden files" msgstr "顯示隱藏檔案" #: qt/app.py:566 #, fuzzy msgid "Compare snapshots…" msgstr "進行快照" #: qt/app.py:627 msgid "&Backup" msgstr "" #: qt/app.py:638 #, fuzzy msgid "&Restore" msgstr "還原" #: qt/app.py:644 #, fuzzy msgid "&Help" msgstr "求助" #: qt/app.py:761 msgid "" "If you close this window Back In Time will not be able to shut down your system when the snapshot has finished.\n" "Do you really want to close?" msgstr "" #: qt/app.py:905 msgid "Working:" msgstr "運作中 :" #: qt/app.py:953 msgid "Done, no backup needed" msgstr "已完成,不需要進行備份" #: qt/app.py:962 #, fuzzy msgid "Working" msgstr "運作中" #: qt/app.py:971 qt/messagebox.py:76 msgid "Error" msgstr "" #: qt/app.py:994 qt/qtsystrayicon.py:202 msgid "Sent" msgstr "" #: qt/app.py:995 qt/qtsystrayicon.py:203 msgid "Speed" msgstr "" #: qt/app.py:996 qt/qtsystrayicon.py:204 msgid "ETA" msgstr "" #: qt/app.py:1050 msgid "Global" msgstr "全域設定" #: qt/app.py:1051 msgid "Root" msgstr "主目錄" #: qt/app.py:1052 msgid "Home" msgstr "家目錄" #: qt/app.py:1067 msgid "Backup folders" msgstr "備份資料夾" #: qt/app.py:1155 msgid "Snapshot Name" msgstr "快照名稱" #: qt/app.py:1202 #, fuzzy 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:1294 #, python-brace-format msgid "" "Create backup copies with trailing {suffix}\n" "before overwriting or removing local elements." msgstr "" #: qt/app.py:1300 qt/settingsdialog.py:774 #, python-brace-format msgid "" "Newer versions of files will be renamed with trailing {suffix} before restoring.\n" "If you don't need them anymore you can remove them with {cmd}" msgstr "" #: qt/app.py:1314 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:1347 msgid "Remove newer elements in original folder." msgstr "" #: qt/app.py:1348 msgid "" "Restore selected files or folders to the original destination and\n" "delete files or folders which are not in the snapshot.\n" "Be extremely careful because this will\n" "delete files and folders which were\n" "excluded during taking the snapshot." msgstr "" #: qt/app.py:1361 #, 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] "快照 {snapshot_id} 執行失敗!!!" #: qt/app.py:1370 #, fuzzy msgid "Do you really want to restore this element?" msgid_plural "Do you really want to restore these elements?" msgstr[0] "確認要刪除此快照" #: qt/app.py:1385 #, python-brace-format msgid "Are you sure you want to remove all newer files in {path}?" msgstr "" #: qt/app.py:1388 msgid "" "Are you sure you want to remove all newer files in your original folder?" msgstr "" #: qt/app.py:1393 msgid "" "WARNING: Deleting files in filesystem root could break your whole system!" msgstr "" #: qt/app.py:1623 #, fuzzy msgid "Snapshot" msgstr "快照" #: qt/app.py:1660 #, fuzzy, python-brace-format msgid "Restore {path}" msgstr "還原" #: qt/app.py:1662 #, fuzzy, python-brace-format msgid "Restore {path} to …" msgstr "還原" #: qt/app.py:1819 msgid "The language settings take effect only after restarting Back In Time." msgstr "" #: qt/app.py:1859 qt/app.py:1876 msgid "Authors" msgstr "" #: qt/app.py:1860 qt/app.py:1879 msgid "Translations" msgstr "" #: qt/app.py:1861 qt/app.py:1882 msgid "License" msgstr "" #: qt/languagedialog.py:29 msgid "Setup language" msgstr "" #: qt/languagedialog.py:87 msgid "System default" msgstr "" #: qt/languagedialog.py:94 msgid "Use operating systems language." msgstr "" #: qt/languagedialog.py:146 #, python-brace-format msgid "Translated: {percent}" msgstr "" #: qt/languagedialog.py:184 #, 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:216 msgid "translation platform" msgstr "" #: qt/languagedialog.py:232 msgid "Your translation" msgstr "" #: qt/logviewdialog.py:62 msgid "Last Log View" msgstr "" #: qt/logviewdialog.py:64 msgid "Snapshot Log View" msgstr "" #: qt/logviewdialog.py:72 qt/qtsystrayicon.py:76 qt/settingsdialog.py:95 #: qt/settingsdialog.py:330 qt/settingsdialog.py:2309 #, fuzzy msgid "Profile" msgstr "設定組合" #: qt/logviewdialog.py:80 qt/qttools.py:347 qt/snapshotsdialog.py:99 msgid "Snapshots" msgstr "快照" #: qt/logviewdialog.py:94 msgid "Filter" msgstr "" #: qt/logviewdialog.py:100 qt/settingsdialog.py:815 msgid "All" msgstr "" #: qt/logviewdialog.py:106 qt/logviewdialog.py:109 qt/settingsdialog.py:814 msgid "Changes" msgstr "" #: qt/logviewdialog.py:106 qt/logviewdialog.py:108 qt/settingsdialog.py:811 #: qt/settingsdialog.py:814 msgid "Errors" msgstr "" #: qt/logviewdialog.py:110 qt/messagebox.py:71 msgid "Information" msgstr "" #: qt/logviewdialog.py:111 msgid "rsync transfer failures (experimental)" msgstr "" #: qt/logviewdialog.py:121 msgid "[E] Error, [I] Information, [C] Change" msgstr "" #: qt/logviewdialog.py:124 qt/qtsystrayicon.py:98 msgid "decode paths" msgstr "" #: qt/logviewdialog.py:182 msgid "Copy" msgstr "" #: qt/logviewdialog.py:190 msgid "Decode" msgstr "" #: qt/logviewdialog.py:212 msgid "Do you want to exclude this?" msgstr "" #: qt/messagebox.py:82 qt/messagebox.py:90 msgid "Question" msgstr "" #: qt/qtsystrayicon.py:103 msgid "View Last Log" msgstr "" #: qt/qtsystrayicon.py:107 #, python-brace-format msgid "Start {appname}" msgstr "" #: qt/qtsystrayicon.py:169 #, fuzzy msgid "Working…" msgstr "運作中" #: qt/qttools.py:370 msgid "Today" msgstr "今天" #: qt/qttools.py:377 msgid "Yesterday" msgstr "昨天" #: qt/qttools.py:386 msgid "This week" msgstr "本週" #: qt/qttools.py:393 msgid "Last week" msgstr "上週" #: qt/qttools.py:539 msgid "This is NOT a snapshot but a live view of your local files" msgstr "" #: qt/qttools.py:544 #, python-brace-format msgid "Last check {time}" msgstr "" #: qt/restoredialog.py:60 msgid "Show full Log" msgstr "" #: qt/settingsdialog.py:87 #, fuzzy msgid "Manage profiles" msgstr "主要設定檔" #: qt/settingsdialog.py:104 msgid "Edit" msgstr "編輯" #: qt/settingsdialog.py:108 qt/settingsdialog.py:566 msgid "Add" msgstr "" #: qt/settingsdialog.py:112 qt/settingsdialog.py:515 qt/settingsdialog.py:584 msgid "Remove" msgstr "" #: qt/settingsdialog.py:127 #, fuzzy msgid "&General" msgstr "一般" #: qt/settingsdialog.py:137 qt/settingsdialog.py:2311 msgid "Mode" msgstr "" #: qt/settingsdialog.py:152 #, 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 "" #: qt/settingsdialog.py:163 qt/settingsdialog.py:1922 msgid "Where to save snapshots" msgstr "快照存放位置" #: qt/settingsdialog.py:179 msgid "Folder" msgstr "" #: qt/settingsdialog.py:187 msgid "SSH Settings" msgstr "" #: qt/settingsdialog.py:199 qt/settingsdialog.py:318 msgid "Host" msgstr "" #: qt/settingsdialog.py:204 msgid "Port" msgstr "" #: qt/settingsdialog.py:209 qt/settingsdialog.py:324 msgid "User" msgstr "" #: qt/settingsdialog.py:214 msgid "Path" msgstr "" #: qt/settingsdialog.py:220 msgid "Cipher" msgstr "" #: qt/settingsdialog.py:226 msgid "Private Key" msgstr "" #: qt/settingsdialog.py:236 msgid "Choose an existing private key file (normally named \"id_rsa\")" msgstr "" #: qt/settingsdialog.py:246 msgid "" "Create a new SSH key without password (not allowed if a private key file is " "already selected)" msgstr "" #: qt/settingsdialog.py:266 qt/settingsdialog.py:275 qt/settingsdialog.py:281 msgid "Password" msgstr "" #: qt/settingsdialog.py:287 msgid "Save Password to Keyring" msgstr "" #: qt/settingsdialog.py:291 msgid "Cache Password for Cron (Security issue: root can read password)" msgstr "" #: qt/settingsdialog.py:306 msgid "Advanced" msgstr "" #: qt/settingsdialog.py:336 msgid "Full snapshot path" msgstr "" #: qt/settingsdialog.py:343 msgid "Schedule" msgstr "排程" #: qt/settingsdialog.py:355 msgid "Disabled" msgstr "關閉" #: qt/settingsdialog.py:356 msgid "At every boot/reboot" msgstr "" #: qt/settingsdialog.py:358 qt/settingsdialog.py:360 qt/settingsdialog.py:362 #, fuzzy, python-brace-format msgid "Every {n} minute" msgid_plural "Every {n} minutes" msgstr[0] "每五分鐘" #: qt/settingsdialog.py:363 msgid "Every hour" msgstr "" #: qt/settingsdialog.py:365 qt/settingsdialog.py:367 qt/settingsdialog.py:369 #: qt/settingsdialog.py:371 #, fuzzy, python-brace-format msgid "Every {n} hour" msgid_plural "Every {n} hours" msgstr[0] "每月" #: qt/settingsdialog.py:372 msgid "Custom hours" msgstr "" #: qt/settingsdialog.py:373 #, fuzzy msgid "Every day" msgstr "每天" #: qt/settingsdialog.py:374 msgid "Repeatedly (anacron)" msgstr "" #: qt/settingsdialog.py:375 msgid "When drive gets connected (udev)" msgstr "" #: qt/settingsdialog.py:376 #, fuzzy msgid "Every week" msgstr "每週" #: qt/settingsdialog.py:377 #, fuzzy msgid "Every month" msgstr "每月" #: qt/settingsdialog.py:378 #, fuzzy msgid "Every year" msgstr "每天" #: qt/settingsdialog.py:383 #, fuzzy msgid "Day" msgstr "天" #: qt/settingsdialog.py:394 msgid "Weekday" msgstr "" #: qt/settingsdialog.py:409 #, fuzzy msgid "Hour" msgstr "時" #: qt/settingsdialog.py:424 #, fuzzy msgid "Hours" msgstr "時" #: qt/settingsdialog.py:435 msgid "" "Run Back In Time repeatedly. This is useful if the computer is not running " "regularly." msgstr "" #: qt/settingsdialog.py:442 #, fuzzy msgid "Every" msgstr "每天" #: qt/settingsdialog.py:456 msgid "Hour(s)" msgstr "" #: qt/settingsdialog.py:457 qt/settingsdialog.py:634 msgid "Day(s)" msgstr "天" #: qt/settingsdialog.py:458 qt/settingsdialog.py:635 msgid "Week(s)" msgstr "週" #: qt/settingsdialog.py:459 msgid "Month(s)" msgstr "" #: qt/settingsdialog.py:469 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:484 #, fuzzy msgid "&Include" msgstr "包含" #: qt/settingsdialog.py:491 msgid "Include files and folders" msgstr "" #: qt/settingsdialog.py:507 qt/settingsdialog.py:570 msgid "Add file" msgstr "新增檔案" #: qt/settingsdialog.py:511 qt/settingsdialog.py:574 msgid "Add folder" msgstr "新增資料夾" #: qt/settingsdialog.py:521 #, fuzzy msgid "&Exclude" msgstr "不包含" #: qt/settingsdialog.py:528 #, python-brace-format msgid "" "Wildcards ({example1}) will be ignored with mode 'SSH encrypted'.\n" "Only single or double asterisks are allowed ({example2})" msgstr "" #: qt/settingsdialog.py:543 msgid "Exclude patterns, files or folders" msgstr "" #: qt/settingsdialog.py:556 msgid "Highly recommended" msgstr "" #: qt/settingsdialog.py:579 msgid "Add default" msgstr "" #: qt/settingsdialog.py:592 msgid "Exclude files bigger than: " msgstr "" #: qt/settingsdialog.py:594 #, python-format msgid "" "Exclude files bigger than value in %(prefix)s.\n" "With 'Full rsync mode' disabled this will only affect new files\n" "because for rsync this is a transfer option, not an exclude option.\n" "So big files that have been backed up before will remain in snapshots\n" "even if they have changed." msgstr "" #: qt/settingsdialog.py:616 #, fuzzy msgid "&Auto-remove" msgstr "自動移除" #: qt/settingsdialog.py:622 #, fuzzy msgid "Older than" msgstr "時間超過" #: qt/settingsdialog.py:636 msgid "Year(s)" msgstr "年" #: qt/settingsdialog.py:644 #, fuzzy msgid "If free space is less than" msgstr "剩餘空間少於" #: qt/settingsdialog.py:664 #, fuzzy msgid "If free inodes is less than" msgstr "剩餘空間少於" #: qt/settingsdialog.py:678 #, fuzzy msgid "Smart remove:" msgstr "智慧移除" #: qt/settingsdialog.py:689 msgid "Run in background on remote host." msgstr "" #: qt/settingsdialog.py:690 msgid "EXPERIMENTAL" msgstr "" #: qt/settingsdialog.py:696 msgid "Keep all snapshots for the last" msgstr "" #: qt/settingsdialog.py:700 qt/settingsdialog.py:707 #, fuzzy msgid "day(s)." msgstr "天" #: qt/settingsdialog.py:703 msgid "Keep one snapshot per day for the last" msgstr "" #: qt/settingsdialog.py:710 msgid "Keep one snapshot per week for the last" msgstr "" #: qt/settingsdialog.py:714 #, fuzzy msgid "week(s)." msgstr "週" #: qt/settingsdialog.py:717 msgid "Keep one snapshot per month for the last" msgstr "" #: qt/settingsdialog.py:721 msgid "month(s)." msgstr "" #: qt/settingsdialog.py:724 msgid "Keep one snapshot per year for all years." msgstr "" #: qt/settingsdialog.py:733 #, fuzzy msgid "Don't remove named snapshots." msgstr "不要移除命名的快照" #: qt/settingsdialog.py:745 #, fuzzy msgid "&Options" msgstr "選項" #: qt/settingsdialog.py:750 msgid "Enable notifications" msgstr "開啟通知" #: qt/settingsdialog.py:754 msgid "Disable snapshots when on battery" msgstr "使用電池時停用快照功能" #: qt/settingsdialog.py:758 msgid "Power status not available from system" msgstr "無法獲得系統電源狀態" #: qt/settingsdialog.py:761 msgid "Run only one snapshot at a time" msgstr "" #: qt/settingsdialog.py:763 msgid "" "Other snapshots will be blocked until the current snapshot is done.\n" "This is a global option. So it will affect all profiles for this user.\n" "But you need to activate this for all other users, too." msgstr "" #: qt/settingsdialog.py:772 msgid "Backup replaced files on restore" msgstr "" #: qt/settingsdialog.py:785 msgid "Continue on errors (keep incomplete snapshots)" msgstr "" #: qt/settingsdialog.py:789 msgid "Use checksum to detect changes" msgstr "" #: qt/settingsdialog.py:793 msgid "Take a new snapshot whether there were changes or not." msgstr "" #: qt/settingsdialog.py:800 msgid "Log Level" msgstr "" #: qt/settingsdialog.py:805 msgid "None" msgstr "" #: qt/settingsdialog.py:825 #, fuzzy msgid "E&xpert Options" msgstr "進階選項" #: qt/settingsdialog.py:830 msgid "" "Caution: Change these options only if you really know what you are doing." msgstr "" #: qt/settingsdialog.py:835 qt/settingsdialog.py:851 qt/settingsdialog.py:873 #, python-brace-format msgid "Run 'rsync' with '{cmd}':" msgstr "" #: qt/settingsdialog.py:842 qt/settingsdialog.py:858 msgid "as cron job" msgstr "" #: qt/settingsdialog.py:847 qt/settingsdialog.py:868 qt/settingsdialog.py:889 msgid "on remote host" msgstr "" #: qt/settingsdialog.py:863 msgid "when taking a manual snapshot" msgstr "" #: qt/settingsdialog.py:876 msgid "(Please install 'nocache' to enable this option)" msgstr "" #: qt/settingsdialog.py:883 msgid "on local machine" msgstr "" #: qt/settingsdialog.py:894 msgid "Redirect stdout to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:903 msgid "Redirect stderr to /dev/null in cronjobs." msgstr "" #: qt/settingsdialog.py:915 msgid "Limit rsync bandwidth usage" msgstr "" #: qt/settingsdialog.py:918 msgid "KB/sec" msgstr "" #: qt/settingsdialog.py:956 msgid "Preserve ACL" msgstr "" #: qt/settingsdialog.py:971 msgid "Preserve extended attributes (xattr)" msgstr "" #: qt/settingsdialog.py:991 msgid "Copy unsafe links (works only with absolute links)" msgstr "" #: qt/settingsdialog.py:1024 #, python-brace-format msgid "Options must be quoted e.g. {example}." msgstr "" #: qt/settingsdialog.py:1027 msgid "Paste additional options to rsync" msgstr "" #: qt/settingsdialog.py:1042 #, python-format msgid "" "Prefix to run before every command on remote host.\n" "Variables need to be escaped with \\$FOO.\n" "This doesn't touch rsync. So to add a prefix\n" "for rsync use \"%(cbRsyncOptions)s\" with\n" "%(rsync_options_value)s\n" "\n" "%(default)s: %(def_value)s" msgstr "" #: qt/settingsdialog.py:1050 qt/settingsdialog.py:2069 msgid "default" msgstr "" #: qt/settingsdialog.py:1052 msgid "Add prefix to SSH commands" msgstr "" #: qt/settingsdialog.py:1065 msgid "Check if remote host is online" msgstr "" #: qt/settingsdialog.py:1067 msgid "" "Warning: if disabled and the remote host\n" "is not available, this could lead to some\n" "weird errors." msgstr "" #: qt/settingsdialog.py:1071 msgid "Check if remote host supports all necessary commands" msgstr "" #: qt/settingsdialog.py:1073 msgid "" "Warning: if disabled and the remote host\n" "does not support all necessary commands,\n" "this could lead to some weird errors." msgstr "" #: qt/settingsdialog.py:1089 msgid "Restore Config" msgstr "" #: qt/settingsdialog.py:1091 msgid "Edit user-callback" msgstr "" #: qt/settingsdialog.py:1109 msgid "New profile" msgstr "新增設定檔" #: qt/settingsdialog.py:1126 msgid "Rename profile" msgstr "重新命名設定檔" #: qt/settingsdialog.py:1142 #, fuzzy, python-brace-format msgid "Are you sure you want to delete the profile \"{name}\"?" msgstr "確認刪除設定檔\"{name}\"?" #: qt/settingsdialog.py:1416 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:1458 msgid "" "You did not choose a private key file for SSH.\n" "Would you like to generate a new password-less public/private key pair?" msgstr "" #: qt/settingsdialog.py:1469 #, python-brace-format msgid "Private key file \"{file}\" does not exist." msgstr "" #: qt/settingsdialog.py:1621 msgid "" "Would you like to copy your public SSH key to the\n" "remote host to enable password-less login?" msgstr "" #: qt/settingsdialog.py:1649 #, python-brace-format msgid "" "The authenticity of host {host} can't be established.\n" "\n" "{keytype} key fingerprint is:" msgstr "" #: qt/settingsdialog.py:1659 msgid "" "Please verify this fingerprint! Would you like to add it to your " "'known_hosts' file?" msgstr "" #: qt/settingsdialog.py:1822 msgid "Exclude pattern" msgstr "排除樣式" #: qt/settingsdialog.py:1835 msgid "Exclude file" msgstr "排除檔案" #: qt/settingsdialog.py:1839 msgid "Exclude folder" msgstr "排除資料夾" #: qt/settingsdialog.py:1863 msgid "Include file" msgstr "包含檔案" #: qt/settingsdialog.py:1872 qt/settingsdialog.py:1902 #, 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:1893 msgid "Include folder" msgstr "包含資料夾" #: qt/settingsdialog.py:1930 #, fuzzy msgid "Are you sure you want to change snapshots folder?" msgstr "確認修改快照資料夾?" #: qt/settingsdialog.py:1955 #, python-brace-format msgid "Failed to create new SSH key in {path}" msgstr "" #: qt/settingsdialog.py:2014 msgid "Full snapshot path: " msgstr "" #: qt/settingsdialog.py:2064 msgid "enabled" msgstr "" #: qt/settingsdialog.py:2067 msgid "disabled" msgstr "" #: qt/settingsdialog.py:2112 msgid "Restore Settings" msgstr "" #: qt/settingsdialog.py:2125 #, python-brace-format msgid "" "Please navigate to the snapshot from which you want to restore {appName}'s configuration. The path may look like:\n" "{samplePath}\n" "\n" "If your snapshots are on a remote drive or if they are encrypted you need to manually mount them first. If you use Mode SSH you also may need to set up public key login to the remote host.\n" "Take a look at 'man backintime'." msgstr "" #: qt/settingsdialog.py:2187 qt/settingsdialog.py:2252 msgid "No config found" msgstr "" #: qt/settingsdialog.py:2459 msgid "user-callback script has no shebang (#!/bin/sh) line." msgstr "" #: qt/settingsdialog.py:2466 msgid "Shebang in user-callback script is not executable." 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:66 msgid "Use %1 and %2 for path parameters" msgstr "使用%1和%2作為路徑參數" #: qt/snapshotsdialog.py:110 msgid "Differing snapshots only" msgstr "" #: qt/snapshotsdialog.py:118 msgid "List only equal snapshots to: " msgstr "" #: qt/snapshotsdialog.py:129 msgid "Deep check (more accurate, but slow)" msgstr "" #: qt/snapshotsdialog.py:150 msgid "Delete" msgstr "" #: qt/snapshotsdialog.py:154 msgid "Select All" msgstr "" #: qt/snapshotsdialog.py:167 msgid "Compare" msgstr "" #: qt/snapshotsdialog.py:178 msgid "Go To" msgstr "前往" #: qt/snapshotsdialog.py:179 #, fuzzy msgid "Options" msgstr "選項" #: qt/snapshotsdialog.py:330 #, fuzzy msgid "You can't compare a snapshot to itself." msgstr "無法進行快照自我比對" #: qt/snapshotsdialog.py:338 #, fuzzy msgid "Command not found" msgstr "指令不存在" #: qt/snapshotsdialog.py:369 #, fuzzy, python-brace-format msgid "Do you really want to delete {file} in snapshot {snapshot_id}?" msgstr "快照 {snapshot_id} 執行失敗!!!" #: qt/snapshotsdialog.py:375 #, python-brace-format msgid "Do you really want to delete {file} in {count} snapshots?" msgstr "" #: qt/snapshotsdialog.py:380 msgid "This cannot be revoked!" msgstr "" #: qt/snapshotsdialog.py:380 msgid "WARNING" msgstr "" #: qt/snapshotsdialog.py:396 #, python-brace-format msgid "Exclude {path} from future snapshots?" msgstr "" #, fuzzy #~ msgid "&Snapshot" #~ msgstr "快照" #~ msgid "..." #~ msgstr "..." #~ msgid "Diff" #~ msgstr "差異比較" #~ msgid "Diff Options" #~ msgstr "比對選項" #~ msgid "Error:" #~ msgstr "錯誤:" #~ msgid "Every 10 minutes" #~ msgstr "每十分鐘" #~ msgid "Every 5 minutes" #~ msgstr "每五分鐘" #, fuzzy, python-brace-format #~ msgid "Profile: {name}" #~ msgstr "設定檔:\"{name}\"" #~ msgid "Settings" #~ msgstr "設定" #, python-format #~ msgid "Snapshot: %s" #~ msgstr "快照:%s" #, fuzzy #~ msgid "View the current disk contents" #~ msgstr "檢視目前磁碟內容" #, fuzzy, python-brace-format #~ msgid "View the snapshot made at {timestamp}" #~ msgstr "檢視於{timestamp}完成之快照" #~ msgid "Working..." #~ msgstr "作業中..." #, fuzzy #~ msgid "You can't include backup folder!" #~ msgstr "不能包含備份資料夾" #, fuzzy #~ msgid "You can't include backup sub-folder!" #~ msgstr "不能包含備份子目錄" #, fuzzy #~ msgid "You can't remove the last profile!" #~ msgstr "你不能刪除最後一個設定檔!" backintime-1.4.3/common/progress.py000066400000000000000000000025401455673541400173520ustar00rootroot00000000000000# 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.4.3/common/qt5_probing.py000066400000000000000000000142611455673541400177420ustar00rootroot00000000000000import os import sys import resource import logger logger.openlog() # from tools import isRoot # This mini python script is used to determine if a Qt5 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_Qt5_working()" normally # but you can also execute it manually via # python3 qt5_probing.py # It works by trying to create a QApplication instance # Any error indicates that Qt5 is not available or not correctly configured. # WORK AROUND: # # The C++ code of Qt5 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 Qt5'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 (qt5_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 PyQt5 import QtCore from PyQt5.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 Qt5 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 PyQt5.QtWidgets import QSystemTrayIcon is_sys_tray_available = QSystemTrayIcon.isSystemTrayAvailable() if is_sys_tray_available: exit_code = 2 logger.debug(f"isSystemTrayAvailable for Qt5: {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 Qt5 GUI available # 1 = only Qt5 GUI available (no sys tray support) # 2 = Qt5 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 Qt5... # So the interpretation is the same as exit code 0. sys.exit(exit_code) backintime-1.4.3/common/snapshotlog.py000066400000000000000000000352171455673541400200560ustar00rootroot00000000000000# 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.4.3/common/snapshots.py000066400000000000000000003305561455673541400175430ustar00rootroot00000000000000# 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 import pathlib import stat import datetime import gettext import bz2 import pwd import grp import subprocess import shutil import time import re import fcntl from tempfile import TemporaryDirectory import config import configfile import logger import tools import encfstools import mount import progress import snapshotlog 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 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". Args: cfg (config.Config): current config """ SNAPSHOT_VERSION = 3 GLOBAL_FLOCK = '/tmp/backintime.lock' 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.flock = None self.restorePermissionFailed = False #TODO: make own class for takeSnapshotMessage def clearTakeSnapshotMessage(self): files = (self.config.takeSnapshotMessageFile(), \ self.config.takeSnapshotProgressFile()) for f in files: if os.path.exists(f): os.remove(f) #TODO: make own class for takeSnapshotMessage def takeSnapshotMessage(self): 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 if not os.path.exists(self.config.takeSnapshotMessageFile()): return None try: with open(self.config.takeSnapshotMessageFile(), 'rt') as f: items = f.read().split('\n') except Exception as e: logger.debug('Failed to get takeSnapshot message from %s: %s' %(self.config.takeSnapshotMessageFile(), str(e)), self) return None if len(items) < 2: return None mid = 0 try: mid = int(items[0]) except Exception as e: logger.debug('Failed to extract message ID from %s: %s' %(items[0], str(e)), self) del items[0] message = '\n'.join(items) return(mid, message) # 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! """ data = str(type_id) + '\n' + message try: with open(self.config.takeSnapshotMessageFile(), 'wt') as f: f.write(data) except Exception as e: logger.debug('Failed to set takeSnapshot message to %s: %s' %(self.config.takeSnapshotMessageFile(), str(e)), self) if 1 == type_id: 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 e: logger.debug('Failed to send message to plugins: %s' %str(e), 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) self.config.PLUGIN_MANAGER.error(1) # not configured 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('Profile "%s" is not scheduled to run now.' %self.config.profileName(), 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 running backup is in file %s. Maybe delete it' % instance.pidFile , self ) self.config.PLUGIN_MANAGER.error(2) # a backup is already running 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 %s. Maybe delete it'\ % restore_instance.pidFile, 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() self.flockExclusive() # global flock to block backups from other profiles or users (and run them serialized) 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("Take a new snapshot. Profile: %s %s" %(profile_id, profile_name), self) if not self.config.canBackup(profile_id): if self.config.PLUGIN_MANAGER.hasGuiPlugins and self.config.notify(): self.setTakeSnapshotMessage(1, _('Can\'t find snapshots folder.\nIf it is on a removable drive please plug it in.') + '\n' + gettext.ngettext('Waiting %s second.', 'Waiting %s seconds.', 30) % 30, 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("Waited %d seconds for target directory to be available", counter) if not self.config.canBackup(profile_id): logger.warning('Can\'t find snapshots folder!', self) self.config.PLUGIN_MANAGER.error(3) # Can't find snapshots directory (is it on a removable drive ?) else: ret_error = False sid = SID(now, self.config) if sid.exists(): logger.warning("Snapshot path \"%s\" already exists" %sid.path(), self) self.config.PLUGIN_MANAGER.error(4, sid) # This snapshot already exists 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: 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) self.config.PLUGIN_MANAGER.error(5, msg) # Fixes #1491 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) self.config.PLUGIN_MANAGER.error(6, sid.displayID) # Fixes #1491 ret_error = False # Why ignore errors now? # 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: self.config.PLUGIN_MANAGER.newSnapshot(sid, sid.path()) #new snapshot self.config.PLUGIN_MANAGER.processEnd() #take snapshot process end 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) if sleep: time.sleep(2) #max 1 backup / second #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 backupInfo(self, sid): """ Save infos about the snapshot into the 'info' file. Args: sid (SID): snapshot that should get an info file """ logger.info("Create info file", self) machine = self.config.host() user = self.config.user() profile_id = self.config.currentProfile() i = configfile.ConfigFile() i.setIntValue('snapshot_version', self.SNAPSHOT_VERSION) 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())) i.setStrValue('filesystem_mounts', json.dumps(tools.filesystemMountInfo())) 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 params = [False, False] # [error, changes] # "return" values set during async rsync execution (as user data "by ref") # 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("Found leftover '%s' which can be continued." %new_snapshot.displayID, 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('Remove leftover {} folder from last run' .format(new_snapshot.displayID)) 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("Can't remove folder: %s" % 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, callback = self.rsyncCallback, # 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! user_data = params, filters = (self.filterRsyncProgress,), parent = self) self.snapshotLog.append('[I] ' + proc.printable_cmd, 3) # TODO introduce centralized log msg builder to avoid spread severity level indicators like "[I]" here? # 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 has_errors = False # TODO Fix inconsistent usage: Collects return value, # but errors are also checked via params[0] # 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"), 23: _("Partial transfer due to error"), # ignored as fix for #1587 (until we introduce a new snapshot result category "(with warnings)") 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 + ": {msg}".format( msg=rsync_non_error_exit_codes[rsync_exit_code])) elif rsync_exit_code > 0: # indicates an rsync error params[0] = True # HACK to fix #489 (params[0] and has_errors should be merged) self.setTakeSnapshotMessage(1, rsync_exit_code_msg + ": " + _("See 'man rsync' for more details")) elif rsync_exit_code < 0: # indicates an rsync error caused by a signal params[0] = True # HACK to fix #489 (params[0] and has_errors should be merged) 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 and not list(self.config.anacrontabFiles()): tools.writeTimeStamp(self.config.anacronSpoolFile()) return [False, has_errors] # Part of fix for #1491: Returns "has_errors" instead of False now # to signal rsync errors (which may have prevented processing any changes) 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("Can't rename %s to %s" % (new_snapshot.path(), 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.backupInfo(sid) if not has_errors and not list(self.config.anacrontabFiles()): 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 healty 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 remove') + ' %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 remove')) 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 # TODO Rename to make the purpose obvious, eg: Serialize_[backup|execution]_via_exclusive_global_flock() def flockExclusive(self): """ Block :py:func:`backup` from other profiles or users and run them serialized """ if self.config.globalFlock(): logger.debug('Set flock %s' %self.GLOBAL_FLOCK, self) self.flock = open(self.GLOBAL_FLOCK, 'w') fcntl.flock(self.flock, fcntl.LOCK_EX) # blocks (waits) until an existing flock is released # make it rw by all if that's not already done. perms = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | \ stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH s = os.fstat(self.flock.fileno()) if not s.st_mode & perms == perms: logger.debug('Set flock permissions %s' %self.GLOBAL_FLOCK, self) os.fchmod(self.flock.fileno(), perms) def flockRelease(self): """ Release lock so other snapshots can continue """ if self.flock: logger.debug('Release flock %s' %self.GLOBAL_FLOCK, self) fcntl.fcntl(self.flock, fcntl.LOCK_UN) self.flock.close() self.flock = None 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 canOpenPath(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): """ Iterate over snapshots in current snapshot path. Use this in a 'for' loop for faster processing than list object 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 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.4.3/common/sshMaxArg.py000066400000000000000000000142111455673541400174010ustar00rootroot00000000000000#!/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.4.3/common/sshtools.py000066400000000000000000001221141455673541400173640ustar00rootroot00000000000000# 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 time import sleep import logger import tools import password_ipc from mount import MountControl from exceptions import MountException, NoPubKeyLogin, KnownHost import bcolors 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.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( '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) 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) ssh = self.config.sshCommand(cmd=['exit'], custom_args=[ '-o', 'PreferredAuthentications=publickey', '-p', str(self.port), self.user_host ], 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 logger.debug('Check ping host', self) versionString = 'SSH-2.0-backintime_{}\r\n'.format( self.config.VERSION).encode() count = 0 while count < 5: try: with socket.create_connection( (self.host, self.port), 2.0) as s: result = s.connect_ex(s.getpeername()) s.sendall(versionString) except: result = -1 if result == 0: logger.debug('Host %s is available' % self.host, self) return logger.debug('Could not ping host %s. Try again' % self.host, self) count += 1 sleep(0.2) if result != 0: logger.debug('Failed pinging host %s' % self.host, self) raise MountException( f'Ping {self.host} failed. Host is down or wrong address.') 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 sshCopyId(pubkey, user, host, port='22', 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 """ if not os.path.exists(pubkey): logger.warning( 'SSH public key "{}" does not exist. Skip copy to remote host' .format(pubkey)) return False 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 password for "{user}"').format(user=user) ) cmd = ['ssh-copy-id', '-i', pubkey, '-p', port] if cipher and cipher != 'default': cmd.extend(['-o', 'Ciphers={}'.format(cipher)]) cmd.append('{}@{}'.format(user, host)) logger.debug('Call command "{}"'.format(' '.join(cmd))) 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.4.3/common/test/000077500000000000000000000000001455673541400161125ustar00rootroot00000000000000backintime-1.4.3/common/test/__init__.py000066400000000000000000000000001455673541400202110ustar00rootroot00000000000000backintime-1.4.3/common/test/config000066400000000000000000000012671455673541400173100ustar00rootroot00000000000000config.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.4.3/common/test/dummy_test_process.sh000077500000000000000000000015351455673541400224050ustar00rootroot00000000000000#!/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.4.3/common/test/generic.py000066400000000000000000000305241455673541400201040ustar00rootroot00000000000000# 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.4.3/common/test/mock_askpass000077500000000000000000000000301455673541400205070ustar00rootroot00000000000000#!/bin/sh echo "travis" backintime-1.4.3/common/test/test_applicationinstance.py000066400000000000000000000252151455673541400235600ustar00rootroot00000000000000# 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.4.3/common/test/test_argparser.py000066400000000000000000000244451455673541400215220ustar00rootroot00000000000000# 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.4.3/common/test/test_backintime.py000066400000000000000000000227071455673541400216410ustar00rootroot00000000000000# 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 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): Multiple tests do compare return codes and output on stdout. The intention might be an integration tests. 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. """ # 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" ] # 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: Create info file 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"], config.Config.VERSION) backintime-1.4.3/common/test/test_backup.py000066400000000000000000000225011455673541400207700ustar00rootroot00000000000000# 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.4.3/common/test/test_config.py000066400000000000000000000201031455673541400207640ustar00rootroot00000000000000# 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 stat import sys from test import generic from tempfile import TemporaryDirectory from unittest.mock import patch 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)) @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): def test_full_command(self): cmd = self.cfg.sshCommand(cmd = ['echo', 'foo']) self.assertListEqual(cmd, ['ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '{}@localhost'.format(self.cfg.user()), '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', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '-o', 'PreferredAuthentications=publickey', '{}@localhost'.format(self.cfg.user()), 'echo', 'foo']) def test_cipher(self): self.cfg.setSshCipher('aes256-cbc') cmd = self.cfg.sshCommand(cmd = ['echo', 'foo']) self.assertListEqual(cmd, ['ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '-o', 'Ciphers=aes256-cbc', '{}@localhost'.format(self.cfg.user()), 'echo', 'foo']) # disable cipher cmd = self.cfg.sshCommand(cmd = ['echo', 'foo'], cipher = False) self.assertListEqual(cmd, ['ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '{}@localhost'.format(self.cfg.user()), 'echo', 'foo']) def test_without_command(self): cmd = self.cfg.sshCommand() self.assertListEqual(cmd, ['ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '{}@localhost'.format(self.cfg.user())]) def test_nice(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', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '{}@localhost'.format(self.cfg.user()), 'ionice', '-c2', '-n7', 'nice', '-n19', 'echo', 'foo']) # without cmd no io-/nice cmd = self.cfg.sshCommand() self.assertListEqual(cmd, ['ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '{}@localhost'.format(self.cfg.user())]) def test_quote(self): cmd = self.cfg.sshCommand(cmd = ['echo', 'foo'], quote = True) self.assertListEqual(cmd, ['ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '{}@localhost'.format(self.cfg.user()), "'", 'echo', 'foo', "'"]) # without cmd no quotes cmd = self.cfg.sshCommand(quote = True) self.assertListEqual(cmd, ['ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '{}@localhost'.format(self.cfg.user())]) 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', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '{}@localhost'.format(self.cfg.user()), 'echo', 'bar', 'echo', 'foo']) # disable prefix cmd = self.cfg.sshCommand(cmd = ['echo', 'foo'], prefix = False) self.assertListEqual(cmd, ['ssh', '-o', 'ServerAliveInterval=240', '-o', 'LogLevel=Error', '-o', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE), '-p', '22', '{}@localhost'.format(self.cfg.user()), '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', 'IdentityFile={}'.format(generic.PRIV_KEY_FILE)]) backintime-1.4.3/common/test/test_configfile.py000066400000000000000000000664521455673541400216450ustar00rootroot00000000000000# 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.4.3/common/test/test_diagnostics.py000066400000000000000000000071641455673541400220420ustar00rootroot00000000000000import sys import pathlib import unittest import pyfakefs.fake_filesystem_unittest as pyfakefs_ut # 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): def test_minimal(self): """Minimal set of elements.""" result = diagnostics.collect_diagnostics() # 1st level keys self.assertEqual( sorted(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 ) class Diagnostics_FakeFS(pyfakefs_ut.TestCase): """Tests using a fake filesystem. """ def setUp(self): self.setUpPyfakefs(allow_root_user=False) def test_git_repo_info(self): # not a git repo self.assertEqual(diagnostics.get_git_repository_info(), None) # 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( diagnostics.get_git_repository_info(), { 'hash': '01234', 'branch': 'fix/foobar' } ) backintime-1.4.3/common/test/test_encfstools.py000066400000000000000000000031071455673541400217030ustar00rootroot00000000000000# 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.4.3/common/test/test_lint.py000066400000000000000000000041021455673541400204660ustar00rootroot00000000000000import unittest import pathlib import subprocess from importlib import metadata from typing import Iterable # PACKAGE_NAME = 'buhtzology' 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. This will prevent the dirty folder-jumping-hack currently done in this code. """ p = pathlib.Path.cwd() # Make sure we are inside the test folder if p.name in ['qt', 'common']: # happens e.g. on TravisCI p = p / 'test' if not p.name.startswith('test'): raise Exception('Something went wrong. The test should run inside' f' the test folder but current folder is {p}.') # Workaround p = p.parent # Find recursive all py-files. return p.rglob('**/*.py') 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', # prevent false-positive no-module-member errors '--extension-pkg-whitelist=PyQt5', # Because of globally installed GNU gettext functions '--additional-builtins=_,ngettext', # Deactivate all checks by default '--disable=all' ] # Explicit activate checks err_codes = [ 'E1101', # no-member 'W1401', # anomalous-backslash-in-string (invalid escape sequence) ] cmd.append('--enable=' + ','.join(err_codes)) for fp in self._collect_py_files(): subprocess.run(cmd + [fp], check=True) backintime-1.4.3/common/test/test_restore.py000066400000000000000000000171621455673541400212150ustar00rootroot00000000000000# 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.4.3/common/test/test_sid.py000066400000000000000000000532231455673541400203070ustar00rootroot00000000000000# 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_canOpenPath(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.canOpenPath('/foo')) self.assertFalse(sid.canOpenPath('/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.canOpenPath('/bar')) #test valid relative symlink inside snapshot os.symlink('./foo', os.path.join(backupPath, 'baz')) self.assertIsLink(backupPath, 'baz') self.assertTrue(sid.canOpenPath('/baz')) #test invalid symlink os.symlink(os.path.join(backupPath, 'asdf'), os.path.join(backupPath, 'qwer')) self.assertIsLink(backupPath, 'qwer') self.assertFalse(sid.canOpenPath('/qwer')) #test valid symlink outside snapshot os.symlink('/tmp', os.path.join(backupPath, 'bla')) self.assertIsLink(backupPath, 'bla') self.assertFalse(sid.canOpenPath('/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.4.3/common/test/test_snapshotlog.py000066400000000000000000000166321455673541400220740ustar00rootroot00000000000000# 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.4.3/common/test/test_snapshots.py000066400000000000000000001344261455673541400215570ustar00rootroot00000000000000# 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(getpass.getuser() != 'germar', 'Password login does not work on Travis-ci.') @unittest.skipIf(not generic.LOCAL_SSH, 'Skip as this test requires a local ssh server, public and private keys installed') def test_sshCopyId(self): with TemporaryDirectory() as tmp: secKey = os.path.join(tmp, 'key') pubKey = secKey + '.pub' authKeys = os.path.expanduser('~/.ssh/authorized_keys') authKeysSic = os.path.join(tmp, 'sic') if os.path.exists(authKeys): shutil.copyfile(authKeys, authKeysSic) os.remove(authKeys) # create new key sshtools.sshKeyGen(secKey) self.assertIsFile(pubKey) with open(pubKey, 'rt') as f: pubKeyValue = f.read() try: # test copy pubKey self.assertTrue(sshtools.sshCopyId(pubKey, self.cfg.user(), 'localhost', askPass = 'test/mock_askpass')) self.assertExists(authKeys) with open(authKeys, 'rt') as f: self.assertIn(pubKeyValue, f.readlines()) finally: # restore original ~/.ssh/authorized_keys file without test pubKey if os.path.exists(authKeysSic): shutil.copyfile(authKeysSic, authKeys) @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 as this test requires a local ssh server, public and private keys installed') 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() backintime-1.4.3/common/test/test_takeSnapshot.py000066400000000000000000000257031455673541400221760ustar00rootroot00000000000000# 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.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'test'))) self.assertTrue(sid1.canOpenPath(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.canOpenPath(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.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertTrue(sid4.canOpenPath(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.canOpenPath(os.path.join(include, 'foo', 'bar', 'baz'))) self.assertTrue(sid1.canOpenPath(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.canOpenPath(os.path.join(self.include.name, 'foo', 'bar'))) self.assertFalse(sid1.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertTrue(sid1.canOpenPath(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.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertTrue(sid1.canOpenPath(os.path.join(self.include.name, 'test'))) self.assertFalse(sid1.canOpenPath(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.canOpenPath(os.path.join(self.include.name, 'foo', 'bar', 'baz'))) self.assertFalse(sid1.canOpenPath(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.4.3/common/test/test_tools.py000066400000000000000000001203411455673541400206640ustar00rootroot00000000000000# 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 import uuid from copy import deepcopy from tempfile import NamedTemporaryFile, TemporaryDirectory from datetime import datetime from test import generic from time import sleep from pyfakefs.fake_filesystem_unittest import patchfs sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import tools import config 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.killProcess() def createProcess(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 killProcess(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_gitRevisionAndHash(self): ref, hashid = tools.gitRevisionAndHash() if isinstance(ref, str): self.assertGreater(len(ref), 0) else: self.assertIsNone(ref) if isinstance(hashid, str): self.assertEqual(len(hashid), 7) else: self.assertIsNone(hashid) 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.createProcess() 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.createProcess() self.assertEqual(tools.processStat(pid), '') def test_processPaused(self): pid = self.createProcess() 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.createProcess() self.assertEqual(tools.processName(pid), generic.DUMMY[:15]) def test_processCmdline(self): pid = self.createProcess() self.assertRegex(tools.processCmdline(pid), r'.*/sh.*/common/test/dummy_test_process\.sh') self.killProcess() pid = self.createProcess('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.createProcess() self.assertEqual(tools.processCmdline(pid), '') def test_pidsWithName(self): self.assertEqual(len(tools.pidsWithName('nonExistingProcess')), 0) pid = self.createProcess() pids = tools.pidsWithName(generic.DUMMY) self.assertGreaterEqual(len(pids), 1) self.assertIn(pid, pids) def test_processExists(self): self.assertFalse(tools.processExists('nonExistingProcess')) self.createProcess() 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.createProcess() self.assertTrue(tools.processAlive(pid)) self.killProcess() 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')) # envLoad and envSave tests are in TestToolsEnviron below 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_device(self): self.assertEqual(tools.device('/proc'), 'proc') self.assertRegex(tools.device('/sys'), r'sys.*') self.assertRegex( tools.device('/nonExistingFolder/foo/bar'), r'(:?/dev/.*|tmpfs|overlay|instances/containers/travis.*)') def test_filesystem(self): self.assertEqual(tools.filesystem('/proc'), 'proc') self.assertRegex(tools.filesystem('/sys'), r'sys.*') self.assertRegex( tools.filesystem('/nonExistingFolder/foo/bar').lower(), r'(:?ext[2-4]|xfs|zfs|jfs|raiserfs|btrfs|tmpfs|overlay|shiftfs)') # tools.uuidFromDev() get called from tools.uuidFromPath because the # latter is a synonym/surrogate for too.suuidFromDev() # So we skip an extra unittest as it's hard to find a dev on all systems @unittest.skipIf(not DISK_BY_UUID_AVAILABLE and not UDEVADM_HAS_UUID, 'No UUIDs available on this system.') def test_uuidFromPath(self): """UUID related to a path. by buhtz: I was wondering why this test passed because the path used here doesn't exists! The function ``tools.uuidFromPath()`` does use ``tools.device()`` function to determine the device (e.g. ``/sda1``) related to the path. Not matter that the path itself doesn't exists only the "mountpoint" is relevant. The mountpoint for the non-existing path is ``/`` which of curse does exists. In short: That test needs a refactoring. """ uuid = tools.uuidFromPath('/nonExistingFolder/foo/bar') self.assertIsInstance(uuid, str) self.assertRegex(uuid.lower(), r'^[a-f0-9\-]+$') self.assertEqual(len(uuid.replace('-', '')), 32) @patchfs def test_uuid_via_filesystem(self, fake_fs): """Extract UUID from /dev filesystem. That test using a faked filesystem via pyfakefs. 16 devices and corresponding uuids are generated.""" # dev-disk folder path_dev = pathlib.Path('/dev') fake_fs.create_dir(path_dev) # create disk-files from "sda1" to "sda4" to "sdd4" dev_list = [ path_dev / f'sd{letter}{number}' for letter in list('abcd') for number in range(1, 5)] # dev-disk-by-uuid path_by_uuid = pathlib.Path('/dev') / 'disk' / 'by-uuid' fake_fs.create_dir(path_by_uuid) # uuids uuid_list = [str(uuid.uuid4()) for _ in range(16)] # connect device files with uuid symlinks for idx in range(16): # 16 devices fake_fs.create_symlink( # e.g. /dev/disk/by-uuid/c7aca0 file_path=path_by_uuid / uuid_list[idx], # e.g. /dev/sda1 link_target=path_dev / dev_list[idx] ) # randomly select one of the dev-uuid pairs check_idx = random.choice(range(16)) # TEST self.assertEqual( tools._uuidFromDev_via_filesystem(dev=dev_list[check_idx]), uuid_list[check_idx] ) @patch('subprocess.check_output') def test_uuid_via_blkid(self, mock_check_output): """Extract UUID from blkid output.""" one_uuid = 'c7aca0a7-89ed-43f0-a4f9-c744dfe673e0' one_dev = '/dev/sda1' output = f'{one_dev}: UUID="{one_uuid}" BLOCK_SIZE="4096" ' \ 'TYPE="ext4" PARTUUID="89ffeb8f-01"' mock_check_output.return_value = output self.assertEqual( tools._uuidFromDev_via_blkid_command(dev=one_dev), one_uuid ) @patch('subprocess.check_output') def test_uuid_via_udevadm(self, mock_check_output): """Extract UUID from udevadm output.""" one_uuid = 'c7aca0a7-89ed-43f0-a4f9-c744dfe673e0' one_dev = '/dev/sda1' # output to mock with injected dev and uuid output = 'P: /devices/pci0000:00/0000:00:1f.2/ata1/host0/target' \ '0:0:0/0:0:0:0/block/sda/sda1\n' \ f'N: {one_dev}\n' \ 'L: 0\n' \ 'S: disk/by-uuid/c7aca0a7-89ed-43f0-a4f9-c744dfe673e0\n' \ 'S: disk/by-id/ata-WDC_WD20EARS-00S8B1_WD-' \ 'WCAVY4333133-part1\n' \ 'S: disk/by-id/wwn-0x50014ee2049ff22c-part1\n' \ 'S: disk/by-path/pci-0000:00:1f.2-ata-1.0-part1\n' \ 'S: disk/by-partuuid/89ffeb8f-01\n' \ 'S: disk/by-path/pci-0000:00:1f.2-ata-1-part1\n' \ 'E: DEVPATH=/devices/pci0000:00/0000:00:1f.2/ata1/host0/' \ 'target0:0:0/0:0:0:0/block/sda/sda1\n' \ 'E: DEVNAME=/dev/sda1\n' \ 'E: DEVTYPE=partition\n' \ 'E: PARTN=1\n' \ 'E: MAJOR=8\n' \ 'E: MINOR=1\n' \ 'E: SUBSYSTEM=block\n' \ 'E: USEC_INITIALIZED=1997408\n' \ 'E: ID_ATA=1\n' \ 'E: ID_TYPE=disk\n' \ 'E: ID_BUS=ata\n' \ 'E: ID_MODEL=WDC_WD20EARS-00S8B1\n' \ 'E: ID_MODEL_ENC=WDC\x20WD20EARS-00S8B1' \ '\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20' \ '\x20\x20\x20\x20\x20\x20\x20\x20\n' \ 'E: ID_REVISION=80.00A80\n' \ 'E: ID_SERIAL=WDC_WD20EARS-00S8B1_WD-WCAVY4333133\n' \ 'E: ID_SERIAL_SHORT=WD-WCAVY4333133\n' \ 'E: ID_ATA_WRITE_CACHE=1\n' \ 'E: ID_ATA_WRITE_CACHE_ENABLED=1\n' \ 'E: ID_ATA_FEATURE_SET_HPA=1\n' \ 'E: ID_ATA_FEATURE_SET_HPA_ENABLED=1\n' \ 'E: ID_ATA_FEATURE_SET_PM=1\n' \ 'E: ID_ATA_FEATURE_SET_PM_ENABLED=1\n' \ 'E: ID_ATA_FEATURE_SET_SECURITY=1\n' \ 'E: ID_ATA_FEATURE_SET_SECURITY_ENABLED=0\n' \ 'E: ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=416\n' \ 'E: ID_ATA_FEATURE_SET_SECURITY_ENHANCED_' \ 'ERASE_UNIT_MIN=416\n' \ 'E: ID_ATA_FEATURE_SET_SECURITY_FROZEN=1\n' \ 'E: ID_ATA_FEATURE_SET_SMART=1\n' \ 'E: ID_ATA_FEATURE_SET_SMART_ENABLED=1\n' \ 'E: ID_ATA_FEATURE_SET_AAM=1\n' \ 'E: ID_ATA_FEATURE_SET_AAM_ENABLED=1\n' \ 'E: ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=128\n' \ 'E: ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=254\n' \ 'E: ID_ATA_FEATURE_SET_PUIS=1\n' \ 'E: ID_ATA_FEATURE_SET_PUIS_ENABLED=0\n' \ 'E: ID_ATA_DOWNLOAD_MICROCODE=1\n' \ 'E: ID_ATA_SATA=1\n' \ 'E: ID_ATA_SATA_SIGNAL_RATE_GEN2=1\n' \ 'E: ID_ATA_SATA_SIGNAL_RATE_GEN1=1\n' \ 'E: ID_WWN=0x50014ee2049ff22c\n' \ 'E: ID_WWN_WITH_EXTENSION=0x50014ee2049ff22c\n' \ 'E: ID_PATH=pci-0000:00:1f.2-ata-1.0\n' \ 'E: ID_PATH_TAG=pci-0000_00_1f_2-ata-1_0\n' \ 'E: ID_PATH_ATA_COMPAT=pci-0000:00:1f.2-ata-1\n' \ 'E: ID_PART_TABLE_UUID=89ffeb8f\n' \ 'E: ID_PART_TABLE_TYPE=dos\n' \ 'E: ID_FS_UUID=c7aca0a7-89ed-43f0-a4f9-c744dfe673e0\n' \ 'E: ID_FS_UUID_ENC=c7aca0a7-89ed-43f0-a4f9-c744dfe673e0\n' \ 'E: ID_FS_VERSION=1.0\n' \ 'E: ID_FS_TYPE=ext4\n' \ 'E: ID_FS_USAGE=filesystem\n' \ 'E: ID_PART_ENTRY_SCHEME=dos\n' \ 'E: ID_PART_ENTRY_UUID=89ffeb8f-01\n' \ 'E: ID_PART_ENTRY_TYPE=0x83\n' \ 'E: ID_PART_ENTRY_NUMBER=1\n' \ 'E: ID_PART_ENTRY_OFFSET=2048\n' \ 'E: ID_PART_ENTRY_SIZE=3907026944\n' \ 'E: ID_PART_ENTRY_DISK=8:0\n' \ 'E: DEVLINKS=/dev/disk/by-uuid/c7aca0a7-89ed-43f0-a4f9-' \ 'c744dfe673e0 /dev/disk/by-id/ata-WDC_WD20EARS-00S8B1_WD'\ '-WCAVY4333133-part1 /dev/disk/by-id/wwn-' \ '0x50014ee2049ff22c-part1 /dev/disk/by-path/pci-0000:00:' \ '1f.2-ata-1.0-part1 /dev/disk/by-partuuid/89ffeb8f-01 ' \ '/dev/disk/by-path/pci-0000:00:1f.2-ata-1-part1\n' \ 'E: TAGS=:systemd:\n' \ 'E: CURRENT_TAGS=:systemd:\n' mock_check_output.return_value = output self.assertEqual( tools._uuidFromDev_via_udevadm_command(dev=one_dev), one_uuid ) @unittest.skipIf(not DISK_BY_UUID_AVAILABLE and not UDEVADM_HAS_UUID, 'No UUIDs available on this system.') def test_filesystemMountInfo(self): """ Basic sanity checks on returned structure """ mounts = tools.filesystemMountInfo() self.assertIsInstance(mounts, dict) self.assertGreater(len(mounts.items()), 0) self.assertIn('/', mounts) self.assertIn('original_uuid', mounts.get('/')) def test_syncfs(self): self.assertTrue(tools.syncfs()) 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) @unittest.skipIf(not tools.checkCommand('crontab'), "'crontab' not found.") def test_readWriteCrontab(self): now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') oldCrontab = tools.readCrontab() self.assertIsInstance(oldCrontab, list) testLine = '#BackInTime Unittest from {}. Test probably failed. ' \ 'You can remove this line.'.format(now) self.assertTrue(tools.writeCrontab(oldCrontab + [testLine, ])) newCrontab = tools.readCrontab() self.assertIn(testLine, newCrontab) self.assertEqual(len(newCrontab), len(oldCrontab) + 1) self.assertTrue(tools.writeCrontab(oldCrontab)) if oldCrontab: self.assertListEqual(oldCrontab, tools.readCrontab()) 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') def test_isIPv6Address(self): self.assertTrue(tools.isIPv6Address('fd00:0::5')) self.assertTrue(tools.isIPv6Address('2001:db8:0:8d3:0:8a2e:70:7344')) self.assertFalse(tools.isIPv6Address('foo.bar')) self.assertFalse(tools.isIPv6Address('192.168.1.1')) self.assertFalse(tools.isIPv6Address('fd00')) def test_excapeIPv6Address(self): self.assertEqual(tools.escapeIPv6Address('fd00:0::5'), '[fd00:0::5]') self.assertEqual( tools.escapeIPv6Address('2001:db8:0:8d3:0:8a2e:70:7344'), '[2001:db8:0:8d3:0:8a2e:70:7344]') self.assertEqual(tools.escapeIPv6Address('foo.bar'), 'foo.bar') self.assertEqual(tools.escapeIPv6Address('192.168.1.1'), '192.168.1.1') self.assertEqual(tools.escapeIPv6Address('fd00'), 'fd00') def test_camelCase(self): self.assertEqual(tools.camelCase('foo'), 'Foo') self.assertEqual(tools.camelCase('Foo'), 'Foo') self.assertEqual(tools.camelCase('foo_bar'), 'FooBar') self.assertEqual(tools.camelCase('foo_Bar'), 'FooBar') 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) backintime-1.4.3/common/tools.py000066400000000000000000002655071455673541400166640ustar00rootroot00000000000000# 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 gettext try: from collections.abc import MutableSet except ImportError: from collections 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. If running from source return default ``/usr/share``. Returns: str: share path like:: /usr/share /usr/local/share /opt/usr/share """ 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' # |---------------------------------------------------| # | 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']) return _determine_current_used_language_code(translation, language_code) 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 backintimePath(*path): """ Get path inside 'backintime' install folder. Args: *path (str): paths that should be joined to 'backintime' Returns: str: 'backintime' child path like:: /usr/share/backintime/common /usr/share/backintime/qt """ return os.path.abspath(os.path.join(__file__, os.pardir, os.pardir, *path)) 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 not path in sys.path: sys.path.insert(0, path) def runningFromSource(): """ Check if BackInTime is running from source (without installing). 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 gitRevisionAndHash(): """ Get the current Git Branch and the last HashID (shot form) if running from source. Returns: tuple: two items of either :py:class:`str` instance if running from source or ``None`` """ ref, hashid = None, None gitPath = os.path.abspath(os.path.join(__file__, os.pardir, os.pardir, '.git')) headPath = os.path.join(gitPath, 'HEAD') refPath = '' if not os.path.isdir(gitPath): return (ref, hashid) try: with open(headPath, 'rt') as f: refPath = f.read().strip('\n') if refPath.startswith('ref: '): refPath = refPath[5:] if refPath: refPath = os.path.join(gitPath, refPath) ref = os.path.basename(refPath) except Exception as e: pass if os.path.isfile(refPath): try: with open(refPath, 'rt') as f: hashid = f.read().strip('\n')[:7] except: pass return (ref, hashid) 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' environ. Args: cmd (str): command Returns: bool: ``True`` if command ``cmd`` is in 'PATH' environ """ cmd = cmd.strip() if not cmd: return False if os.path.isfile(cmd): return True return not which(cmd) is None def which(cmd): """ Get the fullpath of executable command ``cmd``. Works like command-line 'which' command. Args: cmd (str): 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_Qt5_working`` instead if you want to be sure that Qt5 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_Qt5_working(systray_required=False): """ Check if the Qt5 GUI library is working (installed and configured) This function is contained in BiT CLI (not BiT Qt) to allow Qt5 diagnostics output even if the BiT Qt GUI is not installed. This function does NOT add a hard Qt5 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 Qt5 as "working" Returns: bool: ``True`` Qt5 can create a GUI ``False`` Qt5 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"), "qt5_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 (qt5_probing.py may hang as root): Kill after timeout logger.debug(f"Qt5 probing result: exit code {proc.returncode}") if proc.returncode != 2 or logger.DEBUG: # if some Qt5 parts are missing: Show details logger.debug(f"Qt5 probing stdout:\n{std_output}") logger.debug(f"Qt5 probing errout:\n{error_output}") return proc.returncode == 2 or (proc.returncode == 1 and systray_required is False) except FileNotFoundError: logger.error(f"Qt5 probing script not found: {cmd[0]}") raise # Fix for #1592 (qt5_probing.py may hang as root): Kill after timeout except subprocess.TimeoutExpired: proc.kill() outs, errs = proc.communicate() logger.info("Qt5 probing sub process killed after timeout without response") logger.debug(f"Qt5 probing stdout:\n{outs}") logger.debug(f"Qt5 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.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 """ 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 filesystemMountInfo(): """ Get a dict of mount point string -> dict of filesystem info for entire system. Returns: dict: {MOUNTPOINT: {'original_uuid': UUID}} """ # There may be multiple mount points inside of the root (/) mount, so # iterate over mtab to find all non-special mounts. with open('/etc/mtab', 'r') as mounts: return {items[1]: {'original_uuid': uuidFromDev(items[0])} for items in [mount_line.strip('\n').split(' ')[:2] for mount_line in mounts] if uuidFromDev(items[0]) != None} def syncfs(): """ Sync any data buffered in memory to disk. Returns: bool: ``True`` if successful """ if checkCommand('sync'): return(Execute(['sync']).run() == 0) 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 BIT_TIME_FORMAT = '%Y%m%d %H%M' ANACRON_TIME_FORMAT = '%Y%m%d' 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: date from timestamp file """ if not os.path.exists(fname): logger.debug("no timestamp in '%(file)s'" % {'file': fname}) return with open(fname, 'r') as f: s = f.read().strip('\n') for i in (ANACRON_TIME_FORMAT, BIT_TIME_FORMAT): try: stamp = datetime.strptime(s, i) logger.debug("read timestamp '%(time)s' from file '%(file)s'" % {'time': stamp, 'file': fname}) return stamp except ValueError: pass def writeTimeStamp(fname): """ Write current date and time into file ``fname``. Args: fname (str): full path to timestamp file """ now = datetime.now().strftime(BIT_TIME_FORMAT) logger.debug("write timestamp '%(time)s' into file '%(file)s'" % {'time': now, '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 readCrontab(): """ Read users crontab. Returns: list: crontab lines """ cmd = ['crontab', '-l'] if not checkCommand(cmd[0]): logger.debug('crontab not found.') return [] else: proc = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE, universal_newlines = True) out, err = proc.communicate() if proc.returncode or err: logger.error('Failed to get crontab lines: %s, %s' %(proc.returncode, err)) return [] else: crontab = [x.strip() for x in out.strip('\n').split('\n')] if crontab == ['']: # Fixes issue #1181 (line count of empty crontab was 1 instead of 0) crontab = [] logger.debug('Read %s lines from user crontab' %len(crontab)) return crontab def writeCrontab(lines): """ Write to users crontab. Note: This will overwrite the whole 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 (:py:class:`list`, :py:class:`tuple`): lines that should be written to crontab Returns: bool: ``True`` if successful """ assert isinstance(lines, (list, tuple)), 'lines is not list or tuple type: %s' % lines with tempfile.NamedTemporaryFile(mode = 'wt') as f: f.write('\n'.join(lines)) f.write('\n\n') f.flush() cmd = ['crontab', f.name] proc = subprocess.Popen(cmd, stdout = subprocess.DEVNULL, stderr = subprocess.PIPE, universal_newlines = True) out, err = proc.communicate() if proc.returncode or err: logger.error('Failed to write lines to crontab: %s, %s' %(proc.returncode, err)) return False else: logger.debug('Wrote %s lines to user crontab' %len(lines)) return True 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 isIPv6Address(address): """ Check if ``address`` is a valid IPv6 address. Args: address (str): address that should get tested Returns: bool: True if ``address`` is a valid IPv6 address """ try: return isinstance(ipaddress.IPv6Address(address), ipaddress.IPv6Address) except: return False def escapeIPv6Address(address): """ Escape IPv6 Addresses with square brackets ``[]``. Args: address (str): address that should be escaped Returns: str: ``address`` in square brackets """ if isIPv6Address(address): return '[{}]'.format(address) else: 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: syncfs() self.started = True proc = subprocess.Popen(['shutdown', '-h', 'now']) proc.communicate() return proc.returncode if self.proxy is None: return(False) else: syncfs() 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 e: if e._dbus_error_name == 'net.launchpad.backintime.InvalidChar': raise InvalidChar(str(e)) elif e._dbus_error_name == 'net.launchpad.backintime.InvalidCmd': raise InvalidCmd(str(e)) elif e._dbus_error_name == 'net.launchpad.backintime.LimitExceeded': raise LimitExceeded(str(e)) 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 e: if e._dbus_error_name == 'com.ubuntu.DeviceDriver.PermissionDeniedByPolicy': raise PermissionDeniedByPolicy(str(e)) else: raise 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.4.3/debian/000077500000000000000000000000001455673541400150655ustar00rootroot00000000000000backintime-1.4.3/debian/backintime-qt.maintscript000066400000000000000000000002341455673541400220730ustar00rootroot00000000000000# Moved to /usr/share in 1.4.2; remove old copy in /etc if not modified rm_conffile /etc/dbus-1/system.d/net.launchpad.backintime.serviceHelper.conf 1.4.2~~backintime-1.4.3/debian/changelog000066400000000000000000000057071455673541400167500ustar00rootroot00000000000000backintime (1.4.3) unstable; urgency=low * 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` -- Germar Reitze Tue, 30 Jan 2024 22:11:16 +0100 backintime-1.4.3/debian/compat000066400000000000000000000000021455673541400162630ustar00rootroot000000000000009 backintime-1.4.3/debian/control000066400000000000000000000050751455673541400164770ustar00rootroot00000000000000Source: backintime Maintainer: BIT Team Section: utils Priority: extra Build-Depends: debhelper (>= 7), dh-python X-Python3-Version: >= 3.3 Standards-Version: 3.9.5 Homepage: https://github.com/bit-team/backintime Package: backintime-common Architecture: all Depends: rsync, cron-daemon, openssh-client, python3-keyring, python3-dbus, python3-packaging, ${python3:Depends}, ${misc:Depends} Recommends: sshfs, encfs Conflicts: backintime Replaces: backintime Description: Simple backup system (common) This package contains non GUI files used by different GUI frontends. Package: backintime-qt Architecture: all Depends: x11-utils, libnotify-bin, python3-pyqt5, python3-dbus.mainloop.pyqt5, policykit-1, backintime-common (>= ${source:Version}~), ${python3:Depends}, ${misc:Depends} Recommends: python3-secretstorage Suggests: meld | kompare Conflicts: backintime-qt4 (<< ${source:Version}~), backintime-kde (<< ${source:Version}~), backintime-kde4 (<< ${source:Version}~), backintime-gnome (<< ${source:Version}~), backintime-notify (<< ${source:Version}~) Replaces: backintime-qt4 (<< ${source:Version}~), backintime-kde (<< ${source:Version}~), backintime-kde4 (<< ${source:Version}~), backintime-gnome (<< ${source:Version}~), backintime-notify (<< ${source:Version}~) Description: Simple backup system This is a Qt5 GUI frontend for backintime-common. Package: backintime-qt4 Section: oldlibs Architecture: all Depends: backintime-qt, ${misc:Depends} Description: Virtual package This is a virtual package to replace backintime-qt4 from official repositories with backintime-qt from PPA. It can safely be removed. Package: backintime-kde Section: oldlibs Architecture: all Depends: backintime-qt, ${misc:Depends} Description: Virtual package This is a virtual package to replace backintime-kde from official repositories with backintime-qt from PPA. It can safely be removed. Package: backintime-kde4 Section: oldlibs Architecture: all Depends: backintime-qt, ${misc:Depends} Description: Virtual package This is a virtual package to replace backintime-kde4 with backintime-qt. It can safely be removed. Package: backintime-gnome Section: oldlibs Architecture: all Depends: backintime-qt, ${misc:Depends} Description: Virtual package This is a virtual package to replace backintime-gnome with backintime-qt. It can safely be removed. Package: backintime-notify Section: oldlibs Architecture: all Depends: backintime-qt, ${misc:Depends} Description: Virtual package This is a virtual package to replace backintime-notify with backintime-qt. It can safely be removed. backintime-1.4.3/debian/copyright000066400000000000000000000015571455673541400170300ustar00rootroot00000000000000Back In Time Copyright (C) 2008-2022 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze A copy of the license can be found: /usr/share/common-licenses/GPL-2 /usr/share/doc/backintime/LICENSE 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. backintime-1.4.3/debian/rules000077500000000000000000000012641455673541400161500ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 override_dh_auto_clean: rm -rf locale common/po/*.mo find $(CURDIR) -name "*\.py[co]" -delete # these get regenerated rm -f */man/C/*.1.gz rm -f common/Makefile qt/Makefile rm -f common/config-example-*.gz override_dh_auto_configure: cd common && ./configure cd qt && ./configure override_dh_auto_build: cd common && $(MAKE) cd qt && $(MAKE) override_dh_auto_install: cd common && DESTDIR=../debian/backintime-common $(MAKE) install cd qt && DESTDIR=../debian/backintime-qt $(MAKE) install override_dh_python3: dh_python3 /usr/share/backintime/ %: dh $@ --with python3 backintime-1.4.3/debian/source/000077500000000000000000000000001455673541400163655ustar00rootroot00000000000000backintime-1.4.3/debian/source/format000066400000000000000000000000041455673541400175720ustar00rootroot000000000000001.0 backintime-1.4.3/make-tarball.sh000077500000000000000000000007521455673541400165420ustar00rootroot00000000000000#!/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}/TRANSLATIONS\ ${NEW}/VERSION ${NEW}/makedeb.sh ${NEW}/common \ ${NEW}/qt ${NEW}/debian backintime-1.4.3/makedeb-src.sh000077500000000000000000000011031455673541400163520ustar00rootroot00000000000000#!/bin/bash RELEASES="bionic focal jammy kinetic" PKGNAME=backintime PKGVER=$(cat VERSION) TMP=$(mktemp -d) CURRENT=$(pwd) for release in ${RELEASES}; do echo "" echo "${PKGNAME} ${PKGVER} ${release}" echo "" DST=${TMP}/${PKGNAME}-${PKGVER}~${release}/ mkdir ${DST} cp -aR ${CURRENT}/* ${DST} cd ${DST} #debian: changelog sed -e "s/backintime (.*)/backintime (${PKGVER}~${release})/g" -e "s/unstable;/${release};/g" -i debian/changelog debuild -i -S done cp ${TMP}/*.build ${TMP}/*.buildinfo ${TMP}/*.changes ${TMP}/*.dsc ${TMP}/*.tar.gz ${CURRENT} rm -rf ${TMP} backintime-1.4.3/makedeb.sh000077500000000000000000000001321455673541400155660ustar00rootroot00000000000000#!/bin/bash PKGNAME=backintime PKGVER=$(cat VERSION) ARCH=all dpkg-buildpackage -us -uc backintime-1.4.3/qt/000077500000000000000000000000001455673541400142675ustar00rootroot00000000000000backintime-1.4.3/qt/app.py000066400000000000000000002201731455673541400154260ustar00rootroot00000000000000# 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 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 from exceptions import MountException from PyQt5.QtGui import QDesktopServices, QColor, QIcon from PyQt5.QtWidgets import (QWidget, QAction, QFrame, QMainWindow, QToolButton, QLabel, QLineEdit, QCheckBox, QListWidget, QTreeView, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QStyledItemDelegate, QVBoxLayout, QHBoxLayout, QStackedLayout, QSplitter, QGroupBox, QMenu, QToolBar, QProgressBar, QMessageBox, QInputDialog, QDialog, QDialogButtonBox, QShortcut, QFileSystemModel, ) from PyQt5.QtCore import (Qt, QObject, QPoint, pyqtSlot, pyqtSignal, QTimer, QThread, QEvent, QSortFilterProxyModel, QDir, QSize, QUrl, pyqtRemoveInputHook, ) import settingsdialog import snapshotsdialog import logviewdialog from restoredialog import RestoreDialog import languagedialog import messagebox 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.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.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.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)), int(self.config.profileIntValue( 'qt.places.SortOrder', Qt.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.Sunken) self.lblFolderDontExists.setFrameShape(QFrame.Panel) self.lblFolderDontExists.setAlignment(Qt.AlignHCenter | Qt.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.NoEditTriggers) self.filesView.setItemsExpandable(False) self.filesView.setDragEnabled(False) self.filesView.setSelectionMode(QAbstractItemView.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.AllDirs | QDir.AllEntries | QDir.NoDotAndDotDot | QDir.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.AscendingOrder if sortOrder else Qt.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.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 settingdialog if it is not configured if not config.isConfigured(): message = _( '{appName} is not configured. Would you like ' 'to restore a previous configuration?') \ .format(appName=self.config.APP_NAME) if QMessageBox.Yes == messagebox.warningYesNo(self, message): 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): messagebox.critical(self, _( "Can't find snapshots folder.\nIf it is on a removable " "drive please plug it in and then press OK.")) 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() @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': ( None, _('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': ( None, _('Translation'), self.slot_help_translation, None, None), '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 = { '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_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, ] toolbar.addActions(actions_for_toolbar) # 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.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.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 has finished.' '\nDo you really want to close?') if QMessageBox.Yes != messagebox.warningYesNo(self, msg): return event.ignore() 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.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) 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)), int(self.config.profileIntValue('qt.places.SortOrder', Qt.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): 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.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): item = QTreeWidgetItem() item.setText(0, name) if icon: item.setIcon(0, QIcon.fromTheme(icon)) item.setData(0, Qt.UserRole, path) if not path: item.setFont(0, qttools.fontBold(item.font(0))) item.setFlags(Qt.ItemIsEnabled) item.setBackground(0, QColor(196, 196, 196)) item.setForeground(0, QColor(60, 60, 60)) 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') #add backup folders include_folders = self.config.include() if include_folders: folders = [] for item in include_folders: if item[1] == 0: folders.append(item[0]) if folders: sortColumn = self.places.header().sortIndicatorSection() sortOrder = self.places.header().sortIndicatorOrder() if not sortColumn: folders.sort(key = lambda v: (v.upper(), v[0].islower()), reverse = sortOrder) self.addPlace(_('Backup folders'), '', '') for folder in 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.AscendingOrder: if profile_id in self.placesSortLoop and self.placesSortLoop[profile_id]: newColumn, newOrder = 1, Qt.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.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])) if QMessageBox.Yes != messagebox.warningYesNo(self, question_msg): 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 = About(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)} msg = self.config.changelog() 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()) cb.setToolTip(_( "Newer versions of files will be renamed with trailing " "{suffix} before restoring.\n" "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())) ) 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.')) cb.setToolTip("""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.NoSelection) return {'widget': fileList, 'retFunc': None} def deleteOnRestore(self): cb = QCheckBox(_('Remove newer elements in original folder.')) cb.setToolTip(_('Restore selected files or folders ' 'to the original destination and\n' 'delete files or folders which are ' 'not in the snapshot.\n' 'Be extremely careful because this will\n' 'delete files and folders which were\n' '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 = '{}\n\n{}'.format( msg, _('WARNING: Deleting files in filesystem root could break ' 'your whole system!')) return QMessageBox.Yes == messagebox.warningYesNo(self, msg) 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 QDialog.Accepted == dlg.exec_(): 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): path = self.path_history.previous() full_path = self.sid.pathBackup(path) if os.path.isdir(full_path) and self.sid.canOpenPath(path): self.path = path self.updateFilesView(0) def btnFolderHistoryNextClicked(self): path = self.path_history.next() full_path = self.sid.pathBackup(path) if os.path.isdir(full_path) and self.sid.canOpenPath(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) if os.path.exists(full_path) and self.sid.canOpenPath(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.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.setFilterRegExp(r'') else: self.filesViewProxyModel.setFilterRegExp(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): has_files = (self.filesViewProxyModel.rowCount(self.filesView.rootIndex()) > 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() class About(QDialog): def __init__(self, parent = None): super(About, self).__init__(parent) self.parent = parent self.config = parent.config import icon self.setWindowTitle(_('About') + ' ' + self.config.APP_NAME) logo = QLabel('Icon') logo.setPixmap(icon.BIT_LOGO.pixmap(QSize(48, 48))) version = self.config.VERSION ref, hashid = tools.gitRevisionAndHash() git_version = '' if ref: git_version = " git branch '{}' hash '{}'".format(ref, hashid) name = QLabel('

' + self.config.APP_NAME + ' ' + version + '

' + git_version) name.setAlignment(Qt.AlignLeft | Qt.AlignTop) homepage = QLabel(self.mkurl('')) homepage.setTextInteractionFlags(Qt.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) buttonBoxLeft = QDialogButtonBox(self) btn_authors = buttonBoxLeft.addButton(_('Authors'), QDialogButtonBox.ActionRole) btn_translations = buttonBoxLeft.addButton(_('Translations'), QDialogButtonBox.ActionRole) btn_license = buttonBoxLeft.addButton(_('License'), QDialogButtonBox.ActionRole) buttonBoxRight = QDialogButtonBox(QDialogButtonBox.Ok) hlayout = QHBoxLayout() hlayout.addWidget(buttonBoxLeft) hlayout.addWidget(buttonBoxRight) vlayout.addLayout(hlayout) btn_authors.clicked.connect(self.authors) btn_translations.clicked.connect(self.translations) btn_license.clicked.connect(self.license) buttonBoxRight.accepted.connect(self.accept) def authors(self): return messagebox.showInfo(self, _('Authors'), self.mkurl(self.config.authors())) def translations(self): return messagebox.showInfo(self, _('Translations'), self.mkurl(self.config.translations())) def license(self): return messagebox.showInfo(self, _('License'), self.config.license()) def mkurl(self, msg): msg = re.sub(r'<(.*?)>', self.aHref, msg) msg = re.sub(r'\n', '
', msg) return msg def aHref(self, m): if m.group(1).count('@'): return '%(url)s' % {'url': m.group(1)} else: return '%(url)s' % {'url': m.group(1)} class ExtraMouseButtonEventFilter(QObject): """ globally catch mouse buttons 4 and 5 (mostly used as back and forward) and assign it to browse in file history. When updating to Qt5 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.MouseButtonPress and event.button() in (Qt.XButton1, Qt.XButton2): if event.button() == Qt.XButton1: self.mainWindow.btnFolderHistoryPreviousClicked() if event.button() == Qt.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() def debugTrace(): """ Set a tracepoint in the Python debugger that works with Qt """ from pdb import set_trace pyqtRemoveInputHook() set_trace() 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.4.3/qt/backintime-qt000077500000000000000000000022151455673541400167450ustar00rootroot00000000000000#!/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.4.3/qt/backintime-qt-root.desktop000066400000000000000000000007161455673541400213770ustar00rootroot00000000000000[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.4.3/qt/backintime-qt.desktop000066400000000000000000000006711455673541400204160ustar00rootroot00000000000000[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.4.3/qt/backintime-qt_polkit000077500000000000000000000005361455673541400203330ustar00rootroot00000000000000#!/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 Qt5 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.4.3/qt/configure000077500000000000000000000177011455673541400162040ustar00rootroot00000000000000#!/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 /etc/dbus-1/system.d/net.launchpad.backintime.serviceHelper.conf\n" >> ${MAKEFILE} printf "\trm -f \$(DEST)/share/backintime/plugins/qt4plugin.py\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" 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 "../debian/copyright" "/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 "../VERSION" "/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.4.3/qt/icon.py000066400000000000000000000132361455673541400155760ustar00rootroot00000000000000# 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 PyQt5.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" Qt5 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')) backintime-1.4.3/qt/icons/000077500000000000000000000000001455673541400154025ustar00rootroot00000000000000backintime-1.4.3/qt/icons/16x16/000077500000000000000000000000001455673541400161675ustar00rootroot00000000000000backintime-1.4.3/qt/icons/16x16/actions/000077500000000000000000000000001455673541400176275ustar00rootroot00000000000000backintime-1.4.3/qt/icons/16x16/actions/show-hidden.svg000066400000000000000000000333671455673541400225750ustar00rootroot00000000000000 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.4.3/qt/icons/22x22/000077500000000000000000000000001455673541400161615ustar00rootroot00000000000000backintime-1.4.3/qt/icons/22x22/actions/000077500000000000000000000000001455673541400176215ustar00rootroot00000000000000backintime-1.4.3/qt/icons/22x22/actions/show-hidden.svg000066400000000000000000000334541455673541400225640ustar00rootroot00000000000000 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.4.3/qt/icons/24x24/000077500000000000000000000000001455673541400161655ustar00rootroot00000000000000backintime-1.4.3/qt/icons/24x24/actions/000077500000000000000000000000001455673541400176255ustar00rootroot00000000000000backintime-1.4.3/qt/icons/24x24/actions/show-hidden.svg000066400000000000000000000334501455673541400225640ustar00rootroot00000000000000 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.4.3/qt/icons/32x32/000077500000000000000000000000001455673541400161635ustar00rootroot00000000000000backintime-1.4.3/qt/icons/32x32/actions/000077500000000000000000000000001455673541400176235ustar00rootroot00000000000000backintime-1.4.3/qt/icons/32x32/actions/show-hidden.svg000066400000000000000000000333751455673541400225700ustar00rootroot00000000000000 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.4.3/qt/icons/48x48/000077500000000000000000000000001455673541400162015ustar00rootroot00000000000000backintime-1.4.3/qt/icons/48x48/actions/000077500000000000000000000000001455673541400176415ustar00rootroot00000000000000backintime-1.4.3/qt/icons/48x48/actions/show-hidden.svg000066400000000000000000000333241455673541400226000ustar00rootroot00000000000000 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.4.3/qt/icons/scalable/000077500000000000000000000000001455673541400171505ustar00rootroot00000000000000backintime-1.4.3/qt/icons/scalable/actions/000077500000000000000000000000001455673541400206105ustar00rootroot00000000000000backintime-1.4.3/qt/icons/scalable/actions/show-hidden.svg000066400000000000000000000333241455673541400235470ustar00rootroot00000000000000 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.4.3/qt/languagedialog.py000066400000000000000000000221641455673541400176110ustar00rootroot00000000000000import unicodedata import textwrap from PyQt5.QtCore import Qt, QSize, QTimer from PyQt5.QtGui import QCursor from PyQt5.QtWidgets import (QApplication, QDialog, QWidget, QScrollArea, QGridLayout, QLayout, QVBoxLayout, QDialogButtonBox, QRadioButton, QLabel, QToolTip, ) import tools import qttools import languages class LanguageDialog(QDialog): 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.WindowMaximizeButtonHint, True) scroll = QScrollArea(self) scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll.setHorizontalScrollBarPolicy(Qt.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.Cancel | QDialogButtonBox.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 = '{}\n{}'.format( tooltip, _('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, val): 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' @staticmethod def _complete_text(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. result = result.format( language=f'{language}', perc=f'{percent} %', translation_platform_url='{}'.format( __class__._URL_PLATFORM, _('translation platform')), back_in_time_project_website='Back In Time {}'.format( __class__._URL_PROJECT, _('Website')) ) 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.WindowMaximizeButtonHint, True) txt = __class__._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.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): QToolTip.showText(QCursor.pos(), url.replace('https://', '')) backintime-1.4.3/qt/logviewdialog.py000066400000000000000000000263511455673541400175040ustar00rootroot00000000000000# 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 PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * import qttools import snapshots import encfstools import snapshotlog import tools import messagebox 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.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(_('Information'), 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.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.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) self.watcher.fileChanged.connect(self.updateLog) # passes the path to the changed file to updateLog() self.txtLogView.setContextMenuPolicy(Qt.CustomContextMenu) self.txtLogView.customContextMenuRequested.connect(self.contextMenuClicked) def cbDecodeChanged(self): if self.cbDecode.isChecked(): if not self.decode: self.decode = encfstools.Decode(self.config) else: if not self.decode is 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 contextMenuClicked(self, point): menu = QMenu() clipboard = qttools.createQApplication().clipboard() cursor = self.txtLogView.textCursor() btnCopy = menu.addAction(_('Copy')) btnCopy.triggered.connect(lambda: clipboard.setText(cursor.selectedText())) btnCopy.setEnabled(cursor.hasSelection()) btnAddExclude = menu.addAction(_('Add to Exclude')) btnAddExclude.triggered.connect(self.btnAddExcludeClicked) btnAddExclude.setEnabled(cursor.hasSelection()) btnDecode = menu.addAction(_('Decode')) btnDecode.triggered.connect(self.btnDecodeClicked) btnDecode.setEnabled(cursor.hasSelection()) btnDecode.setVisible(self.config.snapshotsMode() == 'ssh_encfs') menu.exec_(self.txtLogView.mapToGlobal(point)) def btnAddExcludeClicked(self): exclude = self.config.exclude() path = self.txtLogView.textCursor().selectedText().strip() if not path or path in exclude: return edit = QLineEdit(self) edit.setText(path) edit.setMinimumWidth(600) options = { 'widget': edit, 'retFunc': edit.text, 'id': 'path' } confirm, opt = messagebox.warningYesNoOptions( self, _('Do you want to exclude this?'), (options, ) ) if not confirm: return exclude.append(opt['path']) self.config.setExclude(exclude) def btnDecodeClicked(self): if not self.decode: self.decode = encfstools.Decode(self.config) cursor = self.txtLogView.textCursor() selection = cursor.selectedText().strip() plain = self.decode.path(selection) cursor.insertText(plain) 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.4.3/qt/man/000077500000000000000000000000001455673541400150425ustar00rootroot00000000000000backintime-1.4.3/qt/man/C/000077500000000000000000000000001455673541400152245ustar00rootroot00000000000000backintime-1.4.3/qt/man/C/backintime-qt.1000066400000000000000000000114351455673541400200420ustar00rootroot00000000000000.TH backintime-qt 1 "Oct 2023" "version 1.4.3" "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 Qt5 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 backintime, backintime-config. .PP Back In Time also has a website: https://github.com/bit-team/backintime .SH AUTHOR This manual page was written by BIT Team(). backintime-1.4.3/qt/messagebox.py000066400000000000000000000102771455673541400170050ustar00rootroot00000000000000# 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 PyQt5.QtCore import QTimer, Qt from PyQt5.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.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 _('Information'), text) def critical(parent, msg): return QMessageBox.critical(parent, _('Error'), msg, buttons = QMessageBox.Ok, defaultButton = QMessageBox.Ok) def warningYesNo(parent, msg): return QMessageBox.question(parent, _('Question'), msg, buttons = QMessageBox.Yes | QMessageBox.No, defaultButton = QMessageBox.No) def warningYesNoOptions(parent, msg, options = ()): # 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.Yes | QDialogButtonBox.No) buttonBox.button(QDialogButtonBox.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.LinksAccessibleByMouse) label.setOpenExternalLinks(True) scroll_area = QScrollArea() scroll_area.setWidget(label) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok) buttonBox.accepted.connect(dlg.accept) vlayout.addWidget(scroll_area) vlayout.addWidget(buttonBox) return dlg.exec_() backintime-1.4.3/qt/net.launchpad.backintime.policy000066400000000000000000000035351455673541400223470ustar00rootroot00000000000000 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.4.3/qt/net.launchpad.backintime.serviceHelper.conf000066400000000000000000000016031455673541400245660ustar00rootroot00000000000000 system backintime-1.4.3/qt/net.launchpad.backintime.serviceHelper.service000066400000000000000000000002121455673541400252740ustar00rootroot00000000000000[D-BUS Service] Name=net.launchpad.backintime.serviceHelper Exec=/usr/bin/python3 -Es /usr/share/backintime/qt/serviceHelper.py User=root backintime-1.4.3/qt/plugins/000077500000000000000000000000001455673541400157505ustar00rootroot00000000000000backintime-1.4.3/qt/plugins/notifyplugin.py000066400000000000000000000036051455673541400210550ustar00rootroot00000000000000# 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.4.3/qt/plugins/systrayiconplugin.py000066400000000000000000000102471455673541400221340ustar00rootroot00000000000000# 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 time import gettext import _thread 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)? # Qt5 can handle wayland now! # if not tools.checkXServer(): # return False # New implementation: Let Qt5 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 Qt5 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_Qt5_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 Qt5 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.4.3/qt/qtsystrayicon.py000066400000000000000000000216761455673541400176110ustar00rootroot00000000000000# 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 # 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 PyQt5.QtCore import QObject, QTimer from PyQt5.QtWidgets import QSystemTrayIcon, QMenu, QProgressBar, QWidget from PyQt5.QtGui import QIcon, 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( '{}: {}'.format(_('Profile'), 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.RenderFlags(QWidget.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(logger.wrapLine(message[1], \ size = 80, \ delimiters = '', \ new_line_indicator = '') \ )) 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.4.3/qt/qttools.py000066400000000000000000000475761455673541400163710ustar00rootroot00000000000000# 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 from PyQt5.QtGui import (QFont, QColor, QKeySequence, QIcon) from PyQt5.QtCore import (QDir, Qt, pyqtSlot, pyqtSignal, QModelIndex, QTranslator, QLocale, QLibraryInfo, QT_VERSION_STR) from PyQt5.QtWidgets import (QFileDialog, QAbstractItemView, QListView, QTreeView, QDialog, QApplication, QStyleFactory, QTreeWidget, QTreeWidgetItem, QComboBox, QAction, QSystemTrayIcon, QWidget) 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 def fontBold(font): font.setWeight(QFont.Bold) return font def setFontBold(widget): widget.setFont(fontBold(widget.font())) def fontNormal(font): font.setWeight(QFont.Normal) return font def setFontNormal(widget): widget.setFont(fontNormal(widget.font())) def 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 givin characters. """ fm = widget.fontMetrics() for c in string: # Convert the unicode character to its integer representation # because fm.inFont() is not able to handle 2-byte characters if not fm.inFontUcs4(ord(c)): return False return True def 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(self.DontUseNativeDialog, True) self.setOption(self.HideNameFilterDetails, True) showHiddenAction = QAction(self) showHiddenAction.setShortcuts([QKeySequence(Qt.CTRL + Qt.Key_H),]) showHiddenAction.triggered.connect(self.toggleShowHidden) self.addAction(showHiddenAction) self.showHidden(hiddenFiles(parent)) def showHidden(self, enable): if enable: self.setFilter(self.filter() | QDir.Hidden) elif int(self.filter() & QDir.Hidden): self.setFilter(self.filter() ^ QDir.Hidden) def toggleShowHidden(self): self.showHidden(not int(self.filter() & QDir.Hidden)) def getExistingDirectories(parent, *args, **kwargs): """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.Directory) dlg.setOption(dlg.ShowDirsOnly, True) mode = QAbstractItemView.ExtendedSelection dlg.findChildren(QListView)[0].setSelectionMode(mode) dlg.findChildren(QTreeView)[0].setSelectionMode(mode) if dlg.exec_() == QDialog.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.Directory) dlg.setOption(dlg.ShowDirsOnly, True) if dlg.exec_() == QDialog.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.ExistingFiles) if dlg.exec_() == QDialog.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.ExistingFile) if dlg.exec_() == QDialog.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/Qt5 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(f'No language code. Use systems current locale.') language_code = QLocale.system().name() rc = translator.load( f'qt_{language_code}', QLibraryInfo.location(QLibraryInfo.TranslationsPath)) if rc == 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).') return translator def indexFirstColumn(idx): if idx.column() > 0: idx = idx.sibling(idx.row(), 0) return idx class MyTreeView(QTreeView): """ subclass QTreeView to emit a SIGNAL myCurrentIndexChanged if the SLOT currentChanged is called """ myCurrentIndexChanged = pyqtSignal(QModelIndex, QModelIndex) def currentChanged(self, current, previous): self.myCurrentIndexChanged.emit(current, previous) super(MyTreeView, self).currentChanged(current, previous) class TimeLine(QTreeWidget): updateFilesView = pyqtSignal(int) def __init__(self, parent): super(TimeLine, self).__init__(parent) self.setRootIsDecorated(False) self.setEditTriggers(QAbstractItemView.NoEditTriggers) self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.setHeaderLabels([_('Snapshots'), 'foo']) self.setSortingEnabled(True) self.sortByColumn(1, Qt.DescendingOrder) self.hideColumn(1) self.header().setSectionsClickable(False) self.parent = parent self.snapshots = parent.snapshots self._resetHeaderData() def clear(self): self._resetHeaderData() return super(TimeLine, self).clear() def _resetHeaderData(self): self.now = date.today() # list of tuples with (text, startDate, endDate) self.headerData = [] # 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.UserRole) class SnapshotItem(TimeLineItem): def __init__(self, sid): super(SnapshotItem, self).__init__() self.setText(0, sid.displayName) self.setFont(0, fontNormal(self.font(0))) self.setData(0, Qt.UserRole, sid) if sid.isRoot: self.setToolTip(0, _('This is NOT a snapshot but a live ' 'view of your local files')) else: self.setToolTip( 0, _('Last check {time}').format(time=sid.lastChecked)) def updateText(self): sid = self.snapshotID() self.setText(0, sid.displayName) class HeaderItem(TimeLineItem): def __init__(self, name, sid): super(HeaderItem, self).__init__() self.setText(0, name) self.setFont(0, fontBold(self.font(0))) self.setBackground(0, QColor(196, 196, 196)) self.setForeground(0, QColor(60, 60, 60)) self.setFlags(Qt.NoItemFlags) self.setData(0, Qt.UserRole, sid) class SortedComboBox(QComboBox): # Prevent inserting items abroad from addItem because this would break # sorting insertItem = NotImplemented def __init__(self, parent=None): super(SortedComboBox, self).__init__(parent) self.sortOrder = Qt.AscendingOrder self.sortRole = Qt.DisplayRole def addItem(self, text, userData=None): """ QComboBox doesn't support sorting so this little hack is used to insert items in sorted order. """ if self.sortRole == Qt.UserRole: sortObject = userData else: sortObject = text the_list = [ self.itemData(i, self.sortRole) for i in range(self.count())] the_list.append(sortObject) the_list.sort(reverse=self.sortOrder) 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.DescendingOrder self.sortRole = Qt.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 # Since Qt 5.1 not needed anymore. # Use menu.setToolTipsVisible(True). # See https://bugreports.qt.io/browse/QTBUG-13663 # class Menu(QMenu): # """ # Subclass QMenu to add ToolTips # """ # def event(self, e): # action = self.activeAction() # if (e.type() == QEvent.ToolTip # and action # and action.toolTip() != action.text()): # QToolTip.showText(e.globalPos(), self.activeAction().toolTip()) # else: # QToolTip.hideText() # return super(Menu, self).event(e) backintime-1.4.3/qt/qttools_path.py000066400000000000000000000027011455673541400173620ustar00rootroot00000000000000# 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 import gettext 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.4.3/qt/restoredialog.py000066400000000000000000000105411455673541400175050ustar00rootroot00000000000000# 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 PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * import tools import qttools 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.NoWrap) self.txtLogView.setMaximumBlockCount(100000) self.mainLayout.addWidget(self.txtLogView) #buttons buttonBox = QDialogButtonBox(QDialogButtonBox.Close) showLog = buttonBox.addButton(_('Show full Log'), QDialogButtonBox.ActionRole) self.mainLayout.addWidget(buttonBox) self.btnClose = buttonBox.button(QDialogButtonBox.Close) self.btnClose.setEnabled(False) buttonBox.rejected.connect(self.close) showLog.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(self.logFile))) #restore in separate thread self.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.4.3/qt/serviceHelper.py000066400000000000000000000317771455673541400174600ustar00rootroot00000000000000# (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.pyqt5 from PyQt5.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 cmd/parameter (%s)" % 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__': dbus.mainloop.pyqt5.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.4.3/qt/settingsdialog.py000066400000000000000000002730401455673541400176670ustar00rootroot00000000000000# 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 from PyQt5.QtGui import QIcon, QFont, QPalette, QBrush, QColor from PyQt5.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, QFileSystemModel, QMenu, QProgressBar, QPlainTextEdit) from PyQt5.QtCore import (Qt, QDir, QSortFilterProxyModel, QThread, pyqtSignal, pyqtRemoveInputHook) import config import tools import qttools import mount import messagebox import snapshots import sshtools import logger from exceptions import MountException, NoPubKeyLogin, KnownHost 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.NoFrame) self.tabs.addTab(scrollArea, _('&General')) layoutWidget = QWidget(self) layout = QVBoxLayout(layoutWidget) # select mode self.mode = None vlayout = QVBoxLayout() layout.addLayout(vlayout) self.lblModes = QLabel(_('Mode') + ':', self) self.comboModes = QComboBox(self) hlayout = QHBoxLayout() hlayout.addWidget(self.lblModes) hlayout.addWidget(self.comboModes, 1) vlayout.addLayout(hlayout) store_modes = {} for key in list(self.config.SNAPSHOT_MODES.keys()): store_modes[key] = self.config.SNAPSHOT_MODES[key][1] self.fillCombo(self.comboModes, store_modes) # encfs security warning self.encfsWarning = QLabel('{}: {}'.format( _('Warning'), _('{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".') .format(app=self.config.APP_NAME) )) self.encfsWarning.setWordWrap(True) layout.addWidget(self.encfsWarning) # Where to save snapshots groupBox = QGroupBox(self) self.modeLocal = groupBox groupBox.setTitle(_('Where to save snapshots')) layout.addWidget(groupBox) vlayout = QVBoxLayout(groupBox) hlayout = QHBoxLayout() vlayout.addLayout(hlayout) self.editSnapshotsPath = QLineEdit(self) self.editSnapshotsPath.setReadOnly(True) self.editSnapshotsPath.textChanged.connect(self.fullPathChanged) hlayout.addWidget(self.editSnapshotsPath) self.btnSnapshotsPath = QToolButton(self) self.btnSnapshotsPath.setToolButtonStyle(Qt.ToolButtonIconOnly) self.btnSnapshotsPath.setIcon(icon.FOLDER) self.btnSnapshotsPath.setText(_('Folder')) self.btnSnapshotsPath.setMinimumSize(32, 28) hlayout.addWidget(self.btnSnapshotsPath) self.btnSnapshotsPath.clicked.connect(self.btnSnapshotsPathClicked) # SSH groupBox = QGroupBox(self) self.modeSsh = groupBox groupBox.setTitle(_('SSH Settings')) layout.addWidget(groupBox) vlayout = QVBoxLayout(groupBox) hlayout1 = QHBoxLayout() vlayout.addLayout(hlayout1) hlayout2 = QHBoxLayout() vlayout.addLayout(hlayout2) hlayout3 = QHBoxLayout() vlayout.addLayout(hlayout3) self.lblSshHost = QLabel(_('Host') + ':', self) hlayout1.addWidget(self.lblSshHost) self.txtSshHost = QLineEdit(self) hlayout1.addWidget(self.txtSshHost) self.lblSshPort = QLabel(_('Port') + ':', self) hlayout1.addWidget(self.lblSshPort) self.txtSshPort = QLineEdit(self) hlayout1.addWidget(self.txtSshPort) self.lblSshUser = QLabel(_('User') + ':', self) hlayout1.addWidget(self.lblSshUser) self.txtSshUser = QLineEdit(self) hlayout1.addWidget(self.txtSshUser) self.lblSshPath = QLabel(_('Path') + ':', self) hlayout2.addWidget(self.lblSshPath) self.txtSshPath = QLineEdit(self) self.txtSshPath.textChanged.connect(self.fullPathChanged) hlayout2.addWidget(self.txtSshPath) self.lblSshCipher = QLabel(_('Cipher') + ':', self) hlayout3.addWidget(self.lblSshCipher) self.comboSshCipher = QComboBox(self) hlayout3.addWidget(self.comboSshCipher) self.fillCombo(self.comboSshCipher, self.config.SSH_CIPHERS) self.lblSshPrivateKeyFile = QLabel(_('Private Key') + ':', self) hlayout3.addWidget(self.lblSshPrivateKeyFile) self.txtSshPrivateKeyFile = QLineEdit(self) self.txtSshPrivateKeyFile.setReadOnly(True) hlayout3.addWidget(self.txtSshPrivateKeyFile) self.btnSshPrivateKeyFile = QToolButton(self) self.btnSshPrivateKeyFile.setToolButtonStyle(Qt.ToolButtonIconOnly) self.btnSshPrivateKeyFile.setIcon(icon.FOLDER) self.btnSshPrivateKeyFile.setToolTip( _('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.ToolButtonIconOnly) self.btnSshKeyGen.setIcon(icon.ADD) self.btnSshKeyGen.setToolTip( _('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) # encfs self.modeLocalEncfs = self.modeLocal self.modeSshEncfs = self.modeSsh # password groupBox = QGroupBox(self) self.groupPassword1 = groupBox groupBox.setTitle(_('Password')) layout.addWidget(groupBox) vlayout = QVBoxLayout(groupBox) hlayout1 = QHBoxLayout() vlayout.addLayout(hlayout1) hlayout2 = QHBoxLayout() vlayout.addLayout(hlayout2) self.lblPassword1 = QLabel(_('Password'), self) hlayout1.addWidget(self.lblPassword1) self.txtPassword1 = QLineEdit(self) self.txtPassword1.setEchoMode(QLineEdit.Password) hlayout1.addWidget(self.txtPassword1) self.lblPassword2 = QLabel(_('Password'), self) hlayout2.addWidget(self.lblPassword2) self.txtPassword2 = QLineEdit(self) self.txtPassword2.setEchoMode(QLineEdit.Password) hlayout2.addWidget(self.txtPassword2) self.cbPasswordSave = QCheckBox(_('Save Password to Keyring'), self) vlayout.addWidget(self.cbPasswordSave) self.cbPasswordUseCache = QCheckBox( _('Cache Password for Cron (Security ' 'issue: root can read password)'), self ) vlayout.addWidget(self.cbPasswordUseCache) self.keyringSupported = tools.keyringSupported() self.cbPasswordSave.setEnabled(self.keyringSupported) # mode change self.comboModes.currentIndexChanged.connect(self.comboModesChanged) # host, user, profile id groupBox = QGroupBox(self) self.frameAdvanced = groupBox groupBox.setTitle(_('Advanced')) layout.addWidget(groupBox) hlayout = QHBoxLayout(groupBox) hlayout.addSpacing(12) vlayout2 = QVBoxLayout() hlayout.addLayout(vlayout2) hlayout2 = QHBoxLayout() vlayout2.addLayout(hlayout2) self.lblHost = QLabel(_('Host') + ':', self) hlayout2.addWidget(self.lblHost) self.txtHost = QLineEdit(self) self.txtHost.textChanged.connect(self.fullPathChanged) hlayout2.addWidget(self.txtHost) self.lblUser = QLabel(_('User') + ':', self) hlayout2.addWidget(self.lblUser) self.txtUser = QLineEdit(self) self.txtUser.textChanged.connect(self.fullPathChanged) hlayout2.addWidget(self.txtUser) self.lblProfile = QLabel(_('Profile') + ':', self) hlayout2.addWidget(self.lblProfile) self.txt_profile = QLineEdit(self) self.txt_profile.textChanged.connect(self.fullPathChanged) hlayout2.addWidget(self.txt_profile) self.lblFullPath = QLabel(_('Full snapshot path') + ': ', self) self.lblFullPath.setWordWrap(True) vlayout2.addWidget(self.lblFullPath) # Schedule groupBox = QGroupBox(self) self.globalScheduleGroupBox = groupBox groupBox.setTitle(_('Schedule')) layout.addWidget(groupBox) glayout = QGridLayout(groupBox) glayout.setColumnStretch(1, 2) self.comboSchedule = QComboBox(self) glayout.addWidget(self.comboSchedule, 0, 0, 1, 2) # 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: _('Every hour'), 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.AlignRight | Qt.AlignVCenter) glayout.addWidget(self.lblScheduleDay, 1, 0) self.comboScheduleDay = QComboBox(self) glayout.addWidget(self.comboScheduleDay, 1, 1) for d in range(1, 29): self.comboScheduleDay.addItem(QIcon(), str(d), d) self.lblScheduleWeekday = QLabel(_('Weekday') + ':', self) self.lblScheduleWeekday.setContentsMargins(5, 0, 0, 0) self.lblScheduleWeekday.setAlignment(Qt.AlignRight | Qt.AlignVCenter) glayout.addWidget(self.lblScheduleWeekday, 2, 0) self.comboScheduleWeekday = QComboBox(self) glayout.addWidget(self.comboScheduleWeekday, 2, 1) for d in range(1, 8): self.comboScheduleWeekday.addItem( QIcon(), datetime.date(2011, 11, 6 + d).strftime("%A"), d ) self.lblScheduleTime = QLabel(_('Hour') + ':', self) self.lblScheduleTime.setContentsMargins(5, 0, 0, 0) self.lblScheduleTime.setAlignment(Qt.AlignRight | Qt.AlignVCenter) glayout.addWidget(self.lblScheduleTime, 3, 0) self.comboScheduleTime = QComboBox(self) glayout.addWidget(self.comboScheduleTime, 3, 1) for t in range(0, 2400, 100): self.comboScheduleTime.addItem( QIcon(), datetime.time(t // 100, t % 100).strftime("%H:%M"), t ) self.lblScheduleCronPatern = QLabel(_('Hours') + ':', self) self.lblScheduleCronPatern.setContentsMargins(5, 0, 0, 0) self.lblScheduleCronPatern.setAlignment( Qt.AlignRight | Qt.AlignVCenter) glayout.addWidget(self.lblScheduleCronPatern, 4, 0) self.txtScheduleCronPatern = QLineEdit(self) glayout.addWidget(self.txtScheduleCronPatern, 4, 1) # anacron self.lblScheduleRepeated = QLabel( _('Run Back In Time repeatedly. This is useful if the ' 'computer is not running regularly.') ) self.lblScheduleRepeated.setContentsMargins(5, 0, 0, 0) self.lblScheduleRepeated.setWordWrap(True) glayout.addWidget(self.lblScheduleRepeated, 5, 0, 1, 2) self.lblScheduleRepeatedPeriod = QLabel(_('Every') + ':') self.lblScheduleRepeatedPeriod.setContentsMargins(5, 0, 0, 0) self.lblScheduleRepeatedPeriod.setAlignment( Qt.AlignRight | Qt.AlignVCenter) glayout.addWidget(self.lblScheduleRepeatedPeriod, 7, 0) hlayout = QHBoxLayout() self.spbScheduleRepeatedPeriod = QSpinBox(self) self.spbScheduleRepeatedPeriod.setSingleStep(1) self.spbScheduleRepeatedPeriod.setRange(1, 10000) hlayout.addWidget(self.spbScheduleRepeatedPeriod) self.comboScheduleRepeatedUnit = QComboBox(self) 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) # layout.addStretch() scrollArea.setWidget(layoutWidget) scrollArea.setWidgetResizable(True) # TAB: Include tabWidget = QWidget(self) self.tabs.addTab(tabWidget, _('&Include')) layout = QVBoxLayout(tabWidget) self.listInclude = QTreeWidget(self) self.listInclude.setSelectionMode(QAbstractItemView.ExtendedSelection) self.listInclude.setRootIsDecorated(False) self.listInclude.setHeaderLabels( [_('Include files and folders'), 'Count']) self.listInclude.header().setSectionResizeMode(0, QHeaderView.Stretch) self.listInclude.header().setSectionsClickable(True) self.listInclude.header().setSortIndicatorShown(True) self.listInclude.header().setSectionHidden(1, True) self.listIncludeSortLoop = False self.listInclude.header().sortIndicatorChanged \ .connect(self.includeCustomSortOrder) layout.addWidget(self.listInclude) self.listIncludeCount = 0 buttonsLayout = QHBoxLayout() layout.addLayout(buttonsLayout) self.btnIncludeFile = QPushButton(icon.ADD, _('Add file'), self) buttonsLayout.addWidget(self.btnIncludeFile) self.btnIncludeFile.clicked.connect(self.btnIncludeFileClicked) self.btnIncludeAdd = QPushButton(icon.ADD, _('Add folder'), self) buttonsLayout.addWidget(self.btnIncludeAdd) self.btnIncludeAdd.clicked.connect(self.btnIncludeAddClicked) self.btnIncludeRemove = QPushButton(icon.REMOVE, _('Remove'), self) buttonsLayout.addWidget(self.btnIncludeRemove) self.btnIncludeRemove.clicked.connect(self.btnIncludeRemoveClicked) # TAB: Exclude tabWidget = QWidget(self) self.tabs.addTab(tabWidget, _('&Exclude')) layout = QVBoxLayout(tabWidget) self.lblSshEncfsExcludeWarning = QLabel( "{}: {}".format( _("Warning"), _( "Wildcards ({example1}) will be ignored " "with mode 'SSH encrypted'.\nOnly single or double " "asterisks are allowed ({example2})" ).format(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.ExtendedSelection) self.listExclude.setRootIsDecorated(False) self.listExclude.setHeaderLabels( [_('Exclude patterns, files or folders'), 'Count']) self.listExclude.header().setSectionResizeMode(0, QHeaderView.Stretch) self.listExclude.header().setSectionsClickable(True) self.listExclude.header().setSortIndicatorShown(True) self.listExclude.header().setSectionHidden(1, True) self.listExcludeSortLoop = False self.listExclude.header().sortIndicatorChanged \ .connect(self.excludeCustomSortOrder) layout.addWidget(self.listExclude) self.listExcludeCount = 0 label = QLabel(_('Highly recommended') + ':', self) qttools.setFontBold(label) layout.addWidget(label) label = QLabel(', '.join(sorted(self.config.DEFAULT_EXCLUDE)), self) label.setWordWrap(True) layout.addWidget(label) buttonsLayout = QHBoxLayout() layout.addLayout(buttonsLayout) self.btnExcludeAdd = QPushButton(icon.ADD, _('Add'), self) buttonsLayout.addWidget(self.btnExcludeAdd) self.btnExcludeAdd.clicked.connect(self.btnExcludeAddClicked) self.btnExcludeFile = QPushButton(icon.ADD, _('Add file'), self) buttonsLayout.addWidget(self.btnExcludeFile) self.btnExcludeFile.clicked.connect(self.btnExcludeFileClicked) self.btnExcludeFolder = QPushButton(icon.ADD, _('Add folder'), self) buttonsLayout.addWidget(self.btnExcludeFolder) self.btnExcludeFolder.clicked.connect(self.btnExcludeFolderClicked) self.btnExcludeDefault = QPushButton(icon.DEFAULT_EXCLUDE, _('Add default'), self) buttonsLayout.addWidget(self.btnExcludeDefault) self.btnExcludeDefault.clicked.connect(self.btnExcludeDefaultClicked) self.btnExcludeRemove = QPushButton(icon.REMOVE, _('Remove'), self) buttonsLayout.addWidget(self.btnExcludeRemove) self.btnExcludeRemove.clicked.connect(self.btnExcludeRemoveClicked) # exclude files by size hlayout = QHBoxLayout() layout.addLayout(hlayout) self.cbExcludeBySize = QCheckBox( _('Exclude files bigger than: '), self) self.cbExcludeBySize.setToolTip( _('Exclude files bigger than value in %(prefix)s.\n' 'With \'Full rsync mode\' disabled this will only affect ' 'new files\n' 'because for rsync this is a transfer option, not an ' 'exclude option.\n' 'So big files that have been backed up before will remain ' 'in snapshots\n' 'even if they have changed.' % {'prefix': 'MiB'}) ) hlayout.addWidget(self.cbExcludeBySize) self.spbExcludeBySize = QSpinBox(self) self.spbExcludeBySize.setSuffix(' MiB') self.spbExcludeBySize.setRange(0, 100000000) hlayout.addWidget(self.spbExcludeBySize) hlayout.addStretch() enabled = lambda state: self.spbExcludeBySize.setEnabled(state) enabled(False) self.cbExcludeBySize.stateChanged.connect(enabled) # TAB: Auto-remove scrollArea = QScrollArea(self) scrollArea.setFrameStyle(QFrame.NoFrame) self.tabs.addTab(scrollArea, _('&Auto-remove')) layoutWidget = QWidget(self) layout = QGridLayout(layoutWidget) # remove old snapshots self.cbRemoveOlder = QCheckBox(_('Older than') + ':', self) layout.addWidget(self.cbRemoveOlder, 0, 0) self.cbRemoveOlder.stateChanged.connect(self.updateRemoveOlder) self.spbRemoveOlder = QSpinBox(self) self.spbRemoveOlder.setRange(1, 1000) layout.addWidget(self.spbRemoveOlder, 0, 1) self.comboRemoveOlderUnit = QComboBox(self) layout.addWidget(self.comboRemoveOlderUnit, 0, 2) 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 remove:'), self) layout.addWidget(self.cbSmartRemove, 3, 0) widget = QWidget(self) widget.setContentsMargins(25, 0, 0, 0) layout.addWidget(widget, 4, 0, 1, 3) smlayout = QGridLayout(widget) self.cbSmartRemoveRunRemoteInBackground = QCheckBox( '{} {}!'.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.NoFrame) self.tabs.addTab(scrollArea, _('&Options')) layoutWidget = QWidget(self) layout = QVBoxLayout(layoutWidget) self.cbNotify = QCheckBox(_('Enable notifications'), self) layout.addWidget(self.cbNotify) self.cbNoSnapshotOnBattery \ = QCheckBox(_('Disable snapshots when on battery'), self) if not tools.powerStatusAvailable(): self.cbNoSnapshotOnBattery.setEnabled(False) self.cbNoSnapshotOnBattery.setToolTip( _('Power status not available from system')) layout.addWidget(self.cbNoSnapshotOnBattery) self.cbGlobalFlock = QCheckBox(_('Run only one snapshot at a time')) self.cbGlobalFlock.setToolTip( _('Other snapshots will be blocked until the current snapshot ' 'is done.\n' 'This is a global option. So it will affect all profiles ' 'for this user.\n' 'But you need to activate this for all other users, too.') ) layout.addWidget(self.cbGlobalFlock) self.cbBackupOnRestore = QCheckBox( _('Backup replaced files on restore'), self) self.cbBackupOnRestore.setToolTip( _("Newer versions of files will be renamed with trailing " "{suffix} before restoring.\n" "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.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) self.cbRedirectStdoutInCron.setToolTip( '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) self.cbRedirectStderrInCron.setToolTip( '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) self.cbBwlimit.setToolTip( 'uses \'rsync --bwlimit=RATE\'\n' 'From \'man rsync\':\n' 'This option allows you to specify the maximum transfer rate for\n' 'the data sent over the socket, specified in units per second.\n' 'The RATE value can be suffixed with a string to indicate a size\n' 'multiplier, and may be a fractional value ' '(e.g. "--bwlimit=1.5m").\n' 'If no suffix is specified, the value will be assumed to be in\n' 'units of 1024 bytes (as if "K" or "KiB" had been appended).\n' 'See the --max-size option for a description of ' 'all the available\n' 'suffixes. A value of zero specifies no limit.\n\n' 'For backward-compatibility reasons, the rate limit will be\n' 'rounded to the nearest KiB unit, so no rate smaller than\n' '1024 bytes per second is possible.\n\n' 'Rsync writes data over the socket in blocks, and this option\n' 'both limits the size of the blocks that rsync writes, and tries\n' 'to keep the average transfer rate at the requested limit.\n' 'Some "burstiness" may be seen where rsync writes out a block\n' 'of data and then sleeps to bring the average rate ' 'into compliance.\n\n' 'Due to the internal buffering of data, the --progress option\n' 'may not be an accurate reflection on how fast the data is being\n' 'sent. This is because some files can show up as being rapidly\n' 'sent when the data is quickly buffered, while other can show up\n' 'as very slow when the flushing of the output buffer occurs.\n' 'This may be fixed in a future version.' ) self.cbPreserveAcl = QCheckBox(_('Preserve ACL'), self) self.cbPreserveAcl.setToolTip( 'uses \'rsync -A\'\n' 'From \'man rsync\':\n' 'This option causes rsync to update the destination ACLs to be\n' 'the same as the source ACLs. The option also implies ' '--perms.\n\n' 'The source and destination systems must have compatible ACL\n' 'entries for this option to work properly.\n' 'See the --fake-super option for a way to backup and restore\n' 'ACLs that are not compatible.' ) layout.addWidget(self.cbPreserveAcl) self.cbPreserveXattr = QCheckBox( _('Preserve extended attributes (xattr)'), self) self.cbPreserveXattr.setToolTip( 'uses \'rsync -X\'\n' 'From \'man rsync\':\n' 'This option causes rsync to update the destination extended\n' 'attributes to be the same as the source ones.\n\n' 'For systems that support extended-attribute namespaces, a copy\n' 'being done by a super-user copies all namespaces except\n' 'system.*. A normal user only copies the user.* namespace.\n' 'To be able to backup and restore non-user namespaces as ' 'a normal\n' 'user, see the --fake-super option.\n\n' 'Note that this option does not copy rsyncs special xattr values\n' '(e.g. those used by --fake-super) unless you repeat the option\n' '(e.g. -XX). This "copy all xattrs" mode cannot be used\n' 'with --fake-super.' ) layout.addWidget(self.cbPreserveXattr) self.cbCopyUnsafeLinks = QCheckBox( _('Copy unsafe links (works only with absolute links)'), self) self.cbCopyUnsafeLinks.setToolTip( 'uses \'rsync --copy-unsafe-links\'\n' 'From \'man rsync\':\n' 'This tells rsync to copy the referent of symbolic links that\n' 'point outside the copied tree. Absolute symlinks are also\n' 'treated like ordinary files, and so are any symlinks in the\n' 'source path itself when --relative is used. This option has\n' 'no additional effect if --copy-links was also specified.\n' ) layout.addWidget(self.cbCopyUnsafeLinks) self.cbCopyLinks = QCheckBox( _('Copy links (dereference symbolic links)'), self) self.cbCopyLinks.setToolTip( 'uses \'rsync --copy-links\'\n' 'From \'man rsync\':\n' 'When symlinks are encountered, the item that they point to\n' '(the referent) is copied, rather than the symlink. In older\n' 'versions of rsync, this option also had the side-effect of\n' 'telling the receiving side to follow symlinks, such as\n' 'symlinks to directories. In a modern rsync such as this one,\n' 'you\'ll need to specify --keep-dirlinks (-K) to get this extra\n' 'behavior. The only exception is when sending files to an rsync\n' 'that is too old to understand -K -- in that case, the -L option\n' 'will still have the side-effect of -K on that older ' 'receiving rsync.' ) layout.addWidget(self.cbCopyLinks) # additional rsync options hlayout = QHBoxLayout() layout.addLayout(hlayout) 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) tooltip = _( 'Prefix to run before every command on remote host.\n' 'Variables need to be escaped with \\$FOO.\n' 'This doesn\'t touch rsync. So to add a prefix\n' 'for rsync use "%(cbRsyncOptions)s" with\n' '%(rsync_options_value)s\n\n' '%(default)s: %(def_value)s') % { 'cbRsyncOptions': self.cbRsyncOptions.text(), 'rsync_options_value': '--rsync-path="FOO=bar:\\$FOO /usr/bin/rsync"', 'default': _('default'), 'def_value': self.config.DEFAULT_SSH_PREFIX} self.cbSshPrefix = QCheckBox(_('Add prefix to SSH commands'), self) self.cbSshPrefix.setToolTip(tooltip) hlayout.addWidget(self.cbSshPrefix) self.txtSshPrefix = QLineEdit(self) self.txtSshPrefix.setToolTip(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')) self.cbSshCheckPing.setToolTip( _('Warning: if disabled and the remote host\n' 'is not available, this could lead to some\n' 'weird errors.')) self.cbSshCheckCommands = QCheckBox( _('Check if remote host supports all necessary commands')) self.cbSshCheckCommands.setToolTip( _('Warning: if disabled and the remote host\n' 'does not support all necessary commands,\n' 'this could lead to some weird errors.')) layout.addWidget(self.cbSshCheckPing) layout.addWidget(self.cbSshCheckCommands) # layout.addStretch() scrollArea.setWidget(layoutWidget) scrollArea.setWidgetResizable(True) # buttons buttonBox = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, parent=self) btnRestore = buttonBox.addButton( _('Restore Config'), QDialogButtonBox.ResetRole) btnUserCallback = buttonBox.addButton( _('Edit user-callback'), QDialogButtonBox.ResetRole) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) btnRestore.clicked.connect(self.restoreConfig) btnUserCallback.clicked.connect(self.editUserCallback) self.mainLayout.addWidget(buttonBox) self.updateProfiles() self.comboModesChanged() # enable tabs scroll buttons again but keep dialog size size = self.sizeHint() self.tabs.setUsesScrollButtons(scrollButtonDefault) self.resize(size) self.finished.connect(self.cleanup) def 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 updateProfile(self): if self.config.currentProfile() == '1': self.btnEditProfile.setEnabled(False) self.btnRemoveProfile.setEnabled(False) else: self.btnEditProfile.setEnabled(True) self.btnRemoveProfile.setEnabled(True) self.btnAddProfile.setEnabled(self.config.isConfigured('1')) # TAB: General # mode self.setComboValue(self.comboModes, self.config.snapshotsMode(), t='str') # local self.editSnapshotsPath.setText( self.config.snapshotsPath(mode='local')) # ssh self.txtSshHost.setText(self.config.sshHost()) self.txtSshPort.setText(str(self.config.sshPort())) self.txtSshUser.setText(self.config.sshUser()) self.txtSshPath.setText(self.config.sshSnapshotsPath()) self.setComboValue(self.comboSshCipher, self.config.sshCipher(), t='str') self.txtSshPrivateKeyFile.setText(self.config.sshPrivateKeyFile()) # local_encfs if self.mode == 'local_encfs': self.editSnapshotsPath.setText(self.config.localEncfsPath()) # password password_1 = self.config.password( mode=self.mode, pw_id=1, only_from_keyring=True) password_2 = self.config.password( mode=self.mode, pw_id=2, only_from_keyring=True) if password_1 is None: password_1 = '' if password_2 is None: password_2 = '' self.txtPassword1.setText(password_1) self.txtPassword2.setText(password_2) self.cbPasswordSave.setChecked( self.keyringSupported and self.config.passwordSave(mode=self.mode)) self.cbPasswordUseCache.setChecked( self.config.passwordUseCache(mode=self.mode)) host, user, profile = self.config.hostUserProfile() self.txtHost.setText(host) self.txtUser.setText(user) self.txt_profile.setText(profile) self.setComboValue(self.comboSchedule, self.config.scheduleMode()) self.setComboValue(self.comboScheduleTime, self.config.scheduleTime()) self.setComboValue(self.comboScheduleDay, self.config.scheduleDay()) self.setComboValue(self.comboScheduleWeekday, self.config.scheduleWeekday()) self.txtScheduleCronPatern.setText(self.config.customBackupTime()) self.spbScheduleRepeatedPeriod.setValue( self.config.scheduleRepeatedPeriod()) self.setComboValue(self.comboScheduleRepeatedUnit, self.config.scheduleRepeatedUnit()) self.updateSchedule(self.config.scheduleMode()) # TAB: Include self.listInclude.clear() for include in self.config.include(): self.addInclude(include) includeSortColumn = int(self.config.profileIntValue( 'qt.settingsdialog.include.SortColumn', 1)) includeSortOrder = int(self.config.profileIntValue( 'qt.settingsdialog.include.SortOrder', Qt.AscendingOrder)) self.listInclude.sortItems(includeSortColumn, includeSortOrder) # TAB: Exclude self.listExclude.clear() for exclude in self.config.exclude(): self.addExclude(exclude) self.cbExcludeBySize.setChecked(self.config.excludeBySizeEnabled()) self.spbExcludeBySize.setValue(self.config.excludeBySize()) excludeSortColumn = int(self.config.profileIntValue( 'qt.settingsdialog.exclude.SortColumn', 1)) excludeSortOrder = int(self.config.profileIntValue( 'qt.settingsdialog.exclude.SortOrder', Qt.AscendingOrder)) self.listExclude.sortItems(excludeSortColumn, excludeSortOrder) # TAB: Auto-remove # remove old snapshots enabled, value, unit = self.config.removeOldSnapshots() self.cbRemoveOlder.setChecked(enabled) self.spbRemoveOlder.setValue(value) self.setComboValue(self.comboRemoveOlderUnit, unit) # min free space enabled, value, unit = self.config.minFreeSpace() self.cbFreeSpace.setChecked(enabled) self.spbFreeSpace.setValue(value) self.setComboValue(self.comboFreeSpaceUnit, unit) # min free inodes self.cbFreeInodes.setChecked(self.config.minFreeInodesEnabled()) self.spbFreeInodes.setValue(self.config.minFreeInodes()) # smart remove smart_remove, keep_all, keep_one_per_day, keep_one_per_week, \ keep_one_per_month = self.config.smartRemove() self.cbSmartRemove.setChecked(smart_remove) self.spbKeepAll.setValue(keep_all) self.spbKeepOnePerDay.setValue(keep_one_per_day) self.spbKeepOnePerWeek.setValue(keep_one_per_week) self.spbKeepOnePerMonth.setValue(keep_one_per_month) self.cbSmartRemoveRunRemoteInBackground.setChecked( self.config.smartRemoveRunRemoteInBackground()) # don't remove named snapshots self.cbDontRemoveNamedSnapshots.setChecked( self.config.dontRemoveNamedSnapshots()) # TAB: Options self.cbNotify.setChecked(self.config.notify()) self.cbNoSnapshotOnBattery.setChecked( self.config.noSnapshotOnBattery()) self.cbGlobalFlock.setChecked(self.config.globalFlock()) self.cbBackupOnRestore.setChecked(self.config.backupOnRestore()) self.cbContinueOnErrors.setChecked(self.config.continueOnErrors()) self.cbUseChecksum.setChecked(self.config.useChecksum()) self.cbTakeSnapshotRegardlessOfChanges.setChecked( self.config.takeSnapshotRegardlessOfChanges()) self.setComboValue(self.comboLogLevel, self.config.logLevel()) # TAB: Expert Options self.cbNiceOnCron.setChecked(self.config.niceOnCron()) self.cbIoniceOnCron.setChecked(self.config.ioniceOnCron()) self.cbIoniceOnUser.setChecked(self.config.ioniceOnUser()) self.cbNiceOnRemote.setChecked(self.config.niceOnRemote()) self.cbIoniceOnRemote.setChecked(self.config.ioniceOnRemote()) self.cbNocacheOnLocal.setChecked( self.config.nocacheOnLocal() and self.nocacheAvailable) self.cbNocacheOnRemote.setChecked(self.config.nocacheOnRemote()) self.cbRedirectStdoutInCron.setChecked( self.config.redirectStdoutInCron()) self.cbRedirectStderrInCron.setChecked( self.config.redirectStderrInCron()) self.cbBwlimit.setChecked(self.config.bwlimitEnabled()) self.spbBwlimit.setValue(self.config.bwlimit()) self.cbPreserveAcl.setChecked(self.config.preserveAcl()) self.cbPreserveXattr.setChecked(self.config.preserveXattr()) self.cbCopyUnsafeLinks.setChecked(self.config.copyUnsafeLinks()) self.cbCopyLinks.setChecked(self.config.copyLinks()) self.cbRsyncOptions.setChecked(self.config.rsyncOptionsEnabled()) self.txtRsyncOptions.setText(self.config.rsyncOptions()) self.cbSshPrefix.setChecked(self.config.sshPrefixEnabled()) self.txtSshPrefix.setText(self.config.sshPrefix()) self.cbSshCheckPing.setChecked(self.config.sshCheckPingHost()) self.cbSshCheckCommands.setChecked(self.config.sshCheckCommands()) # update self.updateRemoveOlder() self.updateFreeSpace() def saveProfile(self): item_data = self.comboSchedule.itemData( self.comboSchedule.currentIndex()) if item_data == self.config.CUSTOM_HOUR: if not tools.checkCronPattern(self.txtScheduleCronPatern.text()): self.errorHandler( _('Custom hours can only be a comma separated list of ' 'hours (e.g. 8,12,18,23) or */3 for periodic ' 'backups every 3 hours.') ) return False # mode mode = str(self.comboModes.itemData(self.comboModes.currentIndex())) self.config.setSnapshotsMode(mode) mount_kwargs = {} # password password_1 = self.txtPassword1.text() password_2 = self.txtPassword2.text() if mode in ('ssh', 'local_encfs'): mount_kwargs = {'password': password_1} if mode == 'ssh_encfs': mount_kwargs = {'ssh_password': password_1, 'encfs_password': password_2} # snapshots path self.config.setHostUserProfile( self.txtHost.text(), self.txtUser.text(), self.txt_profile.text() ) # save ssh self.config.setSshHost(self.txtSshHost.text()) self.config.setSshPort(self.txtSshPort.text()) self.config.setSshUser(self.txtSshUser.text()) self.config.setSshSnapshotsPath(self.txtSshPath.text()) self.config.setSshCipher( self.comboSshCipher.itemData(self.comboSshCipher.currentIndex())) if mode in ('ssh', 'ssh_encfs'): if not self.txtSshPrivateKeyFile.text(): question = _('You did not choose a private key file for ' 'SSH.\nWould 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.AscendingOrder) include_list = [] for index in range(self.listInclude.topLevelItemCount()): item = self.listInclude.topLevelItem(index) include_list.append((item.text(0), item.data(0, Qt.UserRole))) self.config.setInclude(include_list) # exclude patterns self.config.setProfileIntValue( 'qt.settingsdialog.exclude.SortColumn', self.listExclude.header().sortIndicatorSection()) self.config.setProfileIntValue( 'qt.settingsdialog.exclude.SortOrder', self.listExclude.header().sortIndicatorOrder()) self.listExclude.sortItems(1, Qt.AscendingOrder) exclude_list = [] for index in range(self.listExclude.topLevelItemCount()): item = self.listExclude.topLevelItem(index) exclude_list.append(item.text(0)) self.config.setExclude(exclude_list) self.config.setExcludeBySize(self.cbExcludeBySize.isChecked(), self.spbExcludeBySize.value()) # schedule self.config.setScheduleMode( self.comboSchedule.itemData(self.comboSchedule.currentIndex())) self.config.setScheduleTime( self.comboScheduleTime.itemData( self.comboScheduleTime.currentIndex())) self.config.setScheduleWeekday( self.comboScheduleWeekday.itemData( self.comboScheduleWeekday.currentIndex())) self.config.setScheduleDay( self.comboScheduleDay.itemData( self.comboScheduleDay.currentIndex())) self.config.setCustomBackupTime(self.txtScheduleCronPatern.text()) self.config.setScheduleRepeatedPeriod( self.spbScheduleRepeatedPeriod.value()) self.config.setScheduleRepeatedUnit( self.comboScheduleRepeatedUnit.itemData( self.comboScheduleRepeatedUnit.currentIndex())) # auto-remove self.config.setRemoveOldSnapshots( self.cbRemoveOlder.isChecked(), self.spbRemoveOlder.value(), self.comboRemoveOlderUnit.itemData( self.comboRemoveOlderUnit.currentIndex())) self.config.setMinFreeSpace( self.cbFreeSpace.isChecked(), self.spbFreeSpace.value(), self.comboFreeSpaceUnit.itemData( self.comboFreeSpaceUnit.currentIndex())) self.config.setMinFreeInodes( self.cbFreeInodes.isChecked(), self.spbFreeInodes.value()) self.config.setDontRemoveNamedSnapshots( self.cbDontRemoveNamedSnapshots.isChecked()) self.config.setSmartRemove( self.cbSmartRemove.isChecked(), self.spbKeepAll.value(), self.spbKeepOnePerDay.value(), self.spbKeepOnePerWeek.value(), self.spbKeepOnePerMonth.value()) self.config.setSmartRemoveRunRemoteInBackground( self.cbSmartRemoveRunRemoteInBackground.isChecked()) # options self.config.setNotify(self.cbNotify.isChecked()) self.config.setNoSnapshotOnBattery( self.cbNoSnapshotOnBattery.isChecked()) self.config.setGlobalFlock(self.cbGlobalFlock.isChecked()) self.config.setBackupOnRestore(self.cbBackupOnRestore.isChecked()) self.config.setContinueOnErrors(self.cbContinueOnErrors.isChecked()) self.config.setUseChecksum(self.cbUseChecksum.isChecked()) self.config.setTakeSnapshotRegardlessOfChanges( self.cbTakeSnapshotRegardlessOfChanges.isChecked()) self.config.setLogLevel( self.comboLogLevel.itemData(self.comboLogLevel.currentIndex())) # expert options self.config.setNiceOnCron(self.cbNiceOnCron.isChecked()) self.config.setIoniceOnCron(self.cbIoniceOnCron.isChecked()) self.config.setIoniceOnUser(self.cbIoniceOnUser.isChecked()) self.config.setNiceOnRemote(self.cbNiceOnRemote.isChecked()) self.config.setIoniceOnRemote(self.cbIoniceOnRemote.isChecked()) self.config.setNocacheOnLocal(self.cbNocacheOnLocal.isChecked()) self.config.setNocacheOnRemote(self.cbNocacheOnRemote.isChecked()) self.config.setRedirectStdoutInCron( self.cbRedirectStdoutInCron.isChecked()) self.config.setRedirectStderrInCron( self.cbRedirectStderrInCron.isChecked()) self.config.setBwlimit(self.cbBwlimit.isChecked(), self.spbBwlimit.value()) self.config.setPreserveAcl(self.cbPreserveAcl.isChecked()) self.config.setPreserveXattr(self.cbPreserveXattr.isChecked()) self.config.setCopyUnsafeLinks(self.cbCopyUnsafeLinks.isChecked()) self.config.setCopyLinks(self.cbCopyLinks.isChecked()) self.config.setRsyncOptions(self.cbRsyncOptions.isChecked(), self.txtRsyncOptions.text()) self.config.setSshPrefix(self.cbSshPrefix.isChecked(), self.txtSshPrefix.text()) self.config.setSshCheckPingHost(self.cbSshCheckPing.isChecked()) self.config.setSshCheckCommands(self.cbSshCheckCommands.isChecked()) # TODO - consider a single API method to bridge the UI layer # (settings dialog) and backend layer (config) # when setting snapshots path rather than having to call the # mount module from the UI layer # # currently, setting snapshots path requires the path to be mounted. # it seems that it might be nice, # since the config object is more than a data structure, but has # side-effect logic as well, to have the # config.setSnapshotsPath() method take care of everything it needs # to perform its job # (mounting and unmounting the fuse filesystem if necessary). # https://en.wikipedia.org/wiki/Single_responsibility_principle if not self.config.SNAPSHOT_MODES[mode][0] is None: # preMountCheck mnt = mount.Mount(cfg=self.config, tmp_mount=True, parent=self) try: mnt.preMountCheck(mode=mode, first_run=True, **mount_kwargs) except NoPubKeyLogin as ex: logger.error(str(ex), self) question = _( 'Would you like to copy your public SSH key to the\n' '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()), 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 = _("The authenticity of host {host} can't be " "established.\n\n{keytype} key fingerprint is:") \ .format(host='"{}"'.format(self.config.sshHost()), keytype=keyType) options = [] lblFingerprint = QLabel(fingerprint + '\n') lblFingerprint.setWordWrap(False) lblFingerprint.setFont(QFont('Monospace')) options.append({'widget': lblFingerprint, 'retFunc': None}) lblQuestion = QLabel( _("Please verify this fingerprint! Would you like to " "add it to your 'known_hosts' file?") ) options.append({'widget': lblQuestion, 'retFunc': None}) if messagebox.warningYesNoOptions(self, msg, options)[0]: sshtools.writeKnownHostsFile(hashedKey) return self.saveProfile() else: return False except MountException as ex: self.errorHandler(str(ex)) return False # okay, lets try to mount try: hash_id = mnt.mount(mode=mode, check=False, **mount_kwargs) except MountException as ex: self.errorHandler(str(ex)) return False # save password self.config.setPasswordSave(self.cbPasswordSave.isChecked(), mode=mode) self.config.setPasswordUseCache(self.cbPasswordUseCache.isChecked(), mode=mode) self.config.setPassword(password_1, mode=mode) self.config.setPassword(password_2, mode=mode, pw_id=2) # save snaphots_path if self.config.SNAPSHOT_MODES[mode][0] is None: snapshots_path = self.editSnapshotsPath.text() else: snapshots_path = self.config.snapshotsPath(mode=mode, tmp_mount=True) ret = self.config.setSnapshotsPath(snapshots_path, mode=mode) if not ret: return ret # umount if not self.config.SNAPSHOT_MODES[mode][0] is None: try: mnt.umount(hash_id=hash_id) except MountException as ex: self.errorHandler(str(ex)) return False return True def errorHandler(self, message): messagebox.critical(self, message) def questionHandler(self, message): return QMessageBox.Yes == messagebox.warningYesNo(self, message) def updateRemoveOlder(self): enabled = self.cbRemoveOlder.isChecked() self.spbRemoveOlder.setEnabled(enabled) self.comboRemoveOlderUnit.setEnabled(enabled) def updateFreeSpace(self): enabled = self.cbFreeSpace.isChecked() self.spbFreeSpace.setEnabled(enabled) self.comboFreeSpaceUnit.setEnabled(enabled) def addInclude(self, data): item = QTreeWidgetItem() if data[1] == 0: item.setIcon(0, self.icon.FOLDER) else: item.setIcon(0, self.icon.FILE) item.setText(0, data[0]) item.setData(0, Qt.UserRole, data[1]) self.listIncludeCount += 1 item.setText(1, str(self.listIncludeCount).zfill(6)) item.setData(1, Qt.UserRole, self.listIncludeCount) self.listInclude.addTopLevelItem(item) if self.listInclude.currentItem() is None: self.listInclude.setCurrentItem(item) return item def addExclude(self, pattern): item = QTreeWidgetItem() item.setText(0, pattern) item.setData(0, Qt.UserRole, pattern) self.listExcludeCount += 1 item.setText(1, str(self.listExcludeCount).zfill(6)) item.setData(1, Qt.UserRole, self.listExcludeCount) self.formatExcludeItem(item) self.listExclude.addTopLevelItem(item) if self.listExclude.currentItem() is None: self.listExclude.setCurrentItem(item) return item def fillCombo(self, combo, d): keys = list(d.keys()) keys.sort() for key in keys: combo.addItem(QIcon(), d[key], key) def setComboValue(self, combo, value, t='int'): for i in range(combo.count()): if t == 'int' and value == combo.itemData(i): combo.setCurrentIndex(i) break if t == 'str' and value == combo.itemData(i): combo.setCurrentIndex(i) break def validate(self): if not self.saveProfile(): return False if not self.config.checkConfig(): return False if not self.config.setupCron(): return False return self.config.save() def btnExcludeRemoveClicked(self): for item in self.listExclude.selectedItems(): index = self.listExclude.indexOfTopLevelItem(item) if index < 0: continue self.listExclude.takeTopLevelItem(index) if self.listExclude.topLevelItemCount() > 0: self.listExclude.setCurrentItem(self.listExclude.topLevelItem(0)) def addExclude_(self, pattern): if not pattern: return for index in range(self.listExclude.topLevelItemCount()): item = self.listExclude.topLevelItem(index) if pattern == item.text(0): return self.addExclude(pattern) def btnExcludeAddClicked(self): dlg = QInputDialog(self) dlg.setInputMode(QInputDialog.TextInput) dlg.setWindowTitle(_('Exclude pattern')) dlg.setLabelText('') dlg.resize(400, 0) if not dlg.exec(): return pattern = dlg.textValue().strip() if not pattern: return self.addExclude_(pattern) def btnExcludeFileClicked(self): for path in qttools.getOpenFileNames(self, _('Exclude file')): self.addExclude_(path) def btnExcludeFolderClicked(self): for path in qttools.getExistingDirectories(self, _('Exclude folder')): self.addExclude_(path) def btnExcludeDefaultClicked(self): for path in self.config.DEFAULT_EXCLUDE: self.addExclude_(path) def btnIncludeRemoveClicked(self): for item in self.listInclude.selectedItems(): index = self.listInclude.indexOfTopLevelItem(item) if index < 0: continue self.listInclude.takeTopLevelItem(index) if self.listInclude.topLevelItemCount() > 0: self.listInclude.setCurrentItem(self.listInclude.topLevelItem(0)) def btnIncludeFileClicked(self): """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): if not params: index = self.comboModes.currentIndex() else: index = params[0] active_mode = str(self.comboModes.itemData(index)) if active_mode != self.mode: for mode in list(self.config.SNAPSHOT_MODES.keys()): getattr(self, 'mode%s' % tools.camelCase(mode)).hide() for mode in list(self.config.SNAPSHOT_MODES.keys()): if active_mode == mode: getattr(self, 'mode%s' % tools.camelCase(mode)).show() self.mode = active_mode if self.config.modeNeedPassword(active_mode): self.lblPassword1.setText( self.config.SNAPSHOT_MODES[active_mode][2] + ':') self.groupPassword1.show() if self.config.modeNeedPassword(active_mode, 2): self.lblPassword2.setText( self.config.SNAPSHOT_MODES[active_mode][3] + ':') self.lblPassword2.show() self.txtPassword2.show() qttools.equalIndent(self.lblPassword1, self.lblPassword2) else: self.lblPassword2.hide() self.txtPassword2.hide() qttools.equalIndent(self.lblPassword1) else: self.groupPassword1.hide() if active_mode == 'ssh_encfs': self.lblSshEncfsExcludeWarning.show() else: self.lblSshEncfsExcludeWarning.hide() self.updateExcludeItems() enabled = active_mode in ('ssh', 'ssh_encfs') self.cbNiceOnRemote.setEnabled(enabled) self.cbIoniceOnRemote.setEnabled(enabled) self.cbNocacheOnRemote.setEnabled(enabled) self.cbSmartRemoveRunRemoteInBackground.setHidden(not enabled) self.cbSshPrefix.setHidden(not enabled) self.txtSshPrefix.setHidden(not enabled) self.cbSshCheckPing.setHidden(not enabled) self.cbSshCheckCommands.setHidden(not enabled) self.encfsWarning.setHidden( active_mode not in ('local_encfs', 'ssh_encfs')) def fullPathChanged(self, dummy): if self.mode in ('ssh', 'ssh_encfs'): path = self.txtSshPath.text() else: path = self.editSnapshotsPath.text() self.lblFullPath.setText( _('Full snapshot path: ') + os.path.join( path, 'backintime', self.txtHost.text(), self.txtUser.text(), self.txt_profile.text() )) def updateExcludeItems(self): for index in range(self.listExclude.topLevelItemCount()): item = self.listExclude.topLevelItem(index) self.formatExcludeItem(item) def formatExcludeItem(self, item): no_encr_wildcard = tools.patternHasNotEncryptableWildcard(item.text(0)) if self.mode == 'ssh_encfs' and no_encr_wildcard: item.setIcon(0, self.icon.INVALID_EXCLUDE) item.setBackground(0, QPalette().brush(QPalette.Active, QPalette.Link)) elif item.text(0) in self.config.DEFAULT_EXCLUDE: item.setIcon(0, self.icon.DEFAULT_EXCLUDE) item.setBackground(0, QBrush()) else: item.setIcon(0, self.icon.EXCLUDE) item.setBackground(0, QBrush()) def customSortOrder(self, header, loop, newColumn, newOrder): if newColumn == 0 and newOrder == Qt.AscendingOrder: if loop: newColumn, newOrder = 1, Qt.AscendingOrder header.setSortIndicator(newColumn, newOrder) loop = False else: loop = True header.model().sort(newColumn, newOrder) return loop def includeCustomSortOrder(self, *args): self.listIncludeSortLoop = self.customSortOrder( self.listInclude.header(), self.listIncludeSortLoop, *args) def excludeCustomSortOrder(self, *args): self.listExcludeSortLoop = self.customSortOrder( self.listExclude.header(), self.listExcludeSortLoop, *args) def printDefault(self, value): if value: value_ = _('enabled') else: value_ = _('disabled') return ' (%s: %s)' % (_('default'), value_) def restoreConfig(self, *args): RestoreConfigDialog(self).exec_() self.updateProfiles() def editUserCallback(self, *args): EditUserCallback(self).exec_() def accept(self): if self.validate(): super(SettingsDialog, self).accept() def cleanup(self, result): self.config.clearHandlers() if not result: self.config.dict = self.configDictCopy self.config.setCurrentProfile(self.originalCurrentProfile) if result: self.parent.remount(self.originalCurrentProfile, self.originalCurrentProfile) self.parent.updateProfiles() class RestoreConfigDialog(QDialog): """ Show a dialog that will help to restore BITs configuration. User can select a config from previous snapshots. """ def __init__(self, parent): super(RestoreConfigDialog, self).__init__(parent) self.parent = parent self.config = parent.config self.snapshots = parent.snapshots import icon self.icon = icon self.setWindowIcon(icon.SETTINGS_DIALOG) self.setWindowTitle(_('Restore Settings')) layout = QVBoxLayout(self) # show a hint on how the snapshot path will look like. samplePath = os.path.join( 'backintime', self.config.host(), self.config.user(), '1', snapshots.SID(datetime.datetime.now(), self.config).sid ) label = QLabel(_( "Please navigate to the snapshot from which you want to restore " "{appName}'s configuration. The path may look like:\n" "{samplePath}\n\nIf your snapshots are on a remote drive or if " "they are encrypted you need to manually mount them first. " "If you use Mode SSH you also may need to set up public key " "login to the remote host.\n" "Take a look at 'man backintime'.") .format( appName=self.config.APP_NAME, samplePath=samplePath), self ) label.setWordWrap(True) layout.addWidget(label) # treeView self.treeView = qttools.MyTreeView(self) self.treeViewModel = QFileSystemModel(self) self.treeViewModel.setRootPath(QDir().rootPath()) self.treeViewModel.setReadOnly(True) self.treeViewModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot | QDir.Hidden) self.treeViewFilterProxy = QSortFilterProxyModel(self) self.treeViewFilterProxy.setDynamicSortFilter(True) self.treeViewFilterProxy.setSourceModel(self.treeViewModel) self.treeViewFilterProxy.setFilterRegExp(r'^[^\.]') self.treeView.setModel(self.treeViewFilterProxy) for col in range(self.treeView.header().count()): self.treeView.setColumnHidden(col, col != 0) self.treeView.header().hide() # expand users home self.expandAll(os.path.expanduser('~')) layout.addWidget(self.treeView) # context menu self.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.treeView.customContextMenuRequested.connect(self.onContextMenu) self.contextMenu = QMenu(self) self.btnShowHidden = self.contextMenu.addAction( icon.SHOW_HIDDEN, _('Show hidden files')) self.btnShowHidden.setCheckable(True) self.btnShowHidden.toggled.connect(self.onBtnShowHidden) # colors self.colorRed = QPalette() self.colorRed.setColor(QPalette.WindowText, QColor(205, 0, 0)) self.colorGreen = QPalette() self.colorGreen.setColor(QPalette.WindowText, QColor(0, 160, 0)) # wait indicator which will show that the scan for # snapshots is still running self.wait = QProgressBar(self) self.wait.setMinimum(0) self.wait.setMaximum(0) self.wait.setMaximumHeight(7) layout.addWidget(self.wait) # show where a snapshot with config was found self.lblFound = QLabel(_('No config found'), self) self.lblFound.setWordWrap(True) self.lblFound.setPalette(self.colorRed) layout.addWidget(self.lblFound) # show profiles inside the config self.widgetProfiles = QWidget(self) self.widgetProfiles.setContentsMargins(0, 0, 0, 0) self.widgetProfiles.hide() self.gridProfiles = QGridLayout() self.gridProfiles.setContentsMargins(0, 0, 0, 0) self.gridProfiles.setHorizontalSpacing(20) self.widgetProfiles.setLayout(self.gridProfiles) layout.addWidget(self.widgetProfiles) self.restoreConfig = None self.scan = ScanFileSystem(self) self.treeView.myCurrentIndexChanged.connect(self.indexChanged) self.scan.foundConfig.connect(self.scanFound) self.scan.finished.connect(self.scanFinished) buttonBox = QDialogButtonBox(self) self.restoreButton = buttonBox.addButton( _('Restore'), QDialogButtonBox.AcceptRole) self.restoreButton.setEnabled(False) buttonBox.addButton(QDialogButtonBox.Cancel) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) layout.addWidget(buttonBox) self.scan.start() self.resize(600, 700) def pathFromIndex(self, index): """ return a path string for a given treeView index """ sourceIndex = self.treeViewFilterProxy.mapToSource(index) return str(self.treeViewModel.filePath(sourceIndex)) def indexFromPath(self, path): """ return the index for path which can be used in treeView """ indexSource = self.treeViewModel.index(path) return self.treeViewFilterProxy.mapFromSource(indexSource) def indexChanged(self, current, previous): """ called 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(), self.config.user()) tryPaths = ['', '..', 'last_snapshot'] tryPaths.extend([ os.path.join(snapshotPath, str(i), 'last_snapshot') for i in range(10)]) for p in tryPaths: cfgPath = os.path.join(path, p, 'config') if os.path.exists(cfgPath): try: cfg = config.Config(cfgPath) if cfg.isConfigured(): return cfg except: pass return def expandAll(self, path): """ expand all folders from filesystem root to given path """ paths = [path, ] while len(path) > 1: path = os.path.dirname(path) paths.append(path) paths.append('/') paths.reverse() [self.treeView.expand(self.indexFromPath(p)) for p in paths] def showProfile(self, cfg): """ show information about the profiles inside cfg """ child = self.gridProfiles.takeAt(0) while child: child.widget().deleteLater() child = self.gridProfiles.takeAt(0) for row, profileId in enumerate(cfg.profiles()): for col, txt in enumerate(( _('Profile') + ': ' + str(profileId), cfg.profileName(profileId), _('Mode') + ': ' + cfg.SNAPSHOT_MODES[ cfg.snapshotsMode(profileId)][1] )): self.gridProfiles.addWidget(QLabel(txt, self), row, col) self.gridProfiles.setColumnStretch(col, 1) self.widgetProfiles.show() def scanFound(self, path): """ scan hit a config. Expand the snapshot folder. """ self.expandAll(os.path.dirname(path)) def scanFinished(self): """ scan is done. Delete the wait indicator """ self.wait.deleteLater() def onContextMenu(self, point): self.contextMenu.exec_(self.treeView.mapToGlobal(point)) def onBtnShowHidden(self, checked): if checked: self.treeViewFilterProxy.setFilterRegExp(r'') else: self.treeViewFilterProxy.setFilterRegExp(r'^[^\.]') def accept(self): """ handle over the dict from the selected config. The dict contains all settings from the config. """ if self.restoreConfig: self.config.dict = self.restoreConfig.dict super(RestoreConfigDialog, self).accept() def exec_(self): """ stop the scan thread if it is still running after dialog was closed. """ ret = super(RestoreConfigDialog, self).exec_() self.scan.stop() return ret class ScanFileSystem(QThread): CONFIG = 'config' BACKUP = 'backup' BACKINTIME = 'backintime' foundConfig = pyqtSignal(str) def __init__(self, parent): super(ScanFileSystem, self).__init__(parent) self.stopper = False def stop(self): """ 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.Ok | QDialogButtonBox.Cancel, parent=self) btnBox.accepted.connect(self.accept) btnBox.rejected.connect(self.reject) layout.addWidget(btnBox) def checkScript(self, script): m = re.match(r'^#!(/[\w/-]+)\n', script) if not m: logger.error( 'user-callback script has no shebang (#!/bin/sh) line.') self.config.errorHandler( _('user-callback script has no shebang (#!/bin/sh) line.')) return False if not tools.checkCommand(m.group(1)): logger.error('Shebang in user-callback script is not executable.') self.config.errorHandler( _('Shebang in user-callback script is not executable.')) return False return True def accept(self): if not self.checkScript(self.edit.toPlainText()): return with open(self.script, 'wt') as f: f.write(self.edit.toPlainText()) os.chmod(self.script, 0o755) super(EditUserCallback, self).accept() def debugTrace(): """ Set a tracepoint in the Python debugger that works with Qt """ from pdb import set_trace pyqtRemoveInputHook() set_trace() backintime-1.4.3/qt/snapshotsdialog.py000066400000000000000000000367231455673541400200560ustar00rootroot00000000000000# 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 PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * import tools import restoredialog import messagebox import qttools import snapshots if tools.checkCommand('meld'): DIFF_CMD = 'meld' DIFF_PARAMS = '%1 %2' elif tools.checkCommand('kompare'): DIFF_CMD = 'kompare' DIFF_PARAMS = '%1 %2' else: DIFF_CMD = 'false' DIFF_PARAMS = '%1 %2' 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) self.diffCmd = self.config.strValue('qt.diff.cmd', DIFF_CMD) self.diffParams = self.config.strValue('qt.diff.params', DIFF_PARAMS) self.mainLayout.addWidget(QLabel(_('Command') + ':'), 0, 0) self.editCmd = QLineEdit(self.diffCmd, self) self.mainLayout.addWidget(self.editCmd, 0, 1) self.mainLayout.addWidget(QLabel(_('Parameters') + ':'), 1, 0) self.editParams = QLineEdit(self.diffParams, self) self.mainLayout.addWidget(self.editParams, 1, 1) self.mainLayout.addWidget(QLabel(_('Use %1 and %2 for path parameters')), 2, 1) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) self.mainLayout.addWidget(buttonBox, 3, 0, 3, 2) def accept(self): diffCmd = self.editCmd.text() diffParams = self.editParams.text() if diffCmd != self.diffCmd or diffParams != self.diffParams: self.config.setStrValue('qt.diff.cmd', diffCmd) self.config.setStrValue('qt.diff.params', diffParams) self.config.save() 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 equal snapshots 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.comboDiff = qttools.SnapshotCombo(self) layout.addWidget(self.comboDiff, 2) #buttons buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.btnGoto = buttonBox.button(QDialogButtonBox.Ok) self.btnCancel = buttonBox.button(QDialogButtonBox.Cancel) self.btnGoto.setText(_('Go To')) btnDiffOptions = buttonBox.addButton(_('Options'), QDialogButtonBox.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) if not tools.checkCommand(diffCmd): messagebox.critical( self, '{}: {}'.format(_('Command not found'), diffCmd) ) return # prevent backup data from being accidentally overwritten # by create a temporary local copy and only open that one if not isinstance(sid1, snapshots.RootSnapshot): path1 = self.parent.tmpCopy(path1, sid1) 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 subprocess.Popen(shlex.split(cmd)) def btnDiffOptionsClicked(self): DiffOptionsDialog(self).exec_() 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 = '{}\n{}: {}'.format( msg, _('WARNING'), _('This cannot be revoked!')) if QMessageBox.Yes == messagebox.warningYesNo(self, msg): for item in items: item.setFlags(Qt.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 and QMessageBox.Yes == messagebox.warningYesNo(self, msg): exclude.append(self.path) self.config.setExclude(exclude) def btnSelectAllClicked(self): """ select all expect 'Now' """ self.timeLine.clearSelection() for item in self.timeLine.iterSnapshotItems(): if not isinstance(item.snapshotID(), snapshots.RootSnapshot): item.setSelected(True) 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.4.3/qt/test/000077500000000000000000000000001455673541400152465ustar00rootroot00000000000000backintime-1.4.3/qt/test/test_lint.py000066400000000000000000000037551455673541400176370ustar00rootroot00000000000000import unittest import pathlib import subprocess from importlib import metadata from typing import Iterable # PACKAGE_NAME = 'buhtzology' 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. """ p = pathlib.Path.cwd() # Make sure we are inside the test folder if p.name in ['qt', 'common']: # happens e.g. on TravisCI p = p / 'test' if not p.name.startswith('test'): raise Exception('Something went wrong. The test should run inside' f' the test folder but current folder is {p}.') # Workaround p = p.parent # Find recursive all py-files. return p.rglob('**/*.py') 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', # prevent false-positive no-module-member errors '--extension-pkg-whitelist=PyQt5', # Because of globally installed GNU gettext functions '--additional-builtins=_,ngettext', # Deactivate all checks by default '--disable=all' ] # Explicit activate checks err_codes = [ 'E1101', # no-member 'W1401', # anomalous-backslash-in-string (invalid escape sequence) ] cmd.append('--enable=' + ','.join(err_codes)) for fp in self._collect_py_files(): subprocess.run(cmd + [fp], check=True) backintime-1.4.3/update_language_files.py000077500000000000000000000307741455673541400205420ustar00rootroot00000000000000#!/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 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' 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(): """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. """ # 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) 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 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 LOCAL_DIR.rglob('**/*.po'): 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 not 'en' 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 check_shortcuts(): """Keyboard shortcuts are indicated via the & in front of a character in an GUI string (e.g. a button or tab). As an example '&Exclude' and '&Export' do not work because both of them indicate the 'E' as a shortcut. These situation can happen in translated strings and is not easy to review or control. This function tries to find such redundancies in the po-files. Review the output with care because it there is a high risk of false positive warnings. """ # RegEx pattern: & followed by a word character (as group) rex = re.compile(r'&(\w)') # each po file in the repository for po_path in list(LOCAL_DIR.rglob('**/*.po')): print(f'\nProcessing {po_path}...') # Remember shortcut relevant entries. msgs = {} # All characters used as shortcuts. 'T' is used in "Back In &Time" # which is an untranslated string. shortcuts = 'T' # each entry in po-file for entry in polib.pofile(po_path): # Ignore untranslated or obsolete strings if not entry.msgstr or entry.obsolete: continue # Source string contain "&" if rex.search(entry.msgid): # Collect the source string and its translation msgs[entry.msgid] = entry.msgstr # Get shortcut character from translated string try: shortcuts = shortcuts + rex.search(entry.msgstr).groups()[0] except AttributeError: print('ATTENTION: Maybe missing shortcut in translated ' f'string.\nmsgid={entry.msgid}\n' f'msgstr={entry.msgstr}') # redundant shortcuts? if len(shortcuts) > len(set(shortcuts)): print(f'ATTENTION: Maybe redundant shortcuts in "{po_path}". ' 'Please take a look.') for key, msgs in msgs.items(): print(f'{key}: {msgs}') 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() 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() create_languages_file() print(FIN_MSG) sys.exit() # Check for redundant &-shortcuts if 'shortcuts' in sys.argv: check_shortcuts() 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)\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 "&"') sys.exit(1) backintime-1.4.3/updatecopyright.sh000077500000000000000000000005301455673541400174130ustar00rootroot00000000000000#!/bin/sh find ./ -type f \ ! -wholename "./common/po/*" \ ! -wholename "./.git/*" \ -exec sed \ -e "/Germar Reitze/s/[cC]opyright ([cC]) \([0-9]*\)-\([0-9]*\) /Copyright (C) \1-$(date +%Y) /g" \ -e "/Germar Reitze/s/[cC]opyright ([cC]) \([0-9]*\) /Copyright (C) \1-$(date +%Y) /g" \ -i {} + backintime-1.4.3/updateversion.sh000077500000000000000000000064621455673541400171020ustar00rootroot00000000000000#!/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` echo VERSION: $VERSION MAINTAINER="Germar Reitze " # MAINTAINER="BIT Team " # MAINTAINER="BIT Team " update_sphinx_config () { echo "Update '$1'" sed -e "s/^\(\s*\)version = '.*'$/\1version = '$VERSION'/" \ -i $1 } update_config () { echo "Update '$1'" sed -e "s/^\(\s*\)VERSION = '.*'$/\1VERSION = '$VERSION'/" \ -i $1 } update_man_page () { echo "Update '$1'" sed -e "s/\.TH\(.*\)\"version\([^\"]*\)\"\(.*\)$/.TH\1\"version $VERSION\"\3/" \ -i $1 } update_omf () { echo "Update '$1'" sed -e "s/^\([ \]*\) $1 # The following awk code extracts the changelog # starting from the "Version" headline of $VERSION # until the next "Version" headline # and create a new file with all the lines in between. cat CHANGES | awk 'BEGIN {ins=0} /^Version '$VERSION'/ && (ins == 0) {ins=1; next} /^Version [0-9.]+/ && (ins == 1) {exit 0} (ins == 1) {print " "$0}' >> $1 if [ $(cat $1 | wc -l) -eq 1 ]; then echo " * TODO prepare next version" >> $1 fi echo " -- ${MAINTAINER} $(date -R)" >> $1 } update_config common/config.py update_sphinx_config common/doc-dev/conf.py update_man_page common/man/C/backintime.1 update_man_page common/man/C/backintime-config.1 update_man_page common/man/C/backintime-askpass.1 update_man_page qt/man/C/backintime-qt.1 update_changelog debian/changelog