diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a4670277a..93066b9d9 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -16,6 +16,10 @@ - Suggest your change by [submitting a new issue](https://github.com/cryptomator/cryptomator/issues/new/choose) and start writing code. +## Do you intend to add a new translation or change an existing one? + +Translations are not managed directly in this repository. Instead, we use [Crowdin](https://translate.cryptomator.org/), which automatically synchronizes translations with this repository. If you want to help us with translations, please visit our translation project on Crowdin. + ## Code of Conduct Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md). diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index a82e89ba7..abb1b4a92 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,7 +1,14 @@ name: Bug Report description: Create a report to help us improve -labels: ["type:bug"] +type: "Bug" body: + - type: input + id: summary + attributes: + label: Summary + placeholder: Please summarize your problem. + validations: + required: true - type: checkboxes id: terms attributes: @@ -11,13 +18,6 @@ body: required: true - label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md) required: true - - type: input - id: summary - attributes: - label: Summary - placeholder: Please summarize your problem. - validations: - required: true - type: textarea id: software-versions attributes: @@ -97,4 +97,4 @@ body: id: further-info attributes: label: Anything else? - description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue? + description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue? \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 652f27234..826f3410a 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -1,7 +1,14 @@ name: Feature Request description: Suggest an idea for this project -labels: ["type:feature-request"] +type: "Feature" body: + - type: input + id: summary + attributes: + label: Summary + placeholder: Please summarize your feature request. + validations: + required: true - type: checkboxes id: terms attributes: @@ -11,13 +18,6 @@ body: required: true - label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md) required: true - - type: input - id: summary - attributes: - label: Summary - placeholder: Please summarize your feature request. - validations: - required: true - type: textarea id: motivation attributes: diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index b4e73e306..c80be8844 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -11,7 +11,7 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: '23.0.1+11' + JAVA_VERSION: '23.0.2' jobs: get-version: @@ -29,12 +29,12 @@ jobs: include: - os: ubuntu-latest appimage-suffix: x86_64 - openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-x64_bin-jmods.zip' - openjfx-sha: '2164bca470bf70a5e2764645e2078ba7f787b274e5be3d7df30d87c5bb62bba6' + openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.2/openjfx-23.0.2_linux-x64_bin-jmods.zip' + openjfx-sha: '063baebc6922e4a89c94b9dfb7a4f53e59e8d6fec400d4e670b31bc2ab324dec' - os: ubuntu-24.04-arm appimage-suffix: aarch64 - openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-aarch64_bin-jmods.zip' - openjfx-sha: '09c92fa9fa0b82adefd88640a14ebb2a49e5f3f733a57d1542f5590d060ffe1b' + openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.2/openjfx-23.0.2_linux-aarch64_bin-jmods.zip' + openjfx-sha: '9bbedaeae1590b69e2b22237bda310936df33e344dbc243bea2e86acaab3a0d8' steps: - uses: actions/checkout@v4 - name: Setup Java diff --git a/.github/workflows/av-whitelist.yml b/.github/workflows/av-whitelist.yml index aeece4e83..3cc164b30 100644 --- a/.github/workflows/av-whitelist.yml +++ b/.github/workflows/av-whitelist.yml @@ -13,15 +13,48 @@ on: description: "Url to the file to upload" required: true type: string + avast: + description: "Upload to Avast" + required: false + type: boolean + default: false + kaspersky: + description: "Upload to Kaspersky" + required: false + type: boolean + default: false jobs: - allowlist: - name: Anti Virus Allowlisting + download-file: + name: Downloads the file into the VM runs-on: ubuntu-latest + outputs: + fileName: ${{ steps.extractName.outputs.fileName}} steps: - - name: Download file + - name: Extract file name + id: extractName run: | - curl --remote-name ${{ inputs.url }} -L + url="${{ inputs.url }}" + echo "fileName=${url##*/}" >> $GITHUB_OUTPUT + - name: Download file + run: curl --remote-name ${{ inputs.url }} -L -o ${{steps.extractName.outputs.fileName}} + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.extractName.outputs.fileName }} + path: ${{ steps.extractName.outputs.fileName }} + if-no-files-found: error + allowlist-kaspersky: + name: Anti Virus Allowlisting Kaspersky + runs-on: ubuntu-latest + needs: download-file + if: github.event_name == 'workflow_call' || inputs.kaspersky + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ needs.download-file.outputs.fileName }} + path: upload - name: Upload to Kaspersky uses: SamKirkland/FTP-Deploy-Action@v4.3.5 with: @@ -30,11 +63,26 @@ jobs: port: 990 username: ${{ secrets.ALLOWLIST_KASPERSKY_USERNAME }} password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }} - - name: Upload to Avast - uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + local-dir: ./upload/ + allowlist-avast: + name: Anti Virus Allowlisting Avast + runs-on: ubuntu-latest + needs: download-file + if: github.event_name == 'workflow_call' || inputs.avast + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ needs.download-file.outputs.fileName }} + path: upload + - name: Upload to Avast + uses: wlixcc/SFTP-Deploy-Action@v1.2.5 with: - protocol: ftp server: whitelisting.avast.com - port: 21 + port: 22 username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }} - password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }} \ No newline at end of file + password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }} + ssh_private_key: '' + sftp_only: true + local_path: './upload/*' + remote_path: '/data' \ No newline at end of file diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index 16c5bc530..197aaf7dc 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -17,18 +17,18 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: '23.0.1+11' + JAVA_VERSION: '23.0.2+7' COFFEELIBS_JDK: 23 - COFFEELIBS_JDK_VERSION: '23.0.1+11-0ppa1' - OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-x64_bin-jmods.zip' - OPENJFX_JMODS_AMD64_HASH: '2164bca470bf70a5e2764645e2078ba7f787b274e5be3d7df30d87c5bb62bba6' - OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-aarch64_bin-jmods.zip' - OPENJFX_JMODS_AARCH64_HASH: '09c92fa9fa0b82adefd88640a14ebb2a49e5f3f733a57d1542f5590d060ffe1b' + COFFEELIBS_JDK_VERSION: '23.0.2+7-0ppa1' + OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/23.0.2/openjfx-23.0.2_linux-x64_bin-jmods.zip' + OPENJFX_JMODS_AMD64_HASH: '063baebc6922e4a89c94b9dfb7a4f53e59e8d6fec400d4e670b31bc2ab324dec' + OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/23.0.2/openjfx-23.0.2_linux-aarch64_bin-jmods.zip' + OPENJFX_JMODS_AARCH64_HASH: '9bbedaeae1590b69e2b22237bda310936df33e344dbc243bea2e86acaab3a0d8' jobs: build: name: Build Debian Package - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - id: versions diff --git a/.github/workflows/mac-dmg-x64.yml b/.github/workflows/mac-dmg-x64.yml index c7ef8b8b6..a26e32565 100644 --- a/.github/workflows/mac-dmg-x64.yml +++ b/.github/workflows/mac-dmg-x64.yml @@ -15,7 +15,7 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: '23.0.1+11' + JAVA_VERSION: '23.0.2+7' jobs: get-version: @@ -35,8 +35,8 @@ jobs: architecture: x64 output-suffix: x64 fuse-lib: macFUSE - openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_osx-x64_bin-jmods.zip' - openjfx-sha: '8857965975c464a0e5d57709292ce357d0ebb39f6168c41d5ca38301e42c3c8e' + openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.2/openjfx-23.0.2_osx-x64_bin-jmods.zip' + openjfx-sha: '5e6c65c065eea22430c0eab36f37a5985eb8ad99e19e8772262021740d338f68' steps: - uses: actions/checkout@v4 - name: Setup Java diff --git a/.github/workflows/mac-dmg.yml b/.github/workflows/mac-dmg.yml index d724e6ed2..27dd8cfdc 100644 --- a/.github/workflows/mac-dmg.yml +++ b/.github/workflows/mac-dmg.yml @@ -16,7 +16,7 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: '23.0.1+11' + JAVA_VERSION: '23.0.2+7' jobs: get-version: @@ -36,8 +36,8 @@ jobs: architecture: aarch64 output-suffix: arm64 fuse-lib: FUSE-T - openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_osx-aarch64_bin-jmods.zip' - openjfx-sha: 'a800724a1f3e6757ecfa0bd5bf7ed64d2e6a7a3f5b3522650a70b8cfc7782fb6' + openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.2/openjfx-23.0.2_osx-aarch64_bin-jmods.zip' + openjfx-sha: 'c690cc642a3924cf56622951f478ba57aec9ce09063761f800c3319331bed3fc' steps: - uses: actions/checkout@v4 - name: Setup Java diff --git a/.github/workflows/win-exe.yml b/.github/workflows/win-exe.yml index 14b9dd5aa..ed525a7f6 100644 --- a/.github/workflows/win-exe.yml +++ b/.github/workflows/win-exe.yml @@ -16,7 +16,7 @@ on: env: JAVA_DIST: 'zulu' - JAVA_VERSION: '23.0.1+11' + JAVA_VERSION: '23.0.2+7' OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_windows-x64_bin-jmods.zip' OPENJFX_JMODS_AMD64_HASH: 'ee176dcee3bd78bde7910735bd67f67c792882f5b89626796ae06f7a1c0119d3' WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi' @@ -40,9 +40,6 @@ jobs: LOOPBACK_ALIAS: 'cryptomator-vault' WIN_CONSOLE_FLAG: '' steps: - - name: Upgrade WIX to latest version - run: choco install wixtoolset --version 3.14.1 - shell: pwsh - uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v4 @@ -394,7 +391,7 @@ jobs: allowlist-exe: uses: ./.github/workflows/av-whitelist.yml - needs: [publish] + needs: [publish, allowlist-msi] with: url: ${{ needs.publish.outputs.download-url-exe }} secrets: inherit diff --git a/.idea/compiler.xml b/.idea/compiler.xml index be1033a0e..1256745d3 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -39,31 +39,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/README.md b/README.md index 9f0a0e0a1..d2bf39f29 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![cryptomator](cryptomator.png)](https://cryptomator.org/) -[![Build](https://github.com/cryptomator/cryptomator/workflows/Build/badge.svg)](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild) +[![Build](https://github.com/cryptomator/cryptomator/workflows/Build/badge.svg)](https://github.com/cryptomator/cryptomator/actions/workflows/build.yml?query=branch%3Adevelop) [![Known Vulnerabilities](https://snyk.io/test/github/cryptomator/cryptomator/badge.svg)](https://snyk.io/test/github/cryptomator/cryptomator) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=cryptomator_cryptomator&metric=alert_status)](https://sonarcloud.io/dashboard?id=cryptomator_cryptomator) [![Mastodon](https://img.shields.io/mastodon/follow/176112?domain=mastodon.online&style=flat)](https://mastodon.online/@cryptomator) diff --git a/dist/linux/appimage/build.sh b/dist/linux/appimage/build.sh index 63cdc09f9..e18054ae6 100755 --- a/dist/linux/appimage/build.sh +++ b/dist/linux/appimage/build.sh @@ -23,12 +23,12 @@ mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests -Djavafx.platform=l cp ../../../LICENSE.txt ../../../target cp ../../../target/cryptomator-*.jar ../../../target/mods -JAVAFX_VERSION=22.0.2 +JAVAFX_VERSION=23.0.2 JAVAFX_ARCH="x64" -JAVAFX_JMODS_SHA256='2164bca470bf70a5e2764645e2078ba7f787b274e5be3d7df30d87c5bb62bba6' +JAVAFX_JMODS_SHA256='063baebc6922e4a89c94b9dfb7a4f53e59e8d6fec400d4e670b31bc2ab324dec' if [ "${CPU_ARCH}" = "aarch64" ]; then JAVAFX_ARCH="aarch64" - JAVAFX_JMODS_SHA256='09c92fa9fa0b82adefd88640a14ebb2a49e5f3f733a57d1542f5590d060ffe1b' + JAVAFX_JMODS_SHA256='9bbedaeae1590b69e2b22237bda310936df33e344dbc243bea2e86acaab3a0d8' fi # download javaFX jmods diff --git a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml index a4d170a29..c109aa19d 100644 --- a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml +++ b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml @@ -6,6 +6,12 @@ Cryptomator Encryption for your cloud made easy + + encryption + security + privacy + +

Cryptomator provides easy-to-use, transparent, client-side encryption for your cloud. @@ -43,16 +49,16 @@ - Encrypt your data, protect your privacy - https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlocked_light.png + Encrypts your data, protects your privacy + https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlockDialog_light.png Dark theme available - https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlock_dark.png + https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlocked_dark.png - Uses AES-GCM 256 - an industry standardized, quantum resistant encryption - https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlockDialog_light.png + Easy to use - work on encrypted files as if they were not + https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlocked_light.png @@ -77,6 +83,9 @@ + + https://github.com/cryptomator/cryptomator/releases/1.16.0 + https://github.com/cryptomator/cryptomator/releases/1.15.3 diff --git a/dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg b/dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg index f8e79de1b..be0642364 100644 --- a/dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg +++ b/dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg @@ -1,12 +1,16 @@ - - - - + + + + + + \ No newline at end of file diff --git a/dist/linux/common/org.cryptomator.Cryptomator.tray.svg b/dist/linux/common/org.cryptomator.Cryptomator.tray.svg index 205998020..62cccb911 100644 --- a/dist/linux/common/org.cryptomator.Cryptomator.tray.svg +++ b/dist/linux/common/org.cryptomator.Cryptomator.tray.svg @@ -1,8 +1,10 @@ - - + + + + \ No newline at end of file diff --git a/dist/linux/debian/control b/dist/linux/debian/control index 96d29acd4..15626ba26 100644 --- a/dist/linux/debian/control +++ b/dist/linux/debian/control @@ -2,7 +2,7 @@ Source: cryptomator Maintainer: Cryptobot Section: utils Priority: optional -Build-Depends: debhelper (>=10), coffeelibs-jdk-23 (>= 23.0.1+11-0ppa1), libgtk-3-0, libxxf86vm1, libgl1 +Build-Depends: debhelper (>=10), coffeelibs-jdk-23 (>= 23.0.2+7-0ppa1), libgtk-3-0, libxxf86vm1, libgl1 Standards-Version: 4.5.0 Homepage: https://cryptomator.org Vcs-Git: https://github.com/cryptomator/cryptomator.git diff --git a/dist/mac/dmg/build.sh b/dist/mac/dmg/build.sh index dbbd818d9..e1bc3a125 100755 --- a/dist/mac/dmg/build.sh +++ b/dist/mac/dmg/build.sh @@ -32,15 +32,15 @@ REVISION_NO=`git rev-list --count HEAD` VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'` FUSE_LIB="FUSE-T" -JAVAFX_VERSION=23.0.1 +JAVAFX_VERSION=23.0.2 JAVAFX_ARCH="undefined" JAVAFX_JMODS_SHA256="undefined" if [ "$(machine)" = "arm64e" ]; then JAVAFX_ARCH="aarch64" - JAVAFX_JMODS_SHA256="a800724a1f3e6757ecfa0bd5bf7ed64d2e6a7a3f5b3522650a70b8cfc7782fb6" + JAVAFX_JMODS_SHA256="c690cc642a3924cf56622951f478ba57aec9ce09063761f800c3319331bed3fc" else JAVAFX_ARCH="x64" - JAVAFX_JMODS_SHA256="8857965975c464a0e5d57709292ce357d0ebb39f6168c41d5ca38301e42c3c8e" + JAVAFX_JMODS_SHA256="5e6c65c065eea22430c0eab36f37a5985eb8ad99e19e8772262021740d338f68" fi JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip" diff --git a/dist/win/build.ps1 b/dist/win/build.ps1 index a733f4557..9864d6306 100644 --- a/dist/win/build.ps1 +++ b/dist/win/build.ps1 @@ -51,7 +51,7 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) { } ## download jfx jmods -$javaFxVersion='23.0.1' +$javaFxVersion='23.0.2' $javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip" $javaFxJmodsSHA256 = 'ee176dcee3bd78bde7910735bd67f67c792882f5b89626796ae06f7a1c0119d3' $javaFxJmods = '.\resources\jfxJmods.zip' diff --git a/pom.xml b/pom.xml index 34a626ab7..2d753b9fe 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cryptomator cryptomator - 1.15.3 + 1.16.0 Cryptomator Desktop App @@ -33,42 +33,42 @@ org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents - 2.8.0 - 1.5.0 + 2.9.0 + 1.5.1 1.3.0 - 1.2.4 - 1.5.2 + 1.3.0 + 1.5.3 5.0.5 2.0.10 3.17.0 - 2.55 + 2.56.1 2.2 - 2.18.2 - 23.0.1 - 4.4.0 + 2.18.3 + 23.0.2 + 4.5.0 9.37.3 - 1.5.16 - 2.0.16 - 0.8.0 + 1.5.18 + 2.0.17 + 0.8.1 1.9.0 - 5.11.4 - 5.15.2 + 5.12.2 + 5.17.0 3.0 - 26.0.1 - 12.0.1 - 0.8.12 + 26.0.2 + 12.1.1 + 0.8.13 2.5.0 1.4.0 - 3.13.0 + 3.14.0 3.3.1 3.8.1 - 3.5.2 + 3.5.3 3.4.2 @@ -80,7 +80,7 @@ org.cryptomator cryptolib - 2.2.0 + 2.2.1 org.cryptomator @@ -159,6 +159,12 @@ com.auth0 java-jwt ${jwt.version} + + + com.fasterxml.jackson.core + jackson-core + + com.nimbusds diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 4dd4242b3..459d3c52d 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -59,6 +59,7 @@ open module org.cryptomator.desktop { uses org.cryptomator.common.locationpresets.LocationPresetsProvider; uses SSLContextProvider; + uses org.cryptomator.event.NotificationHandler; provides TrayMenuController with AwtTrayMenuController; provides Configurator with LogbackConfiguratorFactory; diff --git a/src/main/java/org/cryptomator/JavaFXUtil.java b/src/main/java/org/cryptomator/JavaFXUtil.java new file mode 100644 index 000000000..e1ec90587 --- /dev/null +++ b/src/main/java/org/cryptomator/JavaFXUtil.java @@ -0,0 +1,22 @@ +package org.cryptomator; + +import javafx.application.Platform; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class JavaFXUtil { + + private JavaFXUtil() {} + + public static boolean startPlatform() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + try { + Platform.startup(latch::countDown); + } catch (IllegalStateException e) { + //already initialized + latch.countDown(); + } + return latch.await(5, TimeUnit.SECONDS); + } + +} diff --git a/src/main/java/org/cryptomator/common/EventMap.java b/src/main/java/org/cryptomator/common/EventMap.java new file mode 100644 index 000000000..2e8dbf035 --- /dev/null +++ b/src/main/java/org/cryptomator/common/EventMap.java @@ -0,0 +1,160 @@ +package org.cryptomator.common; + +import org.cryptomator.cryptofs.event.BrokenDirFileEvent; +import org.cryptomator.cryptofs.event.BrokenFileNodeEvent; +import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent; +import org.cryptomator.cryptofs.event.ConflictResolvedEvent; +import org.cryptomator.cryptofs.event.DecryptionFailedEvent; +import org.cryptomator.cryptofs.event.FilesystemEvent; +import org.cryptomator.event.VaultEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javafx.beans.InvalidationListener; +import javafx.collections.FXCollections; +import javafx.collections.MapChangeListener; +import javafx.collections.ObservableMap; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.Set; + +/** + * Map containing {@link VaultEvent}s. + * The map is keyed by the ciphertext path of the affected resource _and_ the {@link FilesystemEvent}s class in order to group same events + *

+ * Use {@link EventMap#put(VaultEvent)} to add an element and {@link EventMap#remove(VaultEvent)} to remove it. + *

+ * The map is size restricted to {@value MAX_SIZE} elements. If a _new_ element (i.e. not already present) is added, the least recently added is removed. + */ +@Singleton +public class EventMap implements ObservableMap { + + private static final int MAX_SIZE = 300; + + public record EventKey(Path ciphertextPath, Class c) {} + + private final ObservableMap delegate; + + @Inject + public EventMap() { + delegate = FXCollections.observableHashMap(); + } + + @Override + public void addListener(MapChangeListener mapChangeListener) { + delegate.addListener(mapChangeListener); + } + + @Override + public void removeListener(MapChangeListener mapChangeListener) { + delegate.removeListener(mapChangeListener); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public VaultEvent get(Object key) { + return delegate.get(key); + } + + @Override + public @Nullable VaultEvent put(EventKey key, VaultEvent value) { + return delegate.put(key, value); + } + + @Override + public VaultEvent remove(Object key) { + return delegate.remove(key); + } + + @Override + public void putAll(@NotNull Map m) { + delegate.putAll(m); + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public @NotNull Set keySet() { + return delegate.keySet(); + } + + @Override + public @NotNull Collection values() { + return delegate.values(); + } + + @Override + public @NotNull Set> entrySet() { + return delegate.entrySet(); + } + + @Override + public void addListener(InvalidationListener invalidationListener) { + delegate.addListener(invalidationListener); + } + + @Override + public void removeListener(InvalidationListener invalidationListener) { + delegate.removeListener(invalidationListener); + } + + public synchronized void put(VaultEvent e) { + //compute key + var key = computeKey(e.actualEvent()); + //if-else + var nullOrEntry = delegate.get(key); + if (nullOrEntry == null) { + if (size() == MAX_SIZE) { + delegate.entrySet().stream() // + .min(Comparator.comparing(entry -> entry.getValue().actualEvent().getTimestamp())) // + .ifPresent(oldestEntry -> delegate.remove(oldestEntry.getKey())); + } + delegate.put(key, e); + } else { + delegate.put(key, nullOrEntry.incrementCount(e.actualEvent())); + } + } + + public synchronized VaultEvent remove(VaultEvent similar) { + //compute key + var key = computeKey(similar.actualEvent()); + return this.remove(key); + } + + private EventKey computeKey(FilesystemEvent e) { + var p = switch (e) { + case DecryptionFailedEvent(_, Path ciphertextPath, _) -> ciphertextPath; + case ConflictResolvedEvent(_, _, _, _, Path resolvedCiphertext) -> resolvedCiphertext; + case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext; + case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext; + case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext; + }; + return new EventKey(p, e.getClass()); + } +} diff --git a/src/main/java/org/cryptomator/common/keychain/KeychainManager.java b/src/main/java/org/cryptomator/common/keychain/KeychainManager.java index ac03e5ed6..04a46e742 100644 --- a/src/main/java/org/cryptomator/common/keychain/KeychainManager.java +++ b/src/main/java/org/cryptomator/common/keychain/KeychainManager.java @@ -2,6 +2,7 @@ package org.cryptomator.common.keychain; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; +import org.cryptomator.common.Passphrase; import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.integrations.keychain.KeychainAccessProvider; @@ -13,20 +14,24 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; @Singleton public class KeychainManager implements KeychainAccessProvider { private final ObjectExpression keychain; private final LoadingCache passphraseStoredProperties; + private final ReentrantReadWriteLock lock; @Inject KeychainManager(ObjectExpression selectedKeychain) { this.keychain = selectedKeychain; this.passphraseStoredProperties = Caffeine.newBuilder() // - .weakValues() // + .softValues() // .build(this::createStoredPassphraseProperty); keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll()); + this.lock = new ReentrantReadWriteLock(false); } private KeychainAccessProvider getKeychainOrFail() throws KeychainAccessException { @@ -42,29 +47,59 @@ public class KeychainManager implements KeychainAccessProvider { return getClass().getName(); } + @Override + public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException { + storePassphrase(key, displayName, passphrase, true); + } + + //TODO: remove ignored parameter once the API is fixed @Override public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean ignored) throws KeychainAccessException { - getKeychainOrFail().storePassphrase(key, displayName, passphrase); + try { + lock.writeLock().lock(); + var kc = getKeychainOrFail(); + //this is the only keychain actually using the parameter + var usesOSAuth = (kc.getClass().getName().equals("org.cryptomator.macos.keychain.TouchIdKeychainAccess")); + kc.storePassphrase(key, displayName, passphrase, usesOSAuth); + } finally { + lock.writeLock().unlock(); + } setPassphraseStored(key, true); } @Override public char[] loadPassphrase(String key) throws KeychainAccessException { - char[] passphrase = getKeychainOrFail().loadPassphrase(key); + char[] passphrase = null; + try { + lock.readLock().lock(); + passphrase = getKeychainOrFail().loadPassphrase(key); + } finally { + lock.readLock().unlock(); + } setPassphraseStored(key, passphrase != null); return passphrase; } @Override public void deletePassphrase(String key) throws KeychainAccessException { - getKeychainOrFail().deletePassphrase(key); + try { + lock.writeLock().lock(); + getKeychainOrFail().deletePassphrase(key); + } finally { + lock.writeLock().unlock(); + } setPassphraseStored(key, false); } @Override public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException { if (isPassphraseStored(key)) { - getKeychainOrFail().changePassphrase(key, displayName, passphrase); + try { + lock.writeLock().lock(); + getKeychainOrFail().changePassphrase(key, displayName, passphrase); + } finally { + lock.writeLock().unlock(); + } setPassphraseStored(key, true); } } @@ -101,13 +136,11 @@ public class KeychainManager implements KeychainAccessProvider { } private void setPassphraseStored(String key, boolean value) { - BooleanProperty property = passphraseStoredProperties.getIfPresent(key); - if (property != null) { - if (Platform.isFxApplicationThread()) { - property.set(value); - } else { - Platform.runLater(() -> property.set(value)); - } + BooleanProperty property = passphraseStoredProperties.get(key, _ -> new SimpleBooleanProperty(value)); + if (Platform.isFxApplicationThread()) { + property.set(value); + } else { + Platform.runLater(() -> property.set(value)); } } @@ -134,4 +167,22 @@ public class KeychainManager implements KeychainAccessProvider { } } + public ObjectExpression getKeychainImplementation() { + return this.keychain; + } + + public static void migrate(KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider, Map idsAndNames) throws KeychainAccessException { + if (oldProvider instanceof KeychainManager || newProvider instanceof KeychainManager) { + throw new IllegalArgumentException("KeychainManger must not be the source or target of migration"); + } + for (var entry : idsAndNames.entrySet()) { + var passphrase = oldProvider.loadPassphrase(entry.getKey()); + if (passphrase != null) { + var wrapper = new Passphrase(passphrase); + oldProvider.deletePassphrase(entry.getKey()); //we cannot apply "first-write-then-delete" pattern here, since we can potentially write to the same passphrase store (e.g., touchID and regular keychain) + newProvider.storePassphrase(entry.getKey(), entry.getValue(), wrapper); + wrapper.destroy(); + } + } + } } diff --git a/src/main/java/org/cryptomator/common/settings/VaultSettings.java b/src/main/java/org/cryptomator/common/settings/VaultSettings.java index fd21fc197..5112415b4 100644 --- a/src/main/java/org/cryptomator/common/settings/VaultSettings.java +++ b/src/main/java/org/cryptomator/common/settings/VaultSettings.java @@ -58,6 +58,7 @@ public class VaultSettings { public final StringExpression mountName; public final StringProperty mountService; public final IntegerProperty port; + public final StringProperty lastKnownKeyLoader; VaultSettings(VaultSettingsJson json) { this.id = json.id; @@ -74,6 +75,7 @@ public class VaultSettings { this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint)); this.mountService = new SimpleStringProperty(this, "mountService", json.mountService); this.port = new SimpleIntegerProperty(this, "port", json.port); + this.lastKnownKeyLoader = new SimpleStringProperty(this, "lastKnownKeyLoader", json.lastKnownKeyLoader); // mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318 this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> { final String name; @@ -99,7 +101,7 @@ public class VaultSettings { } Observable[] observables() { - return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService}; + return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService, lastKnownKeyLoader}; } public static VaultSettings withRandomId() { @@ -130,6 +132,7 @@ public class VaultSettings { json.mountPoint = mountPoint.map(Path::toString).getValue(); json.mountService = mountService.get(); json.port = port.get(); + json.lastKnownKeyLoader = lastKnownKeyLoader.get(); return json; } diff --git a/src/main/java/org/cryptomator/common/settings/VaultSettingsJson.java b/src/main/java/org/cryptomator/common/settings/VaultSettingsJson.java index 43aa204e8..870b74e07 100644 --- a/src/main/java/org/cryptomator/common/settings/VaultSettingsJson.java +++ b/src/main/java/org/cryptomator/common/settings/VaultSettingsJson.java @@ -48,6 +48,9 @@ class VaultSettingsJson { @JsonProperty("mountService") String mountService; + @JsonProperty("lastKnownKeyLoader") + String lastKnownKeyLoader; + @JsonProperty("port") int port = VaultSettings.DEFAULT_PORT; diff --git a/src/main/java/org/cryptomator/common/vaults/Vault.java b/src/main/java/org/cryptomator/common/vaults/Vault.java index f857d6ba1..2e1ae4bba 100644 --- a/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -10,6 +10,7 @@ package org.cryptomator.common.vaults; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Constants; +import org.cryptomator.event.FileSystemEventAggregator; import org.cryptomator.common.mount.Mounter; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; @@ -18,9 +19,11 @@ import org.cryptomator.cryptofs.CryptoFileSystemProperties; import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags; import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; +import org.cryptomator.cryptofs.event.FilesystemEvent; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; +import org.cryptomator.event.VaultEvent; import org.cryptomator.integrations.mount.MountFailedException; import org.cryptomator.integrations.mount.Mountpoint; import org.cryptomator.integrations.mount.UnmountFailedException; @@ -32,6 +35,7 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; +import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; @@ -74,6 +78,7 @@ public class Vault { private final ObjectBinding mountPoint; private final Mounter mounter; private final Settings settings; + private final FileSystemEventAggregator fileSystemEventAggregator; private final BooleanProperty showingStats; private final AtomicReference mountHandle = new AtomicReference<>(null); @@ -85,7 +90,8 @@ public class Vault { VaultState state, // @Named("lastKnownException") ObjectProperty lastKnownException, // VaultStats stats, // - Mounter mounter, Settings settings) { + Mounter mounter, Settings settings, // + FileSystemEventAggregator fileSystemEventAggregator) { this.vaultSettings = vaultSettings; this.configCache = configCache; this.cryptoFileSystem = cryptoFileSystem; @@ -102,6 +108,7 @@ public class Vault { this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state); this.mounter = mounter; this.settings = settings; + this.fileSystemEventAggregator = fileSystemEventAggregator; this.showingStats = new SimpleBooleanProperty(false); this.quickAccessEntry = new AtomicReference<>(null); } @@ -143,6 +150,7 @@ public class Vault { .withFlags(flags) // .withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) // .withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) // + .withFilesystemEventConsumer(this::consumeVaultEvent) // .build(); return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps); } @@ -251,6 +259,11 @@ public class Vault { } } + + private void consumeVaultEvent(FilesystemEvent e) { + fileSystemEventAggregator.put(this, e); + } + // ****************************************************************************** // Observable Properties // ******************************************************************************* @@ -412,6 +425,17 @@ public class Vault { } } + /** + * Gets the cleartext name from a given path to an encrypted vault file + */ + public String getCleartextName(Path ciphertextPath) throws IOException { + if (!state.getValue().equals(VaultState.Value.UNLOCKED)) { + throw new IllegalStateException("Vault is not unlocked"); + } + var fs = cryptoFileSystem.get(); + return fs.getCleartextName(ciphertextPath); + } + public VaultConfigCache getVaultConfigCache() { return configCache; } diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index 87faff77a..c362ca0c0 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -27,6 +27,7 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.ResourceBundle; @@ -49,9 +50,9 @@ public class VaultListManager { @Inject public VaultListManager(ObservableList vaultList, // AutoLocker autoLocker, // - List mountServices, - VaultComponent.Factory vaultComponentFactory, - ResourceBundle resourceBundle, + List mountServices, // + VaultComponent.Factory vaultComponentFactory, // + ResourceBundle resourceBundle, // Settings settings) { this.vaultList = vaultList; this.autoLocker = autoLocker; @@ -114,6 +115,10 @@ public class VaultListManager { private Vault create(VaultSettings vaultSettings) { var wrapper = new VaultConfigCache(vaultSettings); try { + if (Objects.isNull(vaultSettings.lastKnownKeyLoader.get())) { + var keyIdScheme = wrapper.get().getKeyId().getScheme(); + vaultSettings.lastKnownKeyLoader.set(keyIdScheme); + } var vaultState = determineVaultState(vaultSettings.path.get()); if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state wrapper.reloadConfig(); diff --git a/src/main/java/org/cryptomator/event/Answer.java b/src/main/java/org/cryptomator/event/Answer.java new file mode 100644 index 000000000..bfb780e52 --- /dev/null +++ b/src/main/java/org/cryptomator/event/Answer.java @@ -0,0 +1,14 @@ +package org.cryptomator.event; + +public sealed interface Answer permits Answer.DoNothing, Answer.DoSomething { + + + record DoNothing() implements Answer {} + + record DoSomething(Runnable action) implements Answer { + + void run() { + action.run(); + } + } +} diff --git a/src/main/java/org/cryptomator/event/FSEventBucket.java b/src/main/java/org/cryptomator/event/FSEventBucket.java new file mode 100644 index 000000000..370a9557a --- /dev/null +++ b/src/main/java/org/cryptomator/event/FSEventBucket.java @@ -0,0 +1,8 @@ +package org.cryptomator.event; + +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.cryptofs.event.FilesystemEvent; + +import java.nio.file.Path; + +public record FSEventBucket(Vault vault, Path idPath, Class c) {} diff --git a/src/main/java/org/cryptomator/event/FSEventBucketContent.java b/src/main/java/org/cryptomator/event/FSEventBucketContent.java new file mode 100644 index 000000000..b252608c7 --- /dev/null +++ b/src/main/java/org/cryptomator/event/FSEventBucketContent.java @@ -0,0 +1,5 @@ +package org.cryptomator.event; + +import org.cryptomator.cryptofs.event.FilesystemEvent; + +public record FSEventBucketContent(FilesystemEvent mostRecentEvent, int count) {} diff --git a/src/main/java/org/cryptomator/event/FileSystemEventAggregator.java b/src/main/java/org/cryptomator/event/FileSystemEventAggregator.java new file mode 100644 index 000000000..c871436fd --- /dev/null +++ b/src/main/java/org/cryptomator/event/FileSystemEventAggregator.java @@ -0,0 +1,107 @@ +package org.cryptomator.event; + +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.cryptofs.event.BrokenDirFileEvent; +import org.cryptomator.cryptofs.event.BrokenFileNodeEvent; +import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent; +import org.cryptomator.cryptofs.event.ConflictResolvedEvent; +import org.cryptomator.cryptofs.event.DecryptionFailedEvent; +import org.cryptomator.cryptofs.event.FilesystemEvent; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Aggregator for {@link FilesystemEvent}s. + *

+ * The aggregator groups filesystem events by the vault where the event occurred, an identifying path (clear- or ciphertext) and the event class (aka type). + * A group is called an {@link FSEventBucket}, its {@link FSEventBucketContent} is the most recent event object and a count of how often the event already occurred. + */ +@Singleton +public class FileSystemEventAggregator { + + private final ConcurrentHashMap map; + private final AtomicBoolean hasUpdates; + + @Inject + public FileSystemEventAggregator() { + this.map = new ConcurrentHashMap<>(); + this.hasUpdates = new AtomicBoolean(false); + } + + /** + * Adds the given event to the map. If a bucket for this event already exists, only the count is updated and the event set as the most recent one. + * + * @param v Vault where the event occurred + * @param e Actual {@link FilesystemEvent} + */ + public void put(Vault v, FilesystemEvent e) { + var key = computeKey(v, e); + map.compute(key, (k, val) -> { + if (val == null) { + return new FSEventBucketContent(e, 1); + } else { + return new FSEventBucketContent(e, val.count() + 1); + } + }); + hasUpdates.set(true); + } + + /** + * Removes an event bucket from the map. + */ + public FSEventBucketContent remove(FSEventBucket key) { + var content = map.remove(key); + hasUpdates.set(true); + return content; + } + + /** + * Clears the event map. + */ + public void clear() { + map.clear(); + hasUpdates.set(true); + } + + + public boolean hasMaybeUpdates() { + return hasUpdates.get(); + } + + /** + * Clones the map entries into a collection. + *

+ * The collection is first cleared, then all map entries are added in one bulk operation. Cleans the hasUpdates status. + * + * @param target collection which is first cleared and then the EntrySet copied to. + */ + public void cloneTo(Collection> target) { + hasUpdates.set(false); + target.clear(); + target.addAll(map.entrySet()); + } + + /** + * Method to compute the identifying key for a given filesystem event + * + * @param v Vault where the event occurred + * @param event Actual {@link FilesystemEvent} + * @return a {@link FSEventBucket} used in the map and lru cache + */ + private static FSEventBucket computeKey(Vault v, FilesystemEvent event) { + var p = switch (event) { + case DecryptionFailedEvent(_, Path ciphertextPath, _) -> ciphertextPath; + case ConflictResolvedEvent(_, _, _, _, Path resolvedCiphertext) -> resolvedCiphertext; + case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext; + case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext; + case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext; + }; + return new FSEventBucket(v, p, event.getClass()); + } +} diff --git a/src/main/java/org/cryptomator/event/NotificationHandler.java b/src/main/java/org/cryptomator/event/NotificationHandler.java new file mode 100644 index 000000000..983a5d2dd --- /dev/null +++ b/src/main/java/org/cryptomator/event/NotificationHandler.java @@ -0,0 +1,15 @@ +package org.cryptomator.event; + +import org.cryptomator.integrations.common.IntegrationsLoader; + +import java.util.ServiceLoader; +import java.util.stream.Stream; + +public interface NotificationHandler { + + Answer handle(VaultEvent e); + + static Stream loadAll() { + return IntegrationsLoader.loadAll(ServiceLoader.load(NotificationHandler.class), NotificationHandler.class); + } +} diff --git a/src/main/java/org/cryptomator/event/VaultEvent.java b/src/main/java/org/cryptomator/event/VaultEvent.java new file mode 100644 index 000000000..8b31747cf --- /dev/null +++ b/src/main/java/org/cryptomator/event/VaultEvent.java @@ -0,0 +1,27 @@ +package org.cryptomator.event; + +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.cryptofs.event.FilesystemEvent; + +import java.time.Instant; + +public record VaultEvent(Vault v, FilesystemEvent actualEvent, int count) implements Comparable { + + public VaultEvent(Vault v, FilesystemEvent actualEvent) { + this(v, actualEvent, 1); + } + + @Override + public int compareTo(VaultEvent other) { + var timeResult = actualEvent.getTimestamp().compareTo(other.actualEvent().getTimestamp()); + if(timeResult != 0) { + return timeResult; + } else { + return this.equals(other) ? 0 : this.actualEvent.getClass().getName().compareTo(other.actualEvent.getClass().getName()); + } + } + + public VaultEvent incrementCount(FilesystemEvent update) { + return new VaultEvent(v, update, count+1); + } +} diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java b/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java index 2ffda4d73..a25384c78 100644 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java +++ b/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java @@ -76,8 +76,10 @@ public class ReadmeGenerator { input.chars().forEachOrdered(c -> { if (c < 128) { sb.append((char) c); + } else if (c <= 0xFF) { + sb.append("\\'").append(String.format("%02X", c)); } else if (c < 0xFFFF) { - sb.append("\\u").append(c); + sb.append("\\uc1\\u").append(c); } }); } diff --git a/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/src/main/java/org/cryptomator/ui/common/FxmlFile.java index 6bf8ac7db..ce8c65a37 100644 --- a/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/src/main/java/org/cryptomator/ui/common/FxmlFile.java @@ -12,7 +12,9 @@ public enum FxmlFile { CONVERTVAULT_HUBTOPASSWORD_START("/fxml/convertvault_hubtopassword_start.fxml"), // CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), // CONVERTVAULT_HUBTOPASSWORD_SUCCESS("/fxml/convertvault_hubtopassword_success.fxml"), // + DECRYPTNAMES("/fxml/decryptnames.fxml"), // ERROR("/fxml/error.fxml"), // + EVENT_VIEW("/fxml/eventview.fxml"), // FORGET_PASSWORD("/fxml/forget_password.fxml"), // HEALTH_START("/fxml/health_start.fxml"), // HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), // diff --git a/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java index 485e89304..348b3a26b 100644 --- a/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java +++ b/src/main/java/org/cryptomator/ui/controls/FontAwesome5Icon.java @@ -7,6 +7,7 @@ public enum FontAwesome5Icon { ANCHOR("\uF13D"), // ARROW_UP("\uF062"), // BAN("\uF05E"), // + BELL("\uF0F3"), // BUG("\uF188"), // CARET_DOWN("\uF0D7"), // CARET_RIGHT("\uF0Da"), // @@ -15,10 +16,12 @@ public enum FontAwesome5Icon { CLIPBOARD("\uF328"), // COG("\uF013"), // COGS("\uF085"), // + COMPRESS_ALT("\uF422"), // COPY("\uF0C5"), // CROWN("\uF521"), // DONATE("\uF4B9"), // EDIT("\uF044"), // + ELLIPSIS_V("\uF142"), // EXCHANGE_ALT("\uF362"), // EXCLAMATION("\uF12A"), // EXCLAMATION_CIRCLE("\uF06A"), // diff --git a/src/main/java/org/cryptomator/ui/convertvault/HubToPasswordConvertController.java b/src/main/java/org/cryptomator/ui/convertvault/HubToPasswordConvertController.java index fd6d49b89..dc95f7051 100644 --- a/src/main/java/org/cryptomator/ui/convertvault/HubToPasswordConvertController.java +++ b/src/main/java/org/cryptomator/ui/convertvault/HubToPasswordConvertController.java @@ -15,6 +15,7 @@ import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.fxapp.FxApplicationWindows; +import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy; import org.cryptomator.ui.recoverykey.RecoveryKeyFactory; import org.jetbrains.annotations.VisibleForTesting; import org.slf4j.Logger; @@ -108,6 +109,7 @@ public class HubToPasswordConvertController implements FxController { .thenRunAsync(this::convertInternal, backgroundExecutorService) // .whenCompleteAsync((result, exception) -> { if (exception == null) { + vault.getVaultSettings().lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME); LOG.info("Conversion of vault {} succeeded.", vault.getPath()); window.setScene(successScene.get()); } else { diff --git a/src/main/java/org/cryptomator/ui/decryptname/CipherAndCleartext.java b/src/main/java/org/cryptomator/ui/decryptname/CipherAndCleartext.java new file mode 100644 index 000000000..909285b73 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/decryptname/CipherAndCleartext.java @@ -0,0 +1,25 @@ +package org.cryptomator.ui.decryptname; + +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.beans.value.ObservableValue; +import java.nio.file.Path; + +public record CipherAndCleartext(Path ciphertext, String cleartextName) { + + public String getCiphertextFilename() { + return ciphertext.getFileName().toString(); + } + + public ObservableValue ciphertextFilenameProperty() { + return new ReadOnlyStringWrapper(getCiphertextFilename()); + } + + public String getCleartextName() { + return cleartextName; + } + + public ObservableValue cleartextNameProperty() { + return new ReadOnlyStringWrapper(getCleartextName()); + } + +} diff --git a/src/main/java/org/cryptomator/ui/decryptname/DecryptFileNamesViewController.java b/src/main/java/org/cryptomator/ui/decryptname/DecryptFileNamesViewController.java new file mode 100644 index 000000000..453762f55 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/decryptname/DecryptFileNamesViewController.java @@ -0,0 +1,233 @@ +package org.cryptomator.ui.decryptname; + +import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.cryptofs.common.Constants; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.controls.FontAwesome5Icon; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javafx.application.Platform; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ListProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.fxml.FXML; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.input.Clipboard; +import javafx.scene.input.DataFormat; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.TransferMode; +import javafx.stage.FileChooser; +import javafx.stage.Stage; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@DecryptNameScoped +public class DecryptFileNamesViewController implements FxController { + + private static final Logger LOG = LoggerFactory.getLogger(DecryptFileNamesViewController.class); + private static final KeyCodeCombination COPY_TO_CLIPBOARD_SHORTCUT = new KeyCodeCombination(KeyCode.C, KeyCodeCombination.SHORTCUT_DOWN); + private static final String COPY_TO_CLIPBOARD_SHORTCUT_STRING_WIN = "CTRL+C"; + private static final String COPY_TO_CLIPBOARD_SHORTCUT_STRING_MAC = "⌘C"; + private static final String COPY_TO_CLIPBOARD_SHORTCUT_STRING_LINUX = "CTRL+C"; + + private final ListProperty mapping; + private final StringProperty dropZoneText = new SimpleStringProperty(); + private final ObjectProperty dropZoneIcon = new SimpleObjectProperty<>(); + private final BooleanProperty wrongFilesSelected = new SimpleBooleanProperty(false); + private final Stage window; + private final Vault vault; + private final ResourceBundle resourceBundle; + private final List initialList; + + @FXML + public TableColumn ciphertextColumn; + @FXML + public TableColumn cleartextColumn; + @FXML + public TableView cipherToCleartextTable; + + @Inject + public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, @DecryptNameWindow List pathsToDecrypt, ResourceBundle resourceBundle) { + this.window = window; + this.vault = vault; + this.resourceBundle = resourceBundle; + this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList()); + this.initialList = pathsToDecrypt; + } + + @FXML + public void initialize() { + cipherToCleartextTable.setItems(mapping); + cipherToCleartextTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + //DragNDrop + cipherToCleartextTable.setOnDragEntered(event -> { + if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { + cipherToCleartextTable.setItems(FXCollections.emptyObservableList()); + } + }); + cipherToCleartextTable.setOnDragOver(event -> { + if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { + if (SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC) { + event.acceptTransferModes(TransferMode.LINK); + } else { + event.acceptTransferModes(TransferMode.ANY); + } + } + }); + cipherToCleartextTable.setOnDragDropped(event -> { + if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { + checkAndDecrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList()); + cipherToCleartextTable.setItems(mapping); + } + }); + cipherToCleartextTable.setOnDragExited(_ -> cipherToCleartextTable.setItems(mapping)); + //selectionModel and copy-to-clipboard action + cipherToCleartextTable.getSelectionModel().setCellSelectionEnabled(true); + cipherToCleartextTable.setOnKeyPressed(keyEvent -> { + if (COPY_TO_CLIPBOARD_SHORTCUT.match(keyEvent)) { + copySingleCelltoClipboard(); + } + }); + ciphertextColumn.setCellValueFactory(new PropertyValueFactory<>("ciphertextFilename")); + cleartextColumn.setCellValueFactory(new PropertyValueFactory<>("cleartextName")); + + dropZoneText.setValue(resourceBundle.getString("decryptNames.dropZone.message")); + dropZoneIcon.setValue(FontAwesome5Icon.FILE_IMPORT); + + wrongFilesSelected.addListener((_, _, areWrongFiles) -> { + if (areWrongFiles) { + CompletableFuture.delayedExecutor(5, TimeUnit.SECONDS, Platform::runLater).execute(() -> { + dropZoneText.setValue(resourceBundle.getString("decryptNames.dropZone.message")); + dropZoneIcon.setValue(FontAwesome5Icon.FILE_IMPORT); + wrongFilesSelected.setValue(false); + }); + } + }); + if (!initialList.isEmpty()) { + checkAndDecrypt(initialList); + } + } + + private void copySingleCelltoClipboard() { + cipherToCleartextTable.getSelectionModel().getSelectedCells().stream().findFirst().ifPresent(tablePosition -> { + var selectedItem = cipherToCleartextTable.getSelectionModel().getSelectedItem(); + //TODO: give user feedback, if content is copied -> must be done via a custom cell factory to access the actual table cell! + if (tablePosition.getTableColumn().equals(ciphertextColumn)) { + Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, selectedItem.ciphertext().toString())); + } else { + Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, selectedItem.cleartextName())); + } + }); + } + + @FXML + public void selectFiles() { + var fileChooser = new FileChooser(); + fileChooser.setTitle(resourceBundle.getString("decryptNames.filePicker.title")); + fileChooser.setSelectedExtensionFilter(new FileChooser.ExtensionFilter(resourceBundle.getString("decryptNames.filePicker.extensionDescription"), List.of("*.c9r"))); + fileChooser.setInitialDirectory(vault.getPath().toFile()); + var ciphertextNodes = fileChooser.showOpenMultipleDialog(window); + if (ciphertextNodes != null) { + checkAndDecrypt(ciphertextNodes.stream().map(File::toPath).toList()); + } + } + + private void checkAndDecrypt(List pathsToDecrypt) { + mapping.clear(); + //Assumption: All files are in the same directory + var testPath = pathsToDecrypt.getFirst(); + if (!testPath.startsWith(vault.getPath())) { + setDropZoneError(resourceBundle.getString("decryptNames.dropZone.error.foreignFiles").formatted(vault.getDisplayName())); + return; + } + if (pathsToDecrypt.size() == 1 && testPath.endsWith(Constants.DIR_ID_BACKUP_FILE_NAME)) { + setDropZoneError(resourceBundle.getString("decryptNames.dropZone.error.vaultInternalFiles")); + return; + } + + try { + var newMapping = pathsToDecrypt.stream().filter(p -> !p.endsWith(Constants.DIR_ID_BACKUP_FILE_NAME)).map(this::getCleartextName).toList(); + mapping.addAll(newMapping); + } catch (UncheckedIOException e) { + setDropZoneError(resourceBundle.getString("decryptNames.dropZone.error.generic")); + LOG.info("Failed to decrypt filenames for directory {}", testPath.getParent(), e); + } catch (IllegalArgumentException e) { + setDropZoneError(resourceBundle.getString("decryptNames.dropZone.error.vaultInternalFiles")); + } catch (UnsupportedOperationException e) { + setDropZoneError(resourceBundle.getString("decryptNames.dropZone.error.noDirIdBackup")); + } + } + + private void setDropZoneError(String text) { + dropZoneIcon.setValue(FontAwesome5Icon.TIMES); + dropZoneText.setValue(text); + wrongFilesSelected.setValue(true); + } + + private CipherAndCleartext getCleartextName(Path ciphertextNode) { + try { + var cleartextName = vault.getCleartextName(ciphertextNode); + return new CipherAndCleartext(ciphertextNode, cleartextName); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + //obvservable getter + + public ObservableValue dropZoneTextProperty() { + return dropZoneText; + } + + public String getDropZoneText() { + return dropZoneText.get(); + } + + public ObservableValue dropZoneIconProperty() { + return dropZoneIcon; + } + + public FontAwesome5Icon getDropZoneIcon() { + return dropZoneIcon.get(); + } + + public void clearTable() { + mapping.clear(); + } + + public void copyTableToClipboard() { + var csv = mapping.stream().map(cipherAndClear -> "\"" + cipherAndClear.ciphertext() + "\", \"" + cipherAndClear.cleartextName() + "\"").collect(Collectors.joining("\n")); + Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, csv)); + } + + public String getCopyToClipboardShortcutString() { + if (SystemUtils.IS_OS_WINDOWS) { + return COPY_TO_CLIPBOARD_SHORTCUT_STRING_WIN; + } else if (SystemUtils.IS_OS_MAC) { + return COPY_TO_CLIPBOARD_SHORTCUT_STRING_MAC; + } else { + return COPY_TO_CLIPBOARD_SHORTCUT_STRING_LINUX; + } + } +} diff --git a/src/main/java/org/cryptomator/ui/decryptname/DecryptNameComponent.java b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameComponent.java new file mode 100644 index 000000000..7684d4286 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameComponent.java @@ -0,0 +1,50 @@ +package org.cryptomator.ui.decryptname; + +import dagger.BindsInstance; +import dagger.Lazy; +import dagger.Subcomponent; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.common.vaults.VaultState; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlScene; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Named; +import javafx.scene.Scene; +import javafx.stage.Stage; +import java.nio.file.Path; +import java.util.List; + +@DecryptNameScoped +@Subcomponent(modules = DecryptNameModule.class) +public interface DecryptNameComponent { + + Logger LOG = LoggerFactory.getLogger(DecryptNameComponent.class); + + @DecryptNameWindow + Stage window(); + + @FxmlScene(FxmlFile.DECRYPTNAMES) + Lazy decryptNamesView(); + + @DecryptNameWindow + Vault vault(); + + default void showDecryptFileNameWindow() { + Stage s = window(); + s.setScene(decryptNamesView().get()); + s.sizeToScene(); + if (vault().isUnlocked()) { + s.show(); + } else { + LOG.error("Aborted showing DecryptFileName window: vault state is not {}, but {}.", VaultState.Value.UNLOCKED, vault().getState()); + } + } + + @Subcomponent.Factory + interface Factory { + + DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner, @BindsInstance @DecryptNameWindow List pathsToDecrypt); + } +} diff --git a/src/main/java/org/cryptomator/ui/decryptname/DecryptNameModule.java b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameModule.java new file mode 100644 index 000000000..0dd573940 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameModule.java @@ -0,0 +1,59 @@ +package org.cryptomator.ui.decryptname; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoMap; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.ui.common.DefaultSceneFactory; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.FxControllerKey; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlLoaderFactory; +import org.cryptomator.ui.common.FxmlScene; +import org.cryptomator.ui.common.StageFactory; + +import javax.inject.Named; +import javax.inject.Provider; +import javafx.scene.Scene; +import javafx.stage.Modality; +import javafx.stage.Stage; +import java.util.Map; +import java.util.ResourceBundle; + +@Module +public abstract class DecryptNameModule { + + @Provides + @DecryptNameScoped + @DecryptNameWindow + static Stage provideStage(StageFactory factory, @Named("windowOwner") Stage owner, @DecryptNameWindow Vault vault, ResourceBundle resourceBundle) { + Stage stage = factory.create(); + stage.setResizable(true); + stage.initModality(Modality.WINDOW_MODAL); + stage.initOwner(owner); + stage.setTitle(resourceBundle.getString("decryptNames.title")); + vault.stateProperty().addListener(((_, _, _) -> stage.close())); //as soon as the state changes from unlocked, close the window + return stage; + } + + @Provides + @DecryptNameScoped + @DecryptNameWindow + static FxmlLoaderFactory provideFxmlLoaderFactory(Map, Provider> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) { + return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle); + } + + @Provides + @FxmlScene(FxmlFile.DECRYPTNAMES) + @DecryptNameScoped + static Scene provideDecryptNamesViewScene(@DecryptNameWindow FxmlLoaderFactory fxmlLoaders) { + return fxmlLoaders.createScene(FxmlFile.DECRYPTNAMES); + } + + @Binds + @IntoMap + @FxControllerKey(DecryptFileNamesViewController.class) + abstract FxController bindDecryptNamesViewController(DecryptFileNamesViewController controller); + +} diff --git a/src/main/java/org/cryptomator/ui/decryptname/DecryptNameScoped.java b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameScoped.java new file mode 100644 index 000000000..2a7ac0fc8 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameScoped.java @@ -0,0 +1,11 @@ +package org.cryptomator.ui.decryptname; + +import javax.inject.Scope; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Scope +@Documented +@Retention(RetentionPolicy.RUNTIME) +@interface DecryptNameScoped {} diff --git a/src/main/java/org/cryptomator/ui/decryptname/DecryptNameWindow.java b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameWindow.java new file mode 100644 index 000000000..7d68c9559 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameWindow.java @@ -0,0 +1,12 @@ +package org.cryptomator.ui.decryptname; + +import javax.inject.Qualifier; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Qualifier +@Documented +@Retention(RUNTIME) +@interface DecryptNameWindow {} diff --git a/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java b/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java index 5107fe740..837bea012 100644 --- a/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java +++ b/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java @@ -38,7 +38,7 @@ public class Dialogs { .setMessageKey("removeVault.message") // .setDescriptionKey("removeVault.description") // .setIcon(FontAwesome5Icon.QUESTION) // - .setOkButtonKey("removeVault.confirmBtn") // + .setOkButtonKey("generic.button.remove") // .setCancelButtonKey("generic.button.cancel") // .setOkAction(stage -> { LOG.debug("Removing vault {}.", vault.getDisplayName()); @@ -54,7 +54,7 @@ public class Dialogs { .setMessageKey("removeCert.message") // .setDescriptionKey("removeCert.description") // .setIcon(FontAwesome5Icon.QUESTION) // - .setOkButtonKey("removeCert.confirmBtn") // + .setOkButtonKey("generic.button.remove") // .setCancelButtonKey("generic.button.cancel") // .setOkAction(stage -> { settings.licenseKey.set(null); diff --git a/src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java b/src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java index 84d9e4f75..08f77849e 100644 --- a/src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java +++ b/src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java @@ -31,8 +31,9 @@ public class SimpleDialog { FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController( // new SimpleDialogController(resolveText(builder.messageKey, null), // resolveText(builder.descriptionKey, null), // - builder.icon, resolveText(builder.okButtonKey, null), // - resolveText(builder.cancelButtonKey, null), // + builder.icon, // + resolveText(builder.okButtonKey, null), // + builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, // () -> builder.okAction.accept(dialogStage), // () -> builder.cancelAction.accept(dialogStage)), // Scene::new, builder.resourceBundle); @@ -67,7 +68,6 @@ public class SimpleDialog { private String descriptionKey; private String okButtonKey; private String cancelButtonKey; - private FontAwesome5Icon icon; private Consumer okAction = Stage::close; private Consumer cancelAction = Stage::close; @@ -128,7 +128,6 @@ public class SimpleDialog { Objects.requireNonNull(messageKey, "SimpleDialog messageKey must be set."); Objects.requireNonNull(descriptionKey, "SimpleDialog descriptionKey must be set."); Objects.requireNonNull(okButtonKey, "SimpleDialog okButtonKey must be set."); - Objects.requireNonNull(cancelButtonKey, "SimpleDialog cancelButtonKey must be set."); try { return new SimpleDialog(this); diff --git a/src/main/java/org/cryptomator/ui/dialogs/SimpleDialogController.java b/src/main/java/org/cryptomator/ui/dialogs/SimpleDialogController.java index 0eee1b308..bbf590145 100644 --- a/src/main/java/org/cryptomator/ui/dialogs/SimpleDialogController.java +++ b/src/main/java/org/cryptomator/ui/dialogs/SimpleDialogController.java @@ -14,6 +14,7 @@ public class SimpleDialogController implements FxController { private final String cancelButtonText; private final Runnable okAction; private final Runnable cancelAction; + private final boolean cancelButtonVisible; public SimpleDialogController(String message, String description, FontAwesome5Icon icon, String okButtonText, String cancelButtonText, Runnable okAction, Runnable cancelAction) { this.message = message; @@ -23,6 +24,11 @@ public class SimpleDialogController implements FxController { this.cancelButtonText = cancelButtonText; this.okAction = okAction; this.cancelAction = cancelAction; + this.cancelButtonVisible = cancelButtonText != null && !cancelButtonText.isEmpty(); + } + + public boolean isCancelButtonVisible() { + return cancelButtonVisible; } public String getMessage() { diff --git a/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java b/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java new file mode 100644 index 000000000..487049d4c --- /dev/null +++ b/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java @@ -0,0 +1,329 @@ +package org.cryptomator.ui.eventview; + +import org.cryptomator.event.FSEventBucket; +import org.cryptomator.event.FSEventBucketContent; +import org.cryptomator.event.FileSystemEventAggregator; +import org.cryptomator.common.Nullable; +import org.cryptomator.common.ObservableUtil; +import org.cryptomator.cryptofs.CryptoPath; +import org.cryptomator.cryptofs.event.BrokenDirFileEvent; +import org.cryptomator.cryptofs.event.BrokenFileNodeEvent; +import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent; +import org.cryptomator.cryptofs.event.ConflictResolvedEvent; +import org.cryptomator.cryptofs.event.DecryptionFailedEvent; +import org.cryptomator.integrations.revealpath.RevealFailedException; +import org.cryptomator.integrations.revealpath.RevealPathService; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.controls.FontAwesome5Icon; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.beans.value.ObservableValue; +import javafx.fxml.FXML; +import javafx.geometry.Side; +import javafx.scene.control.Button; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.Tooltip; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.scene.layout.HBox; +import javafx.util.Duration; +import java.nio.file.Path; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.Map; +import java.util.Optional; +import java.util.ResourceBundle; +import java.util.function.Function; + +public class EventListCellController implements FxController { + + private static final Logger LOG = LoggerFactory.getLogger(EventListCellController.class); + private static final DateTimeFormatter LOCAL_DATE_FORMATTER = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withZone(ZoneId.systemDefault()); + private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault()); + + private final FileSystemEventAggregator fileSystemEventAggregator; + @Nullable + private final RevealPathService revealService; + private final ResourceBundle resourceBundle; + private final ObjectProperty> eventEntry; + private final StringProperty eventMessage; + private final StringProperty eventDescription; + private final ObjectProperty eventIcon; + private final ObservableValue eventCount; + private final ObservableValue vaultUnlocked; + private final ObservableValue readableTime; + private final ObservableValue readableDate; + private final ObservableValue message; + private final ObservableValue description; + private final ObservableValue icon; + private final BooleanProperty actionsButtonVisible; + private final Tooltip eventTooltip; + + @FXML + HBox root; + @FXML + ContextMenu eventActionsMenu; + @FXML + Button eventActionsButton; + + @Inject + public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator, Optional revealService, ResourceBundle resourceBundle) { + this.fileSystemEventAggregator = fileSystemEventAggregator; + this.revealService = revealService.orElseGet(() -> null); + this.resourceBundle = resourceBundle; + this.eventEntry = new SimpleObjectProperty<>(null); + this.eventMessage = new SimpleStringProperty(); + this.eventDescription = new SimpleStringProperty(); + this.eventIcon = new SimpleObjectProperty<>(); + this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1? "" : "("+ e.getValue().count() +")", ""); + this.vaultUnlocked = ObservableUtil.mapWithDefault(eventEntry.flatMap(e -> e.getKey().vault().unlockedProperty()), Function.identity(), false); + this.readableTime = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_TIME_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), ""); + this.readableDate = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_DATE_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), ""); + this.message = Bindings.createStringBinding(this::selectMessage, vaultUnlocked, eventMessage); + this.description = Bindings.createStringBinding(this::selectDescription, vaultUnlocked, eventDescription); + this.icon = Bindings.createObjectBinding(this::selectIcon, vaultUnlocked, eventIcon); + this.actionsButtonVisible = new SimpleBooleanProperty(); + this.eventTooltip = new Tooltip(); + eventTooltip.setShowDelay(Duration.millis(500.0)); + } + + @FXML + public void initialize() { + actionsButtonVisible.bind(Bindings.createBooleanBinding(this::determineActionsButtonVisibility, root.hoverProperty(), eventActionsMenu.showingProperty(), vaultUnlocked)); + vaultUnlocked.addListener((_, _, newValue) -> eventActionsMenu.hide()); + Tooltip.install(root, eventTooltip); + } + + private boolean determineActionsButtonVisibility() { + return vaultUnlocked.getValue() && (eventActionsMenu.isShowing() || root.isHover()); + } + + public void setEventEntry(@NotNull Map.Entry item) { + eventEntry.set(item); + eventActionsMenu.hide(); + eventActionsMenu.getItems().clear(); + eventTooltip.setText(item.getKey().vault().getDisplayName()); + addAction("generic.action.dismiss", () -> { + fileSystemEventAggregator.remove(item.getKey()); + }); + switch (item.getValue().mostRecentEvent()) { + case ConflictResolvedEvent fse -> this.adjustToConflictResolvedEvent(fse); + case ConflictResolutionFailedEvent fse -> this.adjustToConflictEvent(fse); + case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse); + case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse); + case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(fse); + } + } + + + private void adjustToBrokenFileNodeEvent(BrokenFileNodeEvent bfe) { + eventIcon.setValue(FontAwesome5Icon.TIMES); + eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message")); + eventDescription.setValue(bfe.ciphertextPath().getFileName().toString()); + if (revealService != null) { + addAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, convertVaultPathToSystemPath(bfe.ciphertextPath()))); + } else { + addAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.ciphertextPath()).toString())); + } + addAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString())); + } + + private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) { + eventIcon.setValue(FontAwesome5Icon.CHECK); + eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message")); + eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString()); + if (revealService != null) { + addAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath()))); + } else { + addAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString())); + } + } + + private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) { + eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT); + eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message")); + eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString()); + if (revealService != null) { + addAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath()))); + addAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath())); + } else { + addAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString())); + addAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString())); + } + } + + private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) { + eventIcon.setValue(FontAwesome5Icon.BAN); + eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message")); + eventDescription.setValue(dfe.ciphertextPath().getFileName().toString()); + if (revealService != null) { + addAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath())); + } else { + addAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString())); + } + } + + private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) { + eventIcon.setValue(FontAwesome5Icon.TIMES); + eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message")); + eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString()); + if (revealService != null) { + addAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath())); + } else { + addAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString())); + } + } + + private void addAction(String localizationKey, Runnable action) { + var entry = new MenuItem(resourceBundle.getString(localizationKey)); + entry.getStyleClass().addLast("dropdown-button-context-menu-item"); + entry.setOnAction(_ -> action.run()); + eventActionsMenu.getItems().addLast(entry); + } + + + private FontAwesome5Icon selectIcon() { + if (vaultUnlocked.getValue()) { + return eventIcon.getValue(); + } else { + return FontAwesome5Icon.LOCK; + } + } + + private String selectMessage() { + if (vaultUnlocked.getValue()) { + return eventMessage.getValue(); + } else { + return "***********"; + } + } + + private String selectDescription() { + if (vaultUnlocked.getValue()) { + return eventDescription.getValue(); + } else if (eventEntry.getValue() != null) { + var e = eventEntry.getValue().getKey(); + return resourceBundle.getString("eventView.entry.vaultLocked.description").formatted(e != null ? e.vault().getDisplayName() : ""); + } else { + return ""; + } + } + + + @FXML + public void toggleEventActionsMenu() { + var e = eventEntry.get(); + if (e != null) { + if (eventActionsMenu.isShowing()) { + eventActionsMenu.hide(); + } else { + eventActionsMenu.show(eventActionsButton, Side.BOTTOM, 0.0, 0.0); + } + } + } + + private Path convertVaultPathToSystemPath(Path p) { + if (!(p instanceof CryptoPath)) { + throw new IllegalArgumentException("Path " + p + " is not a vault path"); + } + var v = eventEntry.getValue().getKey().vault(); + if (!v.isUnlocked()) { + return Path.of(System.getProperty("user.home")); + } + + var mountUri = v.getMountPoint().uri(); + var internalPath = p.toString().substring(1); + return Path.of(mountUri.getPath().concat(internalPath).substring(1)); + } + + private void reveal(RevealPathService s, Path p) { + try { + s.reveal(p); + } catch (RevealFailedException e) { + LOG.warn("Failed to show path {}", p, e); + } + } + + private void copyToClipboard(String s) { + var content = new ClipboardContent(); + content.putString(s); + Clipboard.getSystemClipboard().setContent(content); + } + + //-- property accessors -- + public ObservableValue messageProperty() { + return message; + } + + public String getMessage() { + return message.getValue(); + } + + public ObservableValue countProperty() { + return eventCount; + } + + public String getCount() { + return eventCount.getValue(); + } + + public ObservableValue descriptionProperty() { + return description; + } + + public String getDescription() { + return description.getValue(); + } + + public ObservableValue iconProperty() { + return icon; + } + + public FontAwesome5Icon getIcon() { + return icon.getValue(); + } + + public ObservableValue actionsButtonVisibleProperty() { + return actionsButtonVisible; + } + + public boolean isActionsButtonVisible() { + return actionsButtonVisible.getValue(); + } + + public ObservableValue eventLocalTimeProperty() { + return readableTime; + } + + public String getEventLocalTime() { + return readableTime.getValue(); + } + + public ObservableValue eventLocalDateProperty() { + return readableDate; + } + + public String getEventLocalDate() { + return readableDate.getValue(); + } + + public ObservableValue vaultUnlockedProperty() { + return vaultUnlocked; + } + + public boolean isVaultUnlocked() { + return vaultUnlocked.getValue(); + } +} diff --git a/src/main/java/org/cryptomator/ui/eventview/EventListCellFactory.java b/src/main/java/org/cryptomator/ui/eventview/EventListCellFactory.java new file mode 100644 index 000000000..e607b41a7 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/eventview/EventListCellFactory.java @@ -0,0 +1,66 @@ +package org.cryptomator.ui.eventview; + +import org.cryptomator.event.FSEventBucket; +import org.cryptomator.event.FSEventBucketContent; +import org.cryptomator.ui.common.FxmlLoaderFactory; + +import javax.inject.Inject; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.util.Callback; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Map; + +@EventViewScoped +public class EventListCellFactory implements Callback>, ListCell>> { + + private static final String FXML_PATH = "/fxml/eventview_cell.fxml"; + + private final FxmlLoaderFactory fxmlLoaders; + + @Inject + EventListCellFactory(@EventViewWindow FxmlLoaderFactory fxmlLoaders) { + this.fxmlLoaders = fxmlLoaders; + } + + + @Override + public ListCell> call(ListView> eventListView) { + try { + FXMLLoader fxmlLoader = fxmlLoaders.load(FXML_PATH); + return new Cell(fxmlLoader.getRoot(), fxmlLoader.getController()); + } catch (IOException e) { + throw new UncheckedIOException("Failed to load %s.".formatted(FXML_PATH), e); + } + } + + private static class Cell extends ListCell> { + + private final Parent root; + private final EventListCellController controller; + + public Cell(Parent root, EventListCellController controller) { + this.root = root; + this.controller = controller; + } + + @Override + protected void updateItem(Map.Entry item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setGraphic(null); + this.getStyleClass().remove("list-cell"); + } else { + this.getStyleClass().addLast("list-cell"); + setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + setGraphic(root); + controller.setEventEntry(item); + } + } + } +} diff --git a/src/main/java/org/cryptomator/ui/eventview/EventViewComponent.java b/src/main/java/org/cryptomator/ui/eventview/EventViewComponent.java new file mode 100644 index 000000000..443885ec6 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/eventview/EventViewComponent.java @@ -0,0 +1,35 @@ +package org.cryptomator.ui.eventview; + +import dagger.Lazy; +import dagger.Subcomponent; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlScene; + +import javafx.scene.Scene; +import javafx.stage.Stage; + +@EventViewScoped +@Subcomponent(modules = {EventViewModule.class}) +public interface EventViewComponent { + + @EventViewWindow + Stage window(); + + @FxmlScene(FxmlFile.EVENT_VIEW) + Lazy scene(); + + default Stage showEventViewerWindow() { + Stage stage = window(); + stage.setScene(scene().get()); + stage.sizeToScene(); + stage.show(); + stage.requestFocus(); + return stage; + } + + @Subcomponent.Factory + interface Factory { + + EventViewComponent create(); + } +} diff --git a/src/main/java/org/cryptomator/ui/eventview/EventViewController.java b/src/main/java/org/cryptomator/ui/eventview/EventViewController.java new file mode 100644 index 000000000..ca4fe9d55 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/eventview/EventViewController.java @@ -0,0 +1,132 @@ +package org.cryptomator.ui.eventview; + +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.event.FSEventBucket; +import org.cryptomator.event.FSEventBucketContent; +import org.cryptomator.event.FileSystemEventAggregator; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.fxapp.FxFSEventList; + +import javax.inject.Inject; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; +import javafx.fxml.FXML; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.ListView; +import javafx.util.StringConverter; +import java.util.Map; +import java.util.ResourceBundle; + +@EventViewScoped +public class EventViewController implements FxController { + + private final FilteredList> filteredEventList; + private final ObservableList vaults; + private final FileSystemEventAggregator aggregator; + private final SortedList> sortedEventList; + private final ObservableList choiceBoxEntries; + private final ResourceBundle resourceBundle; + private final EventListCellFactory cellFactory; + + @FXML + ChoiceBox vaultFilterChoiceBox; + @FXML + ListView> eventListView; + + @Inject + public EventViewController(FxFSEventList fxFSEventList, ObservableList vaults, ResourceBundle resourceBundle, EventListCellFactory cellFactory, FileSystemEventAggregator aggregator) { + this.filteredEventList = fxFSEventList.getObservableList().filtered(_ -> true); + this.vaults = vaults; + this.aggregator = aggregator; + this.sortedEventList = new SortedList<>(filteredEventList, this::compareBuckets); + this.choiceBoxEntries = FXCollections.observableArrayList(); + this.resourceBundle = resourceBundle; + this.cellFactory = cellFactory; + } + + /** + * Comparison method for the lru cache. During comparsion the map is accessed. + * First the entries are compared by the event timestamp, then vaultId, then identifying path and lastly by class name. + * + * @param left an entry of a {@link FSEventBucket} and its content + * @param right another entry of a {@link FSEventBucket} plus content, compared to {@code left} + * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second. + */ + private int compareBuckets(Map.Entry left, Map.Entry right) { + var t1 = left.getValue().mostRecentEvent().getTimestamp(); + var t2 = right.getValue().mostRecentEvent().getTimestamp(); + var timeComparison = t1.compareTo(t2); + if (timeComparison != 0) { + return -timeComparison; //we need the reverse timesorting + } + var vaultIdComparison = left.getKey().vault().getId().compareTo(right.getKey().vault().getId()); + if (vaultIdComparison != 0) { + return vaultIdComparison; + } + var pathComparison = left.getKey().idPath().compareTo(right.getKey().idPath()); + if (pathComparison != 0) { + return pathComparison; + } + return left.getKey().c().getName().compareTo(right.getKey().c().getName()); + } + + @FXML + public void initialize() { + choiceBoxEntries.add(null); + choiceBoxEntries.addAll(vaults); + vaults.addListener((ListChangeListener) c -> { + while (c.next()) { + choiceBoxEntries.removeAll(c.getRemoved()); + choiceBoxEntries.addAll(c.getAddedSubList()); + } + }); + + eventListView.setCellFactory(cellFactory); + eventListView.setItems(sortedEventList); + + vaultFilterChoiceBox.setItems(choiceBoxEntries); + vaultFilterChoiceBox.valueProperty().addListener(this::applyVaultFilter); + vaultFilterChoiceBox.setConverter(new VaultConverter(resourceBundle)); + } + + private void applyVaultFilter(ObservableValue v, Vault oldV, Vault newV) { + if (newV == null) { + filteredEventList.setPredicate(_ -> true); + } else { + filteredEventList.setPredicate(e -> e.getKey().vault().equals(newV)); + } + } + + @FXML + void clearEvents() { + aggregator.clear(); + } + + private static class VaultConverter extends StringConverter { + + private final ResourceBundle resourceBundle; + + VaultConverter(ResourceBundle resourceBundle) { + this.resourceBundle = resourceBundle; + } + + @Override + public String toString(Vault v) { + if (v == null) { + return resourceBundle.getString("eventView.filter.allVaults"); + } else { + return v.getDisplayName(); + } + } + + @Override + public Vault fromString(String displayLanguage) { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/src/main/java/org/cryptomator/ui/eventview/EventViewModule.java b/src/main/java/org/cryptomator/ui/eventview/EventViewModule.java new file mode 100644 index 000000000..94829023f --- /dev/null +++ b/src/main/java/org/cryptomator/ui/eventview/EventViewModule.java @@ -0,0 +1,67 @@ +package org.cryptomator.ui.eventview; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoMap; +import org.cryptomator.ui.common.DefaultSceneFactory; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.FxControllerKey; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlLoaderFactory; +import org.cryptomator.ui.common.FxmlScene; +import org.cryptomator.ui.common.StageFactory; +import org.cryptomator.ui.fxapp.FxFSEventList; + +import javax.inject.Provider; +import javafx.scene.Scene; +import javafx.stage.Modality; +import javafx.stage.Stage; +import java.util.Map; +import java.util.ResourceBundle; + +@Module +abstract class EventViewModule { + + @Provides + @EventViewScoped + @EventViewWindow + static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle, FxFSEventList fxFSEventList) { + Stage stage = factory.create(); + stage.setHeight(498); + stage.setTitle(resourceBundle.getString("eventView.title")); + stage.setResizable(true); + stage.initModality(Modality.NONE); + stage.focusedProperty().addListener((_,_,isFocused) -> { + if(isFocused) { + fxFSEventList.unreadEventsProperty().setValue(false); + } + }); + return stage; + } + + @Provides + @EventViewScoped + @EventViewWindow + static FxmlLoaderFactory provideFxmlLoaderFactory(Map, Provider> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) { + return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle); + } + + @Provides + @FxmlScene(FxmlFile.EVENT_VIEW) + @EventViewScoped + static Scene provideEventViewerScene(@EventViewWindow FxmlLoaderFactory fxmlLoaders) { + return fxmlLoaders.createScene(FxmlFile.EVENT_VIEW); + } + + + @Binds + @IntoMap + @FxControllerKey(EventViewController.class) + abstract FxController bindEventViewController(EventViewController controller); + + @Binds + @IntoMap + @FxControllerKey(EventListCellController.class) + abstract FxController bindEventListCellController(EventListCellController controller); +} diff --git a/src/main/java/org/cryptomator/ui/eventview/EventViewScoped.java b/src/main/java/org/cryptomator/ui/eventview/EventViewScoped.java new file mode 100644 index 000000000..5281db3cd --- /dev/null +++ b/src/main/java/org/cryptomator/ui/eventview/EventViewScoped.java @@ -0,0 +1,13 @@ +package org.cryptomator.ui.eventview; + +import javax.inject.Scope; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Scope +@Documented +@Retention(RetentionPolicy.RUNTIME) +@interface EventViewScoped { + +} diff --git a/src/main/java/org/cryptomator/ui/eventview/EventViewWindow.java b/src/main/java/org/cryptomator/ui/eventview/EventViewWindow.java new file mode 100644 index 000000000..44e9b312a --- /dev/null +++ b/src/main/java/org/cryptomator/ui/eventview/EventViewWindow.java @@ -0,0 +1,14 @@ +package org.cryptomator.ui.eventview; + +import javax.inject.Qualifier; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Qualifier +@Documented +@Retention(RUNTIME) +@interface EventViewWindow { + +} diff --git a/src/main/java/org/cryptomator/ui/eventview/UpdateEventViewController.java b/src/main/java/org/cryptomator/ui/eventview/UpdateEventViewController.java new file mode 100644 index 000000000..19c475447 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/eventview/UpdateEventViewController.java @@ -0,0 +1,14 @@ +package org.cryptomator.ui.eventview; + +import org.cryptomator.ui.common.FxController; + +import javax.inject.Inject; + +@EventViewScoped +public class UpdateEventViewController implements FxController { + + @Inject + public UpdateEventViewController() { + + } +} diff --git a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java index 719071ed2..65bc080ca 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AutoUnlocker.java @@ -43,8 +43,8 @@ public class AutoUnlocker { private CompletionStage unlockSequentially(Stream vaultStream) { // this is an attempt to run all the unlock workflows sequentially, i.e. start the next workflow only after completing/failing the previous workflow. return vaultStream.filter(Vault::isLocked).reduce(CompletableFuture.completedFuture(null), - (prevUnlock, nextVault) -> prevUnlock.thenCompose(unused -> appWindows.startUnlockWorkflow(nextVault, null)), - (prevUnlock, nextUnlock) -> nextUnlock.exceptionally(e -> null) // we don't care here about the exception, logged elsewhere + (prevUnlock, nextVault) -> prevUnlock.thenCompose(_ -> appWindows.startUnlockWorkflow(nextVault, null)), + (_, nextUnlock) -> nextUnlock.exceptionally(_ -> null) // we don't care here about the exception, logged elsewhere ); } diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index fd480033c..ccc0684af 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -29,9 +29,10 @@ public class FxApplication { private final FxApplicationStyle applicationStyle; private final FxApplicationTerminator applicationTerminator; private final AutoUnlocker autoUnlocker; + private final FxFSEventList fxFSEventList; @Inject - FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) { + FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker, FxFSEventList fxFSEventList) { this.startupTime = startupTime; this.environment = environment; this.settings = settings; @@ -41,6 +42,7 @@ public class FxApplication { this.applicationStyle = applicationStyle; this.applicationTerminator = applicationTerminator; this.autoUnlocker = autoUnlocker; + this.fxFSEventList = fxFSEventList; } public void start() { @@ -85,6 +87,7 @@ public class FxApplication { migrateAndInformDokanyRemoval(); launchEventHandler.startHandlingLaunchEvents(); + fxFSEventList.schedulePollForUpdates(); autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES); } diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java index af98e284c..8eb221883 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java @@ -7,7 +7,9 @@ package org.cryptomator.ui.fxapp; import dagger.Module; import dagger.Provides; +import org.cryptomator.ui.decryptname.DecryptNameComponent; import org.cryptomator.ui.error.ErrorComponent; +import org.cryptomator.ui.eventview.EventViewComponent; import org.cryptomator.ui.health.HealthCheckComponent; import org.cryptomator.ui.lock.LockComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent; @@ -19,11 +21,15 @@ import org.cryptomator.ui.unlock.UnlockComponent; import org.cryptomator.ui.updatereminder.UpdateReminderComponent; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; +import javax.inject.Named; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.scene.image.Image; import java.io.IOException; import java.io.InputStream; @Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, // + DecryptNameComponent.class, // MainWindowComponent.class, // PreferencesComponent.class, // VaultOptionsComponent.class, // @@ -33,7 +39,8 @@ import java.io.InputStream; ErrorComponent.class, // HealthCheckComponent.class, // UpdateReminderComponent.class, // - ShareVaultComponent.class}) + ShareVaultComponent.class, // + EventViewComponent.class}) abstract class FxApplicationModule { private static Image createImageFromResource(String resourceName) throws IOException { @@ -66,4 +73,10 @@ abstract class FxApplicationModule { return builder.build(); } + @Provides + @FxApplicationScoped + static EventViewComponent provideEventViewComponent(EventViewComponent.Factory factory) { + return factory.create(); + } + } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java index 54acf62a3..c8a870fd8 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java @@ -6,7 +6,9 @@ import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.integrations.tray.TrayIntegrationProvider; import org.cryptomator.ui.dialogs.Dialogs; +import org.cryptomator.ui.dialogs.SimpleDialog; import org.cryptomator.ui.error.ErrorComponent; +import org.cryptomator.ui.eventview.EventViewComponent; import org.cryptomator.ui.lock.LockComponent; import org.cryptomator.ui.mainwindow.MainWindowComponent; import org.cryptomator.ui.preferences.PreferencesComponent; @@ -51,6 +53,7 @@ public class FxApplicationWindows { private final UpdateReminderComponent.Factory updateReminderWindowFactory; private final LockComponent.Factory lockWorkflowFactory; private final ErrorComponent.Factory errorWindowFactory; + private final Lazy eventViewWindow; private final ExecutorService executor; private final VaultOptionsComponent.Factory vaultOptionsWindow; private final ShareVaultComponent.Factory shareVaultWindow; @@ -69,6 +72,7 @@ public class FxApplicationWindows { ErrorComponent.Factory errorWindowFactory, // VaultOptionsComponent.Factory vaultOptionsWindow, // ShareVaultComponent.Factory shareVaultWindow, // + Lazy eventViewWindow, // ExecutorService executor, // Dialogs dialogs) { this.primaryStage = primaryStage; @@ -80,6 +84,7 @@ public class FxApplicationWindows { this.updateReminderWindowFactory = updateReminderWindowFactory; this.lockWorkflowFactory = lockWorkflowFactory; this.errorWindowFactory = errorWindowFactory; + this.eventViewWindow = eventViewWindow; this.executor = executor; this.vaultOptionsWindow = vaultOptionsWindow; this.shareVaultWindow = shareVaultWindow; @@ -93,17 +98,17 @@ public class FxApplicationWindows { // register preferences shortcut if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) { - desktop.setPreferencesHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ANY)); + desktop.setPreferencesHandler(_ -> showPreferencesWindow(SelectedPreferencesTab.ANY)); } // register preferences shortcut if (desktop.isSupported(Desktop.Action.APP_ABOUT)) { - desktop.setAboutHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ABOUT)); + desktop.setAboutHandler(_ -> showPreferencesWindow(SelectedPreferencesTab.ABOUT)); } // register app reopen listener if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) { - desktop.addAppEventListener((AppReopenedListener) e -> showMainWindow()); + desktop.addAppEventListener((AppReopenedListener) _ -> showMainWindow()); } // observe visible windows @@ -135,11 +140,12 @@ public class FxApplicationWindows { } public CompletionStage showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) { - return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors); + return showMainWindow().thenApplyAsync(_ -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater) // + .whenComplete(this::reportErrors); } public void showQuitWindow(QuitResponse response, boolean forced) { - CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater); + CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response, forced), Platform::runLater); } public void showUpdateReminderWindow() { @@ -147,13 +153,14 @@ public class FxApplicationWindows { } public void showDokanySupportEndWindow() { - CompletableFuture.runAsync(() -> dialogs.prepareDokanySupportEndDialog( - mainWindow.get().window(), - stage -> { - showPreferencesWindow(SelectedPreferencesTab.VOLUME); - stage.close(); - } - ).build().showAndWait(), Platform::runLater); + CompletableFuture.runAsync(() -> createDokanySupportEndDialog().showAndWait(), Platform::runLater); + } + + private SimpleDialog createDokanySupportEndDialog() { + return dialogs.prepareDokanySupportEndDialog(mainWindow.get().window(), stage -> { + showPreferencesWindow(SelectedPreferencesTab.VOLUME); + stage.close(); + }).build(); } public CompletionStage startUnlockWorkflow(Vault vault, @Nullable Stage owner) { @@ -162,8 +169,7 @@ public class FxApplicationWindows { LOG.debug("Start unlock workflow for {}", vault.getDisplayName()); return unlockWorkflowFactory.create(vault, owner).unlockWorkflow(); }, Platform::runLater) // - .thenAcceptAsync(UnlockWorkflow::run, executor) - .exceptionally(e -> { + .thenAcceptAsync(UnlockWorkflow::run, executor).exceptionally(e -> { showErrorWindow(e, owner == null ? primaryStage : owner, null); return null; }); @@ -182,6 +188,11 @@ public class FxApplicationWindows { }); } + + public CompletionStage showEventViewer() { + return CompletableFuture.supplyAsync(() -> eventViewWindow.get().showEventViewerWindow(), Platform::runLater).whenComplete(this::reportErrors); + } + /** * Displays the generic error scene in the given window. * @@ -199,5 +210,4 @@ public class FxApplicationWindows { LOG.error("Failed to display stage", error); } } - } diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxFSEventList.java b/src/main/java/org/cryptomator/ui/fxapp/FxFSEventList.java new file mode 100644 index 000000000..e9e574b95 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/fxapp/FxFSEventList.java @@ -0,0 +1,67 @@ +package org.cryptomator.ui.fxapp; + +import org.cryptomator.event.FSEventBucket; +import org.cryptomator.event.FSEventBucketContent; +import org.cryptomator.event.FileSystemEventAggregator; + +import javax.inject.Inject; +import javafx.application.Platform; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * List of all occurred filesystem events. + *

+ * The list exposes an observable list and a property to listen for updates. Internally it polls the {@link FileSystemEventAggregator} in a regular interval for updates. + * If an update is available, the list from the {@link FileSystemEventAggregator } is cloned to this list on the FX application thread. + */ +@FxApplicationScoped +public class FxFSEventList { + + private final ObservableList> events; + private final FileSystemEventAggregator eventAggregator; + private final ScheduledExecutorService scheduler; + private final BooleanProperty unreadEvents; + + @Inject + public FxFSEventList(FileSystemEventAggregator fsEventAggregator, ScheduledExecutorService scheduler) { + this.events = FXCollections.observableArrayList(); + this.eventAggregator = fsEventAggregator; + this.scheduler = scheduler; + this.unreadEvents = new SimpleBooleanProperty(false); + } + + public void schedulePollForUpdates() { + scheduler.schedule(this::checkForEventUpdates, 1000, TimeUnit.MILLISECONDS); + } + + /** + * Checks for event updates and reschedules. + * If updates are available, the aggregated events are copied from back- to the frontend. + * Reschedules itself on successful execution + */ + private void checkForEventUpdates() { + if (eventAggregator.hasMaybeUpdates()) { + Platform.runLater(() -> { + eventAggregator.cloneTo(events); + unreadEvents.setValue(true); + schedulePollForUpdates(); + }); + } else { + schedulePollForUpdates(); + } + } + + public ObservableList> getObservableList() { + return events; + } + + public BooleanProperty unreadEventsProperty() { + return unreadEvents; + } +} diff --git a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java index f3f0aff8e..b9af0f4a3 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java +++ b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java @@ -3,6 +3,8 @@ package org.cryptomator.ui.keyloading; import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; +import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy; +import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +30,33 @@ public interface KeyLoadingStrategy extends MasterkeyLoader { @Override Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException; + /** + * Determines whether the provided key loader scheme corresponds to a Hub Vault. + *

+ * This method compares the {@code keyLoader} parameter with the known Hub Vault schemes + * {@link HubKeyLoadingStrategy#SCHEME_HUB_HTTP} and {@link HubKeyLoadingStrategy#SCHEME_HUB_HTTPS}. + * + * @param keyLoader A string representing the key loader scheme to be checked. + * @return {@code true} if the given key loader scheme represents a Hub Vault; {@code false} otherwise. + */ + static boolean isHubVault(String keyLoader) { + return HubKeyLoadingStrategy.SCHEME_HUB_HTTP.equals(keyLoader) || HubKeyLoadingStrategy.SCHEME_HUB_HTTPS.equals(keyLoader); + } + + /** + * Determines whether the provided key loader scheme corresponds to a Masterkey File Vault. + *

+ * This method checks if the {@code keyLoader} parameter matches the known Masterkey File Vault scheme + * {@link MasterkeyFileLoadingStrategy#SCHEME}. + *

+ * + * @param keyLoader A string representing the key loader scheme to be checked. + * @return {@code true} if the given key loader scheme represents a Masterkey File Vault; {@code false} otherwise. + */ + static boolean isMasterkeyFileVault(String keyLoader) { + return MasterkeyFileLoadingStrategy.SCHEME.equals(keyLoader); + } + /** * Allows the loader to try and recover from an exception thrown during the last attempt. * diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java index aa56a22e7..f94d882fa 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java @@ -165,6 +165,7 @@ public class ReceiveKeyController implements FxController { var vaultKeyUri = hubConfig.URIs.API.resolve("vaults/" + vaultId + "/access-token"); var request = HttpRequest.newBuilder(vaultKeyUri) // .header("Authorization", "Bearer " + bearerToken) // + .header("Hub-Device-ID", deviceId) // .GET() // .timeout(REQ_TIMEOUT) // .build(); diff --git a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java index 68877430a..a13f3e223 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java +++ b/src/main/java/org/cryptomator/ui/keyloading/masterkeyfile/MasterkeyFileLoadingStrategy.java @@ -112,12 +112,12 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy { } private void savePasswordToSystemkeychain(Passphrase passphrase) { - if (keychain.isSupported()) { - try { + try { + if (keychain.isSupported() && !keychain.getPassphraseStoredProperty(vault.getId()).get()) { keychain.storePassphrase(vault.getId(), vault.getDisplayName(), passphrase); - } catch (KeychainAccessException e) { - LOG.error("Failed to store passphrase in system keychain.", e); } + } catch (KeychainAccessException e) { + LOG.error("Failed to store passphrase in system keychain.", e); } } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java index 999ff7882..c6e084518 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowController.java @@ -1,13 +1,5 @@ package org.cryptomator.ui.mainwindow; -import javafx.beans.Observable; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ReadOnlyBooleanProperty; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.fxml.FXML; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.LicenseHolder; import org.cryptomator.common.settings.Settings; @@ -21,6 +13,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javafx.beans.Observable; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.fxml.FXML; +import javafx.geometry.Rectangle2D; +import javafx.scene.layout.StackPane; +import javafx.stage.Screen; +import javafx.stage.Stage; +import javafx.stage.WindowEvent; @MainWindowScoped public class MainWindowController implements FxController { @@ -63,27 +66,66 @@ public class MainWindowController implements FxController { } window.focusedProperty().addListener(this::mainWindowFocusChanged); - if (!neverTouched()) { - window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight()); - window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth()); - window.setX(settings.windowXPosition.get()); - window.setY(settings.windowYPosition.get()); + int x = settings.windowXPosition.get(); + int y = settings.windowYPosition.get(); + int width = settings.windowWidth.get(); + int height = settings.windowHeight.get(); + if (windowPositionSaved(x, y, width, height)) { + window.setX(x); + window.setY(y); + window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth())); + window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight())); } - window.widthProperty().addListener((_, _, _) -> savePositionalSettings()); - window.heightProperty().addListener((_, _, _) -> savePositionalSettings()); - window.xProperty().addListener((_, _, _) -> savePositionalSettings()); - window.yProperty().addListener((_, _, _) -> savePositionalSettings()); + + window.setOnShowing(this::checkDisplayBounds); + + settings.windowXPosition.bind(window.xProperty()); + settings.windowYPosition.bind(window.yProperty()); + settings.windowWidth.bind(window.widthProperty()); + settings.windowHeight.bind(window.heightProperty()); } - private boolean neverTouched() { - return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0); + private boolean windowPositionSaved(int x, int y, int width, int height) { + return x != 0 || y != 0 || width != 0 || height != 0; } - public void savePositionalSettings() { - settings.windowWidth.setValue(window.getWidth()); - settings.windowHeight.setValue(window.getHeight()); - settings.windowXPosition.setValue(window.getX()); - settings.windowYPosition.setValue(window.getY()); + private void checkDisplayBounds(WindowEvent windowEvent) { + int x = settings.windowXPosition.get(); + int y = settings.windowYPosition.get(); + int width = settings.windowWidth.get(); + int height = settings.windowHeight.get(); + + Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds(); + if (!isWithinDisplayBounds(x, y, width, height)) { //use stored window position + LOG.debug("Resetting window position due to insufficient screen overlap"); + var centeredX = (primaryScreenBounds.getWidth() - window.getMinWidth()) / 2; + var centeredY = (primaryScreenBounds.getHeight() - window.getMinHeight()) / 2; + //check if we can keep width and height + if (isWithinDisplayBounds((int) centeredX, (int) centeredY, width, height)) { + //if so, keep window size + window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth())); + window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight())); + } + //reset position of upper left corner + window.setX(centeredX); + window.setY(centeredY); + } + } + + private boolean isWithinDisplayBounds(int x, int y, int width, int height) { + // define a rect which is inset on all sides from the window's rect: + final int shrinkedX = x + 20; // 20px left + final int shrinkedY = y + 5; // 5px top + final int shrinkedWidth = width - 40; // 20px left + 20px right + final int shrinkedHeigth = height - 25; // 5px top + 20px bottom + return isRectangleWithinBounds(shrinkedX, shrinkedY, 0, shrinkedHeigth) // Left pixel column + && isRectangleWithinBounds(shrinkedX + shrinkedWidth, shrinkedY, 0, shrinkedHeigth) // Right pixel column + && isRectangleWithinBounds(shrinkedX, shrinkedY, shrinkedWidth, 0) // Top pixel row + && isRectangleWithinBounds(shrinkedX, shrinkedY + shrinkedHeigth, shrinkedWidth, 0); // Bottom pixel row + } + + private boolean isRectangleWithinBounds(int x, int y, int width, int height) { + return !Screen.getScreensForRectangle(x, y, width, height).isEmpty(); } private void mainWindowFocusChanged(Observable observable) { @@ -124,7 +166,7 @@ public class MainWindowController implements FxController { return updateAvailable.get(); } - public BooleanBinding licenseValidProperty(){ + public BooleanBinding licenseValidProperty() { return licenseHolder.validLicenseProperty(); } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java index b80804f2e..fa1b441d9 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/MainWindowModule.java @@ -1,6 +1,7 @@ package org.cryptomator.ui.mainwindow; import dagger.Binds; +import dagger.Lazy; import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoMap; @@ -14,9 +15,11 @@ import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.StageFactory; import org.cryptomator.ui.common.StageInitializer; import org.cryptomator.ui.error.ErrorComponent; +import org.cryptomator.ui.fxapp.FxApplicationTerminator; import org.cryptomator.ui.fxapp.PrimaryStage; import org.cryptomator.ui.migration.MigrationComponent; import org.cryptomator.ui.stats.VaultStatisticsComponent; +import org.cryptomator.ui.traymenu.TrayMenuComponent; import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent; import javax.inject.Named; @@ -35,11 +38,19 @@ abstract class MainWindowModule { @Provides @MainWindow @MainWindowScoped - static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) { + static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer, FxApplicationTerminator terminator, Lazy trayMenu) { initializer.accept(stage); stage.setTitle("Cryptomator"); stage.setMinWidth(650); stage.setMinHeight(498); + stage.setOnCloseRequest(e -> { + if (!trayMenu.get().isInitialized()) { + terminator.terminate(); + e.consume(); + } else { + stage.close(); + } + }); return stage; } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java index 8212f598f..6a7c046f2 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java @@ -8,9 +8,9 @@ import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; import javax.inject.Inject; +import javafx.beans.binding.Bindings; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.stage.Stage; @@ -21,7 +21,6 @@ public class VaultDetailLockedController implements FxController { private final ReadOnlyObjectProperty vault; private final FxApplicationWindows appWindows; private final VaultOptionsComponent.Factory vaultOptionsWindow; - private final KeychainManager keychain; private final Stage mainWindow; private final ObservableValue passwordSaved; @@ -30,13 +29,11 @@ public class VaultDetailLockedController implements FxController { this.vault = vault; this.appWindows = appWindows; this.vaultOptionsWindow = vaultOptionsWindow; - this.keychain = keychain; this.mainWindow = mainWindow; - if (keychain.isSupported() && !keychain.isLocked()) { - this.passwordSaved = vault.flatMap(v -> keychain.getPassphraseStoredProperty(v.getId())).orElse(false); - } else { - this.passwordSaved = new SimpleBooleanProperty(false); - } + this.passwordSaved = Bindings.createBooleanBinding(() -> { + var v = vault.get(); + return v != null && keychain.getPassphraseStoredProperty(v.getId()).getValue(); + }, vault, keychain.getKeychainImplementation()); } @FXML diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java index ef6148c87..42a8fda7e 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java @@ -6,12 +6,14 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.tobiasdiez.easybind.EasyBind; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.Nullable; import org.cryptomator.common.vaults.Vault; import org.cryptomator.integrations.mount.Mountpoint; import org.cryptomator.integrations.revealpath.RevealFailedException; import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.VaultService; +import org.cryptomator.ui.decryptname.DecryptNameComponent; import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.stats.VaultStatisticsComponent; import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent; @@ -39,10 +41,13 @@ import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.ResourceBundle; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; @MainWindowScoped public class VaultDetailUnlockedController implements FxController { @@ -56,26 +61,39 @@ public class VaultDetailUnlockedController implements FxController { private final WrongFileAlertComponent.Builder wrongFileAlert; private final Stage mainWindow; private final Optional revealPathService; + private final DecryptNameComponent.Factory decryptNameWindowFactory; private final ResourceBundle resourceBundle; private final LoadingCache vaultStats; private final VaultStatisticsComponent.Builder vaultStatsBuilder; private final ObservableValue accessibleViaPath; private final ObservableValue accessibleViaUri; private final ObservableValue mountPoint; - private final BooleanProperty draggingOver = new SimpleBooleanProperty(); + private final BooleanProperty draggingOverLocateEncrypted = new SimpleBooleanProperty(); + private final BooleanProperty draggingOverDecryptName = new SimpleBooleanProperty(); private final BooleanProperty ciphertextPathsCopied = new SimpleBooleanProperty(); - //FXML - public Button dropZone; + @FXML + public Button revealEncryptedDropZone; + @FXML + public Button decryptNameDropZone; @Inject - public VaultDetailUnlockedController(ObjectProperty vault, FxApplicationWindows appWindows, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, WrongFileAlertComponent.Builder wrongFileAlert, @MainWindow Stage mainWindow, Optional revealPathService, ResourceBundle resourceBundle) { + public VaultDetailUnlockedController(ObjectProperty vault, // + FxApplicationWindows appWindows, // + VaultService vaultService, // + VaultStatisticsComponent.Builder vaultStatsBuilder, // + WrongFileAlertComponent.Builder wrongFileAlert, // + @MainWindow Stage mainWindow, // + Optional revealPathService, // + DecryptNameComponent.Factory decryptNameWindowFactory, // + ResourceBundle resourceBundle) { this.vault = vault; this.appWindows = appWindows; this.vaultService = vaultService; this.wrongFileAlert = wrongFileAlert; this.mainWindow = mainWindow; this.revealPathService = revealPathService; + this.decryptNameWindowFactory = decryptNameWindowFactory; this.resourceBundle = resourceBundle; this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats)); this.vaultStatsBuilder = vaultStatsBuilder; @@ -92,89 +110,81 @@ public class VaultDetailUnlockedController implements FxController { } public void initialize() { - dropZone.setOnDragEntered(this::handleDragEvent); - dropZone.setOnDragOver(this::handleDragEvent); - dropZone.setOnDragDropped(this::handleDragEvent); - dropZone.setOnDragExited(this::handleDragEvent); + revealEncryptedDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverLocateEncrypted)); + revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealOrCopyPaths)); + revealEncryptedDropZone.setOnDragExited(_ -> draggingOverLocateEncrypted.setValue(false)); - EasyBind.includeWhen(dropZone.getStyleClass(), ACTIVE_CLASS, draggingOver); + decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName)); + decryptNameDropZone.setOnDragDropped(e -> showDecryptNameWindow(e.getDragboard().getFiles().stream().map(File::toPath).toList())); + decryptNameDropZone.setOnDragExited(_ -> draggingOverDecryptName.setValue(false)); + + EasyBind.includeWhen(revealEncryptedDropZone.getStyleClass(), ACTIVE_CLASS, draggingOverLocateEncrypted); + EasyBind.includeWhen(decryptNameDropZone.getStyleClass(), ACTIVE_CLASS, draggingOverDecryptName); } - private void handleDragEvent(DragEvent event) { - if (DragEvent.DRAG_OVER.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) { - if(SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC) { + private void handleDragOver(DragEvent event, BooleanProperty prop) { + if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { + if (SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC) { event.acceptTransferModes(TransferMode.LINK); } else { event.acceptTransferModes(TransferMode.ANY); } - draggingOver.set(true); - } else if (DragEvent.DRAG_DROPPED.equals(event.getEventType()) && event.getGestureSource() == null && event.getDragboard().hasFiles()) { - List ciphertextPaths = event.getDragboard().getFiles().stream().map(File::toPath).map(this::getCiphertextPath).flatMap(Optional::stream).toList(); - if (ciphertextPaths.isEmpty()) { - wrongFileAlert.build().showWrongFileAlertWindow(); - } else { - revealOrCopyPaths(ciphertextPaths); - } - event.setDropCompleted(!ciphertextPaths.isEmpty()); - event.consume(); - } else if (DragEvent.DRAG_EXITED.equals(event.getEventType())) { - draggingOver.set(false); + prop.set(true); } } - private VaultStatisticsComponent buildVaultStats(Vault vault) { - return vaultStatsBuilder.vault(vault).build(); + private void handleDragDropped(DragEvent event, Function computation, Consumer> positiveAction) { + if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { + List objects = event.getDragboard().getFiles().stream().map(File::toPath).map(computation).filter(Objects::nonNull).toList(); + if (objects.isEmpty()) { + wrongFileAlert.build().showWrongFileAlertWindow(); + } else { + positiveAction.accept(objects); + } + event.setDropCompleted(!objects.isEmpty()); + event.consume(); + } } @FXML - public void revealAccessLocation() { - vaultService.reveal(vault.get()); - } - - @FXML - public void copyMountUri() { - ClipboardContent clipboardContent = new ClipboardContent(); - clipboardContent.putString(mountPoint.getValue()); - Clipboard.getSystemClipboard().setContent(clipboardContent); - } - - @FXML - public void lock() { - appWindows.startLockWorkflow(vault.get(), mainWindow); - } - - @FXML - public void showVaultStatistics() { - vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow(); - } - - @FXML - public void chooseFileAndReveal() { + public void chooseDecryptedFileAndReveal() { Preconditions.checkState(accessibleViaPath.getValue()); var fileChooser = new FileChooser(); - fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.filePickerTitle")); + fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.locateEncrypted.filePickerTitle")); fileChooser.setInitialDirectory(Path.of(mountPoint.getValue()).toFile()); var cleartextFile = fileChooser.showOpenDialog(mainWindow); if (cleartextFile != null) { - var ciphertextPaths = getCiphertextPath(cleartextFile.toPath()).stream().toList(); - revealOrCopyPaths(ciphertextPaths); + var ciphertextPath = getCiphertextPath(cleartextFile.toPath()); + if (ciphertextPath != null) { + revealOrCopyPaths(List.of(ciphertextPath)); + } } } + @FXML + public void showDecryptNameWindow() { + showDecryptNameWindow(List.of()); + } + + private void showDecryptNameWindow(List pathsToDecrypt) { + decryptNameWindowFactory.create(vault.get(), mainWindow, pathsToDecrypt).showDecryptFileNameWindow(); + } + private boolean startsWithVaultAccessPoint(Path path) { return path.startsWith(Path.of(mountPoint.getValue())); } - private Optional getCiphertextPath(Path path) { + @Nullable + private Path getCiphertextPath(Path path) { if (!startsWithVaultAccessPoint(path)) { - LOG.debug("Path does not start with access point of selected vault: {}", path); - return Optional.empty(); + LOG.debug("Path does not start with mount point of selected vault: {}", path); + return null; } try { - return Optional.of(vault.get().getCiphertextPath(path)); + return vault.get().getCiphertextPath(path); } catch (IOException e) { LOG.warn("Unable to get ciphertext path from path: {}", path, e); - return Optional.empty(); + return null; } } @@ -206,6 +216,32 @@ public class VaultDetailUnlockedController implements FxController { }); } + private VaultStatisticsComponent buildVaultStats(Vault vault) { + return vaultStatsBuilder.vault(vault).build(); + } + + @FXML + public void revealAccessLocation() { + vaultService.reveal(vault.get()); + } + + @FXML + public void copyMountUri() { + ClipboardContent clipboardContent = new ClipboardContent(); + clipboardContent.putString(mountPoint.getValue()); + Clipboard.getSystemClipboard().setContent(clipboardContent); + } + + @FXML + public void lock() { + appWindows.startLockWorkflow(vault.get(), mainWindow); + } + + @FXML + public void showVaultStatistics() { + vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow(); + } + /* Getter/Setter */ public ReadOnlyObjectProperty vaultProperty() { @@ -247,4 +283,6 @@ public class VaultDetailUnlockedController implements FxController { public boolean isCiphertextPathsCopied() { return ciphertextPathsCopied.get(); } + } + diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java index e353fe8ed..a457ade3f 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java @@ -10,6 +10,7 @@ import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.VaultService; import org.cryptomator.ui.dialogs.Dialogs; +import org.cryptomator.ui.fxapp.FxFSEventList; import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.preferences.SelectedPreferencesTab; import org.slf4j.Logger; @@ -26,6 +27,7 @@ import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.geometry.Side; +import javafx.scene.control.Button; import javafx.scene.control.ContextMenu; import javafx.scene.control.ListView; import javafx.scene.input.ContextMenuEvent; @@ -34,7 +36,6 @@ import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; -import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import java.io.File; @@ -66,6 +67,7 @@ public class VaultListController implements FxController { private final VaultListCellFactory cellFactory; private final AddVaultWizardComponent.Builder addVaultWizard; private final BooleanBinding emptyVaultList; + private final BooleanProperty unreadEvents; private final VaultListManager vaultListManager; private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty(); private final ResourceBundle resourceBundle; @@ -76,7 +78,7 @@ public class VaultListController implements FxController { public ListView vaultList; public StackPane root; @FXML - private HBox addVaultButton; + private Button addVaultButton; @FXML private ContextMenu addVaultContextMenu; @@ -91,7 +93,8 @@ public class VaultListController implements FxController { ResourceBundle resourceBundle, // FxApplicationWindows appWindows, // Settings settings, // - Dialogs dialogs) { + Dialogs dialogs, // + FxFSEventList fxFSEventList) { this.mainWindow = mainWindow; this.vaults = vaults; this.selectedVault = selectedVault; @@ -104,6 +107,7 @@ public class VaultListController implements FxController { this.dialogs = dialogs; this.emptyVaultList = Bindings.isEmpty(vaults); + this.unreadEvents = fxFSEventList.unreadEventsProperty(); selectedVault.addListener(this::selectedVaultDidChange); cellSize = settings.compactMode.map(compact -> compact ? 30.0 : 60.0); @@ -263,6 +267,10 @@ public class VaultListController implements FxController { appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY); } + @FXML + public void showEventViewer() { + appWindows.showEventViewer(); + } // Getter and Setter public BooleanBinding emptyVaultListProperty() { @@ -289,4 +297,11 @@ public class VaultListController implements FxController { return cellSize.getValue(); } + public ObservableValue unreadEventsPresentProperty() { + return unreadEvents; + } + + public boolean getUnreadEventsPresent() { + return unreadEvents.getValue(); + } } diff --git a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java index 800a292a9..4505f412d 100644 --- a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java @@ -1,10 +1,13 @@ package org.cryptomator.ui.preferences; +import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Environment; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.settings.Settings; import org.cryptomator.integrations.autostart.AutoStartProvider; import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException; import org.cryptomator.integrations.common.NamedServiceProvider; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.cryptomator.integrations.quickaccess.QuickAccessService; import org.cryptomator.ui.common.FxController; @@ -14,6 +17,7 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.application.Application; +import javafx.beans.Observable; import javafx.beans.binding.Bindings; import javafx.fxml.FXML; import javafx.scene.control.CheckBox; @@ -23,6 +27,10 @@ import javafx.stage.Stage; import javafx.util.StringConverter; import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutorService; +import java.util.stream.Collectors; @PreferencesScoped public class GeneralPreferencesController implements FxController { @@ -36,6 +44,8 @@ public class GeneralPreferencesController implements FxController { private final Application application; private final Environment environment; private final List keychainAccessProviders; + private final KeychainManager keychain; + private final ExecutorService backgroundExecutor; private final FxApplicationWindows appWindows; public CheckBox useKeychainCheckbox; public ChoiceBox keychainBackendChoiceBox; @@ -47,12 +57,18 @@ public class GeneralPreferencesController implements FxController { public CheckBox autoStartCheckbox; public ToggleGroup nodeOrientation; + private CompletionStage keychainMigrations = CompletableFuture.completedFuture(null); + @Inject - GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional autoStartProvider, List keychainAccessProviders, Application application, Environment environment, FxApplicationWindows appWindows) { + GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional autoStartProvider, // + List keychainAccessProviders, KeychainManager keychain, Application application, // + Environment environment, FxApplicationWindows appWindows, ExecutorService backgroundExecutor) { this.window = window; this.settings = settings; this.autoStartProvider = autoStartProvider; this.keychainAccessProviders = keychainAccessProviders; + this.keychain = keychain; + this.backgroundExecutor = backgroundExecutor; this.quickAccessServices = QuickAccessService.get().toList(); this.application = application; this.environment = environment; @@ -73,6 +89,7 @@ public class GeneralPreferencesController implements FxController { Bindings.bindBidirectional(settings.keychainProvider, keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter); useKeychainCheckbox.selectedProperty().bindBidirectional(settings.useKeychain); keychainBackendChoiceBox.disableProperty().bind(useKeychainCheckbox.selectedProperty().not()); + keychainBackendChoiceBox.valueProperty().addListener(this::migrateKeychainEntries); useQuickAccessCheckbox.selectedProperty().bindBidirectional(settings.useQuickAccess); var quickAccessSettingsConverter = new ServiceToSettingsConverter<>(quickAccessServices); @@ -83,6 +100,25 @@ public class GeneralPreferencesController implements FxController { quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not()); } + private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) { + //currently, we only migrate on macOS (touchID vs regular keychain) + if (SystemUtils.IS_OS_MAC) { + var idsAndNames = settings.directories.stream().collect(Collectors.toMap(vs -> vs.id, vs -> vs.displayName.getValue())); + if (!idsAndNames.isEmpty()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Migrating keychain entries {} from {} to {}", idsAndNames.keySet(), oldProvider.displayName(), newProvider.displayName()); + } + keychainMigrations = keychainMigrations.thenRunAsync(() -> { + try { + KeychainManager.migrate(oldProvider, newProvider, idsAndNames); + } catch (KeychainAccessException e) { + LOG.warn("Failed to migrate all entries from {} to {}", oldProvider.displayName(), newProvider.displayName(), e); + } + }, backgroundExecutor); + } + } + } + public boolean isAutoStartSupported() { return autoStartProvider.isPresent(); } diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index afa05cc8c..f5a72290f 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -19,6 +19,8 @@ import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.scene.control.CheckBox; import javafx.scene.control.ContentDisplay; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; @@ -32,7 +34,10 @@ import java.util.ResourceBundle; @PreferencesScoped public class UpdatesPreferencesController implements FxController { - private static final String DOWNLOADS_URI = "https://cryptomator.org/downloads"; + private static final String DOWNLOADS_URI_TEMPLATE = "https://cryptomator.org/downloads/" // + + "?utm_source=cryptomator-desktop" // + + "&utm_medium=update-notification&" // + + "utm_campaign=app-update-%s"; private final Application application; private final Environment environment; @@ -50,6 +55,7 @@ public class UpdatesPreferencesController implements FxController { private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false); private final DateTimeFormatter formatter; private final BooleanBinding upToDate; + private final String downloadsUri; /* FXML */ public CheckBox checkForUpdatesCheckbox; @@ -65,12 +71,13 @@ public class UpdatesPreferencesController implements FxController { this.latestVersion = updateChecker.latestVersionProperty(); this.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty(); this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck); - this.currentVersion = updateChecker.getCurrentVersion(); + this.currentVersion = environment.getAppVersion(); this.updateAvailable = updateChecker.updateAvailableProperty(); this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault()); this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion)); this.checkFailed = updateChecker.checkFailedProperty(); this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck); + this.downloadsUri = DOWNLOADS_URI_TEMPLATE.formatted(URLEncoder.encode(currentVersion, StandardCharsets.US_ASCII)); } public void initialize() { @@ -93,7 +100,7 @@ public class UpdatesPreferencesController implements FxController { @FXML public void visitDownloadsPage() { - application.getHostServices().showDocument(DOWNLOADS_URI); + application.getHostServices().showDocument(downloadsUri); } @FXML diff --git a/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java b/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java index 63230cbbf..859c9e38d 100644 --- a/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java +++ b/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java @@ -3,7 +3,7 @@ package org.cryptomator.ui.sharevault; import dagger.Lazy; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy; +import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import javax.inject.Inject; import javafx.application.Application; @@ -33,8 +33,7 @@ public class ShareVaultController implements FxController { this.window = window; this.application = application; this.vault = vault; - var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme(); - this.hubVault = (vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS)); + this.hubVault = KeyLoadingStrategy.isHubVault(vault.getVaultSettings().lastKnownKeyLoader.get()); } @FXML diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsController.java b/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsController.java index 78d228995..154eb30dd 100644 --- a/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsController.java +++ b/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsController.java @@ -3,6 +3,7 @@ package org.cryptomator.ui.vaultoptions; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy; import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy; import org.slf4j.Logger; @@ -42,11 +43,11 @@ public class VaultOptionsController implements FxController { window.setOnShowing(this::windowWillAppear); selectedTabProperty.addListener(observable -> this.selectChosenTab()); tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged()); - var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme(); - if(!vaultScheme.equals(MasterkeyFileLoadingStrategy.SCHEME)){ + var vaultKeyLoader = vault.getVaultSettings().lastKnownKeyLoader.get(); + if(!KeyLoadingStrategy.isMasterkeyFileVault(vaultKeyLoader)){ tabPane.getTabs().remove(keyTab); } - if(!(vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS))){ + if(!KeyLoadingStrategy.isHubVault(vaultKeyLoader)){ tabPane.getTabs().remove(hubTab); } diff --git a/src/main/resources/css/dark_theme.css b/src/main/resources/css/dark_theme.css index dae0d1898..fff15e834 100644 --- a/src/main/resources/css/dark_theme.css +++ b/src/main/resources/css/dark_theme.css @@ -16,6 +16,10 @@ src: url('opensans_bold.ttf'); } +@font-face { + src: url('firacode_regular.ttf'); +} + /******************************************************************************* * * * Root Styling & Colors * @@ -125,6 +129,12 @@ -fx-fill: TEXT_FILL; } +.cryptic-text { + -fx-fill: TEXT_FILL; + -fx-font-family: 'Fira Code'; + -fx-font-size: 1.1em; +} + /******************************************************************************* * * * Glyph Icons * @@ -183,19 +193,37 @@ } .main-window .button-bar { + -fx-min-height:42px; + -fx-max-height:42px; -fx-background-color: MAIN_BG; -fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent; -fx-border-width: 1px 0 0 0; } -.main-window .button-left { +.main-window .button-bar .button-left { -fx-border-color: CONTROL_BORDER_NORMAL; -fx-border-width: 0 1px 0 0; + -fx-background-color: MAIN_BG; + -fx-background-radius: 0px; + -fx-min-height: 42px; + -fx-max-height: 42px; } -.main-window .button-right { +.main-window .button-bar .button-right { -fx-border-color: CONTROL_BORDER_NORMAL; -fx-border-width: 0 0 0 1px; + -fx-background-color: MAIN_BG; + -fx-background-radius: 0px; + -fx-min-height: 42px; + -fx-max-height: 42px; +} + +.main-window .button-bar .button-left:armed { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED; +} + +.main-window .button-bar .button-right:armed { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED; } /******************************************************************************* @@ -284,6 +312,10 @@ -fx-font-size: 1.0em; } +.list-cell .header-misc { + -fx-font-size: 1.0em; +} + .list-cell .detail-label { -fx-text-fill: TEXT_FILL_MUTED; -fx-font-size: 0.8em; @@ -312,6 +344,42 @@ -fx-fill: transparent; } +/******************************************************************************* + * * + * Event List * + * * + ******************************************************************************/ + +.event-window .button-bar { + -fx-min-height:42px; + -fx-max-height:42px; + -fx-background-color: MAIN_BG; + -fx-border-color: transparent transparent CONTROL_BORDER_NORMAL transparent; + -fx-border-width: 0 0 1px 0; +} + +.event-window .button-bar .button-right { + -fx-border-color: transparent transparent transparent CONTROL_BORDER_NORMAL; + -fx-border-width: 0 0 0 1px; + -fx-background-color: MAIN_BG; + -fx-background-radius: 0px; + -fx-min-height: 42px; + -fx-max-height: 42px; +} + +.event-window .button-bar .button-right:armed { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED; +} + +.event-window .list-view .list-cell:hover { + -fx-background-color: CONTROL_BG_SELECTED; +} + +.event-window .list-view .list-cell:selected { + -fx-background-color: PRIMARY, CONTROL_BG_SELECTED; + -fx-background-insets: 0, 0 0 0 3px; +} + /******************************************************************************* * * * NotificationBar * @@ -341,6 +409,12 @@ -fx-background-color: PRIMARY; } +.notification-debug:hover .notification-label, +.notification-update:hover .notification-label, +.notification-support:hover .notification-label { + -fx-underline:true; +} + /******************************************************************************* * * * ScrollBar * @@ -575,6 +649,16 @@ -fx-graphic-text-gap: 9px; } +/******************************************************************************* + * * + * Update indicator + * * + ******************************************************************************/ + +.icon-update-indicator { + -fx-fill: RED_5; +} + /******************************************************************************* * * * Hyperlinks * @@ -938,4 +1022,167 @@ -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL; -fx-background-insets: 0, 1px; -fx-background-radius: 4px; -} \ No newline at end of file +} + +/******************************************************************************* + * * + * Decrypt Name Window + * * + ******************************************************************************/ + +.decrypt-name-window .button-bar { + -fx-min-height:42px; + -fx-max-height:42px; + -fx-background-color: MAIN_BG; + -fx-border-color: transparent transparent CONTROL_BORDER_NORMAL transparent; + -fx-border-width: 0 0 1px 0; +} + +.decrypt-name-window .button-bar .button-right { + -fx-border-color: transparent transparent transparent CONTROL_BORDER_NORMAL; + -fx-border-width: 0 0 0 1px; + -fx-background-color: MAIN_BG; + -fx-background-radius: 0px; + -fx-min-height: 42px; + -fx-max-height: 42px; +} + +.decrypt-name-window .button-bar .button-right:armed { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED; +} + +.decrypt-name-window .table-view { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL; + -fx-background-insets: 0,1; + /* There is some oddness if padding is in em values rather than pixels, + in particular, the left border of the control doesn't show. */ + -fx-padding: 1; /* 0.083333em; */ +} + +.table-view > .placeholder { + -fx-background-color: transparent; + -fx-background-radius: 0px; +} + +.table-view > .placeholder > .button { + -fx-border-width: 0; + -fx-border-color: transparent; + -fx-background-radius: 0px; +} + +.table-view:focused { + -fx-background-color: CONTROL_BORDER_FOCUSED, CONTROL_BG_NORMAL; + -fx-background-insets: 0, 1; + -fx-background-radius: 0, 0; + /* There is some oddness if padding is in em values rather than pixels, + in particular, the left border of the control doesn't show. */ + -fx-padding: 1; /* 0.083333em; */ +} + +.table-view > .virtual-flow > .scroll-bar:vertical { + -fx-background-insets: 0, 0 0 0 1; + -fx-padding: -1 -1 -1 0; +} + +.table-view > .virtual-flow > .corner { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL ; + -fx-background-insets: 0, 1 0 0 1; +} + +/* Each row in the table is a table-row-cell. Inside a table-row-cell is any + number of table-cell. */ +.table-row-cell { + -fx-background-color: GRAY_3, CONTROL_BG_NORMAL; + -fx-background-insets: 0, 0 0 1 0; + -fx-padding: 0.0em; /* 0 */ + -fx-text-fill: TEXT_FILL; +} + +.table-row-cell:odd { + -fx-background-color: GRAY_3, GRAY_1; + -fx-background-insets: 0, 0 0 1 0; +} + +.table-cell { + -fx-padding: 3px 6px 3px 6px; + -fx-background-color: transparent; + -fx-border-color: transparent CONTROL_BORDER_NORMAL transparent transparent; + -fx-border-width: 1px; + -fx-cell-size: 30px; + -fx-text-fill: TEXT_FILL; + -fx-text-overrun: center-ellipsis; +} + +.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected > .table-cell { + -fx-text-fill: TEXT_FILL; +} + +/* selected, hover - not specified */ + +/* selected, focused, hover */ +/* selected, focused */ +/* selected */ +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:selected, +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused:selected, +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused:selected:hover { + -fx-background-color: CONTROL_PRIMARY_BG_NORMAL, PRIMARY_D1; + -fx-background-insets: 0 0 0 0, 1 1 1 3; + -fx-text-fill: TEXT_FILL; +} +/* focused */ +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused { + -fx-background-color: CONTROL_PRIMARY_BORDER_FOCUSED, CONTROL_PRIMARY_BG_NORMAL , CONTROL_BG_NORMAL; + -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2; + -fx-text-fill: TEXT_FILL; +} +/* focused, hover */ +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused:hover { + -fx-background-color: CONTROL_PRIMARY_BORDER_FOCUSED, CONTROL_PRIMARY_BG_NORMAL , PRIMARY_D2; + -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2; + -fx-text-fill: TEXT_FILL; +} +/* hover */ +.table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:hover { + -fx-background-color: PRIMARY_D2; + -fx-text-fill: TEXT_FILL; + -fx-background-insets: 0 0 1 0; +} + +/* The column-resize-line is shown when the user is attempting to resize a column. */ +.table-view .column-resize-line { + -fx-background-color: CONTROL_BG_ARMED; + -fx-padding: 0.0em 0.0416667em 0.0em 0.0416667em; /* 0 0.571429 0 0.571429 */ +} + +/* This is the area behind the column headers. An ideal place to specify background + and border colors for the whole area (not individual column-header's). */ +.table-view .column-header-background { + -fx-background-color: GRAY_2; + -fx-padding: 0; +} + +/* The column header row is made up of a number of column-header, one for each + TableColumn, and a 'filler' area that extends from the right-most column + to the edge of the tableview, or up to the 'column control' button. */ +.table-view .column-header { + -fx-text-fill: TEXT_FILL; + -fx-font-size: 1.083333em; /* 13pt ; 1 more than the default font */ + -fx-size: 24; + -fx-border-style: solid; + -fx-border-color: + transparent + GRAY_3 + GRAY_3 + transparent; + -fx-border-insets: 0 0 0 0; + -fx-border-width: 0.083333em; +} + +.table-view .column-header .label { + -fx-alignment: center; +} + +.table-view .empty-table { + -fx-background-color: MAIN_BG; + -fx-font-size: 1.166667em; /* 14pt - 2 more than the default font */ +} diff --git a/src/main/resources/css/firacode_regular.ttf b/src/main/resources/css/firacode_regular.ttf new file mode 100644 index 000000000..b8a44d2db Binary files /dev/null and b/src/main/resources/css/firacode_regular.ttf differ diff --git a/src/main/resources/css/light_theme.css b/src/main/resources/css/light_theme.css index 84df05b10..39e2892ac 100644 --- a/src/main/resources/css/light_theme.css +++ b/src/main/resources/css/light_theme.css @@ -16,6 +16,10 @@ src: url('opensans_bold.ttf'); } +@font-face { + src: url('firacode_regular.ttf'); +} + /******************************************************************************* * * * Root Styling & Colors * @@ -79,6 +83,7 @@ PROGRESS_INDICATOR_END: GRAY_4; PROGRESS_BAR_BG: GRAY_8; + -fx-background-color: MAIN_BG; -fx-text-fill: TEXT_FILL; -fx-font-family: 'Open Sans'; @@ -124,6 +129,12 @@ -fx-fill: TEXT_FILL; } +.cryptic-text { + -fx-fill: TEXT_FILL; + -fx-font-family: 'Fira Code'; + -fx-font-size: 1.1em; +} + /******************************************************************************* * * * Glyph Icons * @@ -182,6 +193,8 @@ } .main-window .button-bar { + -fx-min-height:42px; + -fx-max-height:42px; -fx-background-color: MAIN_BG; -fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent; -fx-border-width: 1px 0 0 0; @@ -190,11 +203,27 @@ .main-window .button-bar .button-left { -fx-border-color: CONTROL_BORDER_NORMAL; -fx-border-width: 0 1px 0 0; + -fx-background-color: MAIN_BG; + -fx-background-radius: 0px; + -fx-min-height: 42px; + -fx-max-height: 42px; } .main-window .button-bar .button-right { -fx-border-color: CONTROL_BORDER_NORMAL; -fx-border-width: 0 0 0 1px; + -fx-background-color: MAIN_BG; + -fx-background-radius: 0px; + -fx-min-height: 42px; + -fx-max-height: 42px; +} + +.main-window .button-bar .button-left:armed { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED; +} + +.main-window .button-bar .button-right:armed { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED; } /******************************************************************************* @@ -283,6 +312,10 @@ -fx-font-size: 1.0em; } +.list-cell .header-misc { + -fx-font-size: 1.0em; +} + .list-cell .detail-label { -fx-text-fill: TEXT_FILL_MUTED; -fx-font-size: 0.8em; @@ -311,6 +344,42 @@ -fx-fill: transparent; } +/******************************************************************************* + * * + * Event List * + * * + ******************************************************************************/ + +.event-window .button-bar { + -fx-min-height:42px; + -fx-max-height:42px; + -fx-background-color: MAIN_BG; + -fx-border-color: transparent transparent CONTROL_BORDER_NORMAL transparent; + -fx-border-width: 0 0 1px 0; +} + +.event-window .button-bar .button-right { + -fx-border-color: transparent transparent transparent CONTROL_BORDER_NORMAL; + -fx-border-width: 0 0 0 1px; + -fx-background-color: MAIN_BG; + -fx-background-radius: 0px; + -fx-min-height: 42px; + -fx-max-height: 42px; +} + +.event-window .button-bar .button-right:armed { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED; +} + +.event-window .list-view .list-cell:hover { + -fx-background-color: CONTROL_BG_SELECTED; +} + +.event-window .list-view .list-cell:selected { + -fx-background-color: PRIMARY, CONTROL_BG_SELECTED; + -fx-background-insets: 0, 0 0 0 3px; +} + /******************************************************************************* * * * NotificationBar * @@ -340,6 +409,12 @@ -fx-background-color: PRIMARY; } +.notification-debug:hover .notification-label, +.notification-update:hover .notification-label, +.notification-support:hover .notification-label { + -fx-underline:true; +} + /******************************************************************************* * * * ScrollBar * @@ -574,6 +649,16 @@ -fx-graphic-text-gap: 9px; } +/******************************************************************************* + * * + * Update indicator + * * + ******************************************************************************/ + +.icon-update-indicator { + -fx-fill: RED_5; +} + /******************************************************************************* * * * Hyperlinks * @@ -786,11 +871,11 @@ /******************************************************************************* * * - * Add Vault - MenuItem * + * Dropdown button context menu * * ******************************************************************************/ -.add-vault-menu-item { +.dropdown-button-context-menu-item { -fx-padding: 4px 8px; } @@ -937,4 +1022,167 @@ -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL; -fx-background-insets: 0, 1px; -fx-background-radius: 4px; -} \ No newline at end of file +} + +/******************************************************************************* + * * + * Decrypt Name Window + * * + ******************************************************************************/ + +.decrypt-name-window .button-bar { + -fx-min-height:42px; + -fx-max-height:42px; + -fx-background-color: MAIN_BG; + -fx-border-color: transparent transparent CONTROL_BORDER_NORMAL transparent; + -fx-border-width: 0 0 1px 0; +} + +.decrypt-name-window .button-bar .button-right { + -fx-border-color: transparent transparent transparent CONTROL_BORDER_NORMAL; + -fx-border-width: 0 0 0 1px; + -fx-background-color: MAIN_BG; + -fx-background-radius: 0px; + -fx-min-height: 42px; + -fx-max-height: 42px; +} + +.decrypt-name-window .button-bar .button-right:armed { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED; +} + +.decrypt-name-window .table-view { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL; + -fx-background-insets: 0,1; + /* There is some oddness if padding is in em values rather than pixels, + in particular, the left border of the control doesn't show. */ + -fx-padding: 1; /* 0.083333em; */ +} + +.table-view > .placeholder { + -fx-background-color: transparent; + -fx-background-radius: 0px; +} + +.table-view > .placeholder > .button { + -fx-border-width: 0; + -fx-border-color: transparent; + -fx-background-radius: 0px; +} + +.table-view:focused { + -fx-background-color: CONTROL_BORDER_FOCUSED, CONTROL_BG_NORMAL; + -fx-background-insets: 0, 1; + -fx-background-radius: 0, 0; + /* There is some oddness if padding is in em values rather than pixels, + in particular, the left border of the control doesn't show. */ + -fx-padding: 1; /* 0.083333em; */ +} + +.table-view > .virtual-flow > .scroll-bar:vertical { + -fx-background-insets: 0, 0 0 0 1; + -fx-padding: -1 -1 -1 0; +} + +.table-view > .virtual-flow > .corner { + -fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL ; + -fx-background-insets: 0, 1 0 0 1; +} + +/* Each row in the table is a table-row-cell. Inside a table-row-cell is any + number of table-cell. */ +.table-row-cell { + -fx-background-color: GRAY_6, CONTROL_BG_NORMAL; + -fx-background-insets: 0, 0 0 1 0; + -fx-padding: 0.0em; /* 0 */ + -fx-text-fill: TEXT_FILL; +} + +.table-row-cell:odd { + -fx-background-color: GRAY_6, GRAY_9; + -fx-background-insets: 0, 0 0 1 0; +} + +.table-cell { + -fx-padding: 3px 6px 3px 6px; + -fx-background-color: transparent; + -fx-border-color: transparent CONTROL_BORDER_NORMAL transparent transparent; + -fx-border-width: 1px; + -fx-cell-size: 30px; + -fx-text-fill: TEXT_FILL; + -fx-text-overrun: center-ellipsis; +} + +.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected > .table-cell { + -fx-text-fill: TEXT_FILL; +} + +/* selected, hover - not specified */ + +/* selected, focused, hover */ +/* selected, focused */ +/* selected */ +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:selected, +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused:selected, +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused:selected:hover { + -fx-background-color: CONTROL_PRIMARY_BG_NORMAL, CONTROL_BG_SELECTED; + -fx-background-insets: 0 0 0 0, 1 1 1 3; + -fx-text-fill: TEXT_FILL; +} +/* focused */ +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused { + -fx-background-color: CONTROL_PRIMARY_BORDER_FOCUSED, CONTROL_PRIMARY_BG_NORMAL , CONTROL_BG_NORMAL; + -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2; + -fx-text-fill: TEXT_FILL; +} +/* focused, hover */ +.table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused:hover { + -fx-background-color: CONTROL_PRIMARY_BORDER_FOCUSED, CONTROL_PRIMARY_BG_NORMAL , PRIMARY_L2; + -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2; + -fx-text-fill: TEXT_FILL; +} +/* hover */ +.table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:hover { + -fx-background-color: PRIMARY_L2; + -fx-text-fill: TEXT_FILL; + -fx-background-insets: 0 0 1 0; +} + +/* The column-resize-line is shown when the user is attempting to resize a column. */ +.table-view .column-resize-line { + -fx-background-color: CONTROL_BG_ARMED; + -fx-padding: 0.0em 0.0416667em 0.0em 0.0416667em; /* 0 0.571429 0 0.571429 */ +} + +/* This is the area behind the column headers. An ideal place to specify background + and border colors for the whole area (not individual column-header's). */ +.table-view .column-header-background { + -fx-background-color: GRAY_7; + -fx-padding: 0; +} + +/* The column header row is made up of a number of column-header, one for each + TableColumn, and a 'filler' area that extends from the right-most column + to the edge of the tableview, or up to the 'column control' button. */ +.table-view .column-header { + -fx-text-fill: TEXT_FILL; + -fx-font-size: 1.083333em; /* 13pt ; 1 more than the default font */ + -fx-size: 24; + -fx-border-style: solid; + -fx-border-color: + CONTROL_BORDER_NORMAL + GRAY_5 + GRAY_5 + transparent; + -fx-border-insets: 0 0 0 0; + -fx-border-width: 0.083333em; +} + +.table-view .column-header .label { + -fx-alignment: center; +} + +.table-view .empty-table { + -fx-background-color: MAIN_BG; + -fx-font-size: 1.166667em; /* 14pt - 2 more than the default font */ +} diff --git a/src/main/resources/fxml/decryptnames.fxml b/src/main/resources/fxml/decryptnames.fxml new file mode 100644 index 000000000..04e16b8e1 --- /dev/null +++ b/src/main/resources/fxml/decryptnames.fxml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/eventview.fxml b/src/main/resources/fxml/eventview.fxml new file mode 100644 index 000000000..3aeb17281 --- /dev/null +++ b/src/main/resources/fxml/eventview.fxml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/eventview_cell.fxml b/src/main/resources/fxml/eventview_cell.fxml new file mode 100644 index 000000000..dc8a679f7 --- /dev/null +++ b/src/main/resources/fxml/eventview_cell.fxml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/simple_dialog.fxml b/src/main/resources/fxml/simple_dialog.fxml index 32ad63abf..0e9b01776 100644 --- a/src/main/resources/fxml/simple_dialog.fxml +++ b/src/main/resources/fxml/simple_dialog.fxml @@ -1,16 +1,16 @@ - - - - - - - - - + + + + + + + + + - - - - + + + - - - - - - - + + + + + + + - + - + - + - + diff --git a/src/main/resources/fxml/vault_list_cell.fxml b/src/main/resources/fxml/vault_list_cell.fxml index 20df9b924..11bfd5927 100644 --- a/src/main/resources/fxml/vault_list_cell.fxml +++ b/src/main/resources/fxml/vault_list_cell.fxml @@ -14,17 +14,15 @@ spacing="12" alignment="CENTER_LEFT"> - - - - - - - + + + + + diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index b03c202bf..5f7024849 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -2,6 +2,7 @@ additionalStyleSheets= # Generics +generic.action.dismiss=Dismiss ## Button generic.button.apply=Apply generic.button.back=Back @@ -107,7 +108,6 @@ addvaultwizard.success.unlockNow=Unlock Now removeVault.title=Remove "%s" removeVault.message=Remove vault? removeVault.description=This will only make Cryptomator forget about this vault. You can add it again. No encrypted files will be deleted from your hard drive. -removeVault.confirmBtn=Remove Vault # Change Password changepassword.title=Change Password @@ -284,7 +284,7 @@ preferences.title=Preferences ## General preferences.general=General preferences.general.startHidden=Hide window when starting Cryptomator -preferences.general.autoCloseVaults=Lock open vaults automatically when quitting application +preferences.general.autoCloseVaults=Lock vaults without asking when quitting application preferences.general.debugLogging=Enable debug logging preferences.general.debugDirectory=Reveal log files preferences.general.autoStart=Launch Cryptomator on system start @@ -396,6 +396,7 @@ main.vaultlist.contextMenu.vaultoptions=Show Vault Options main.vaultlist.contextMenu.reveal=Reveal Drive main.vaultlist.addVaultBtn.menuItemNew=Create New Vault... main.vaultlist.addVaultBtn.menuItemExisting=Open Existing Vault... +main.vaultlist.showEventsButton.tooltip=Open event view ##Notificaition main.notification.updateAvailable=Update is available. main.notification.support=Support Cryptomator. @@ -424,7 +425,9 @@ main.vaultDetail.stats=Vault Statistics main.vaultDetail.locateEncryptedFileBtn=Locate Encrypted File main.vaultDetail.locateEncryptedFileBtn.tooltip=Choose a file from your vault to locate its encrypted counterpart main.vaultDetail.encryptedPathsCopied=Paths Copied to Clipboard! -main.vaultDetail.filePickerTitle=Select File Inside Vault +main.vaultDetail.locateEncrypted.filePickerTitle=Select File Inside Vault +main.vaultDetail.decryptName.buttonLabel=Decrypt File Name +main.vaultDetail.decryptName.tooltip=Choose an encrypted vault file to decrypt its name ### Missing main.vaultDetail.missing.info=Cryptomator could not find a vault at this path. main.vaultDetail.missing.recheck=Recheck @@ -578,4 +581,43 @@ shareVault.hub.message=How to share a Hub vault shareVault.hub.description=In order to share the vault content with another team member, you have to perform two steps: shareVault.hub.instruction.1=1. Share access of the encrypted vault folder via cloud storage. shareVault.hub.instruction.2=2. Grant access to team member in Cryptomator Hub. -shareVault.hub.openHub=Open Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Open Cryptomator Hub + +# Decrypt File Names +decryptNames.title=Decrypt File Names +decryptNames.filePicker.title=Select encrypted file +decryptNames.filePicker.extensionDescription=Cryptomator encrypted file +decryptNames.copyTable.tooltip=Copy table +decryptNames.clearTable.tooltip=Clear table +decryptNames.copyHint=Copy cell content with %s +decryptNames.dropZone.message=Drop files or click to select +decryptNames.dropZone.error.vaultInternalFiles=Vault internal files with no decrypt-able name selected +decryptNames.dropZone.error.foreignFiles=Files do not belong to vault "%s" +decryptNames.dropZone.error.noDirIdBackup=Directory of selected files does not contain dirId.c9r file +decryptNames.dropZone.error.generic=Failed to decrypt file names + + +# Event View +eventView.title=Events +eventView.filter.allVaults=All +eventView.clearListButton.tooltip=Clear list +## event list entries +eventView.entry.vaultLocked.description=Unlock "%s" for details +eventView.entry.conflictResolved.message=Resolved conflict +eventView.entry.conflictResolved.showDecrypted=Show decrypted file +eventView.entry.conflictResolved.copyDecrypted=Copy decrypted path +eventView.entry.conflict.message=Conflict resolution failed +eventView.entry.conflict.showDecrypted=Show decrypted, original file +eventView.entry.conflict.copyDecrypted=Copy decrypted, original path +eventView.entry.conflict.showEncrypted=Show conflicting, encrypted file +eventView.entry.conflict.copyEncrypted=Copy conflicting, encrypted path +eventView.entry.decryptionFailed.message=Decryption failed +eventView.entry.decryptionFailed.showEncrypted=Show encrypted file +eventView.entry.decryptionFailed.copyEncrypted=Copy encrypted path +eventView.entry.brokenDirFile.message=Broken directory link +eventView.entry.brokenDirFile.showEncrypted=Show broken, encrypted link +eventView.entry.brokenDirFile.copyEncrypted=Copy path of broken link +eventView.entry.brokenFileNode.message=Broken filesystem node +eventView.entry.brokenFileNode.showEncrypted=Show broken, encrypted node +eventView.entry.brokenFileNode.copyEncrypted=Copy path of broken, encrypted node +eventView.entry.brokenFileNode.copyDecrypted=Copy decrypted path diff --git a/src/main/resources/i18n/strings_af.properties b/src/main/resources/i18n/strings_af.properties index 16d2016cd..aa86df2bd 100644 --- a/src/main/resources/i18n/strings_af.properties +++ b/src/main/resources/i18n/strings_af.properties @@ -121,3 +121,10 @@ #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_ar.properties b/src/main/resources/i18n/strings_ar.properties index 1739b866d..5faff677f 100644 --- a/src/main/resources/i18n/strings_ar.properties +++ b/src/main/resources/i18n/strings_ar.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=تجاهل ## Button generic.button.apply=تطبيق generic.button.back=رجوع @@ -176,6 +177,7 @@ hub.registerFailed.description.generic=حدث خطأ في عملية تسجيل hub.registerFailed.description.deviceAlreadyExists=هذا الجهاز مسجل لمستخدم مختلف بالفعل. حاول تغيير حساب المستخدم أو استخدام جهاز مختلف. ### Unauthorized hub.unauthorized.message=تم رفض الوصول +hub.unauthorized.description=غير مسموح لك بفتح هذا المستودع. اتصل بمالك المستودع لطلب الوصول. ### Requires Account Initialization hub.requireAccountInit.message=مطلوب اتخاذ إجراء hub.requireAccountInit.description.0=للمتابعة، يرجى إكمال الخطوات المطلوبة في @@ -281,7 +283,7 @@ preferences.title=تفضيلات ## General preferences.general=عام preferences.general.startHidden=إخفاء النافذة عند بدء تشغيل Cryptomator -preferences.general.autoCloseVaults=اقفل الخزانات المفتوحة تلقائياً عند الإقلاع عن التطبيق +preferences.general.autoCloseVaults=اقفل المخازن دون السؤال عند الإقلاع عن التطبيق preferences.general.debugLogging=تمكين سجلات التصحيح preferences.general.debugDirectory=عرض ملفات السجل preferences.general.autoStart=تشغيل Cryptomator عند بدء تشغيل النظام @@ -421,7 +423,6 @@ main.vaultDetail.stats=إحصائيات الخزنة main.vaultDetail.locateEncryptedFileBtn=تحديد موقع الملف المشفر main.vaultDetail.locateEncryptedFileBtn.tooltip=اختر ملف من خزانتك لتحديد مكان نظيره المشفر main.vaultDetail.encryptedPathsCopied=تم نسخ مسارات الملفات إلى الحافظة! -main.vaultDetail.filePickerTitle=إختر الملف من الخزنة ### Missing main.vaultDetail.missing.info=لم يتمكن Cryptomator من العثور على خزنة في هذا المسار. main.vaultDetail.missing.recheck=إعادة الفحص @@ -552,6 +553,10 @@ dokanySupportEnd.description=نوع وحدة التخزين Dokany لم يعد dokanySupportEnd.preferencesBtn=فتح التفضيلات #Retry If Readonly +retryIfReadonly.title=الوصول إلى المخزن المقيّد +retryIfReadonly.message=لا يوجد وصول للكتابة إلى مجلد المخزن +retryIfReadonly.description=Cryptomator لا يمكنه الكتابة إلى دليل المخزن. يمكنك تغيير المخزن ليكون للقراءة فقط وحاول مرة أخرى. يمكن تعطيل هذا الخيار في خيارات المخزن. +retryIfReadonly.retry=غيّر وأعد المحاولة # Share Vault shareVault.title=مشاركة الخزانة @@ -571,4 +576,10 @@ shareVault.hub.message=كيفية مشاركة خزانة Hub shareVault.hub.description=لمشاركة محتوى الخزانة مع عضو آخر في الفريق، عليك القيام بخطوتين: shareVault.hub.instruction.1=1. شارك الوصول إلى مجلد الخزانة المشفر عبر التخزين السحابي. shareVault.hub.instruction.2=2. امنح الوصول لعضو الفريق في Cryptomator Hub. -shareVault.hub.openHub=زيارة Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=زيارة Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_ba.properties b/src/main/resources/i18n/strings_ba.properties index f71aa0917..a4020caa5 100644 --- a/src/main/resources/i18n/strings_ba.properties +++ b/src/main/resources/i18n/strings_ba.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Кире ҡаҡ ## Button generic.button.apply=Ҡуллан generic.button.back=Артҡа @@ -269,7 +270,6 @@ preferences.title=Көйләүҙәр ## General preferences.general=Дөйөм preferences.general.startHidden=Cryptomator башланған саҡта тәҙрәне йәшерергә -preferences.general.autoCloseVaults=Ҡушымтанан сыҡҡан ваҡытта һаҡлағыстарҙы автоматик рәүештә бикләргә preferences.general.debugLogging=Төҙөкләндереү журналын асырға preferences.general.debugDirectory=Журнал файлдарын күрһәт preferences.general.autoStart=Система стартында Cryptomator-ҙы эшләтеп ебәрергә @@ -389,7 +389,6 @@ main.vaultDetail.stats=Һаҡлағыс статистикаһы main.vaultDetail.locateEncryptedFileBtn=Шифрланған файлды тап main.vaultDetail.locateEncryptedFileBtn.tooltip=Шифрланған аналогын табыр өсөн һаҡлағыстан файл һайлағыҙ main.vaultDetail.encryptedPathsCopied=Юлдарҙын Clipboard-ҡа күсермәһе алынды! -main.vaultDetail.filePickerTitle=Һаҡлағыс эсендә файл һайлау ### Missing main.vaultDetail.missing.info=Cryptomator был юлдан һаҡлағыс таба алманы. main.vaultDetail.missing.recheck=Яңынан тикшер @@ -518,3 +517,10 @@ dokanySupportEnd.preferencesBtn=Көйләүҙәрҙе ас #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_be.properties b/src/main/resources/i18n/strings_be.properties index 87de4f257..49794305e 100644 --- a/src/main/resources/i18n/strings_be.properties +++ b/src/main/resources/i18n/strings_be.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Адхіліць ## Button generic.button.apply=Ужыць generic.button.back=Назад @@ -258,7 +259,6 @@ preferences.title=Налады ## General preferences.general=Агульныя preferences.general.startHidden=Хаваць акно пры запуску Cryptomator -preferences.general.autoCloseVaults=Замыкаць адчыненыя скарбніцы аўтаматычна пры выхадзе з праграмы preferences.general.debugLogging=Уключыць пратакаляванне адладкі preferences.general.debugDirectory=Паказаць файлы пратаколу preferences.general.autoStart=Запускаць Cryptomator падчас запуску сістэмы @@ -376,7 +376,6 @@ main.vaultDetail.stats=Статыстыка скарбніцы main.vaultDetail.locateEncryptedFileBtn=Знайсці зашыфраваны файл main.vaultDetail.locateEncryptedFileBtn.tooltip=Абяры файл у тваёй скрабніцы, каб знайсці ягоны зашыфраваны адпаведнік main.vaultDetail.encryptedPathsCopied=Шлях скапіяваны ў буфер абмену! -main.vaultDetail.filePickerTitle=Абраць файл унутры скарбніцы ### Missing main.vaultDetail.missing.info=Cryptomator ня змог знайсці скарбніцу па гэтай сцежцы. main.vaultDetail.missing.recheck=Пераправерыць @@ -498,3 +497,10 @@ dokanySupportEnd.preferencesBtn=Адчыніць налады #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_bg.properties b/src/main/resources/i18n/strings_bg.properties index 2817fb65d..1f366aa3b 100644 --- a/src/main/resources/i18n/strings_bg.properties +++ b/src/main/resources/i18n/strings_bg.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Отхвърляне ## Button generic.button.apply=Прилагане generic.button.back=Назад @@ -269,11 +270,11 @@ preferences.title=Настройки ## General preferences.general=Общи preferences.general.startHidden=Скриване на прозореца при отваряне на Криптоматор -preferences.general.autoCloseVaults=Заключване на хранилищата при затваряне на приложението preferences.general.debugLogging=Дневник за отстраняване на дефекти preferences.general.debugDirectory=Файлове на дневниците preferences.general.autoStart=Отваряне на Криптоматор при старт на системата preferences.general.keychainBackend=Съхраняванеа паролите в +preferences.general.quickAccessService=Добавяне на отключените хранилища в зоната за бърз достъп ## Interface preferences.interface=Външен вид preferences.interface.theme=Тема @@ -389,7 +390,6 @@ main.vaultDetail.stats=Статистики на хранилището main.vaultDetail.locateEncryptedFileBtn=Намиране на шифровани файлове main.vaultDetail.locateEncryptedFileBtn.tooltip=Изберете файл от хранилището, за да бъде намерено шифрованото му копие main.vaultDetail.encryptedPathsCopied=Пътищата са копирани! -main.vaultDetail.filePickerTitle=Изберете файл от хранилището ### Missing main.vaultDetail.missing.info=Криптоматор не намира хранилище на това място. main.vaultDetail.missing.recheck=Повторен опит @@ -518,3 +518,10 @@ dokanySupportEnd.preferencesBtn=Към настройките #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_bn.properties b/src/main/resources/i18n/strings_bn.properties index 7fb8a46df..1e06f4043 100644 --- a/src/main/resources/i18n/strings_bn.properties +++ b/src/main/resources/i18n/strings_bn.properties @@ -185,3 +185,10 @@ vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন ক #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_bs.properties b/src/main/resources/i18n/strings_bs.properties index 9955f00a7..7ccd204a5 100644 --- a/src/main/resources/i18n/strings_bs.properties +++ b/src/main/resources/i18n/strings_bs.properties @@ -321,3 +321,10 @@ quit.lockAndQuitBtn=Zaključaj i zatvori #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_ca.properties b/src/main/resources/i18n/strings_ca.properties index 61471bff8..64978397e 100644 --- a/src/main/resources/i18n/strings_ca.properties +++ b/src/main/resources/i18n/strings_ca.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Descartar ## Button generic.button.apply=Aplica generic.button.back=Enrere @@ -176,6 +177,7 @@ hub.registerFailed.description.generic=S'ha produït un error en el procés de r hub.registerFailed.description.deviceAlreadyExists=El dispositiu ja ha estat registrat per un altre usuari. Mireu de canviar el compte d'usuari o feu servir un dispositiu diferent. ### Unauthorized hub.unauthorized.message=Accés denegat +hub.unauthorized.description=No estàs autoritzat a obrir aquesta caixa forta. Contacta amb el seu propietari per obtenir accés. ### Requires Account Initialization hub.requireAccountInit.message=Acció necessària hub.requireAccountInit.description.0=Per a continuar, si us plau, seguiu els passos necessaris en el vostre @@ -281,7 +283,7 @@ preferences.title=Preferències ## General preferences.general=General preferences.general.startHidden=Amaga la finestra quan s'inicia Cryptomator -preferences.general.autoCloseVaults=Bloquejar les caixes fortes automàticament quan surti de l'aplicació +preferences.general.autoCloseVaults=Bloca les caixes fortes sense preguntar-ho en sortir de l'aplicació preferences.general.debugLogging=Habilita el registre de depuració preferences.general.debugDirectory=Mostra els fitxers de registres preferences.general.autoStart=Executa Cryptomator en engegar el sistema @@ -340,6 +342,7 @@ preferences.contribute.sponsor=Patrocinador ### Remove License Key Dialog removeCert.title=Suprimeix certificat +removeCert.message=Eliminar certificat de col·laborador? removeCert.description=Les característiques principals de Cryptomator no es veuen afectades per això. Ni l'accés a les vostres caixes fortes s'ha restringit ni el nivell de seguretat ha estat rebaixat. #<-- Add entries for donations and code/translation/documentation contribution --> @@ -420,7 +423,6 @@ main.vaultDetail.stats=Estadístiques de la caixa forta main.vaultDetail.locateEncryptedFileBtn=Trobar fitxer xifrat main.vaultDetail.locateEncryptedFileBtn.tooltip=Esculli un fitxer de la caixa forta per trobar el seu homòleg xifrat main.vaultDetail.encryptedPathsCopied=Rutes copiades al porta-retalls! -main.vaultDetail.filePickerTitle=Seleccioni un fitxer dins la caixa forta ### Missing main.vaultDetail.missing.info=Cryptomator no ha trobat una caixa forta en aquesta ruta. main.vaultDetail.missing.recheck=Torna a comprovar @@ -545,6 +547,9 @@ updateReminder.yesOnce=Sí, una vegada updateReminder.yesAutomatically=Sí, automàticament #Dokany Support End +dokanySupportEnd.title=Avís d'obsolescència +dokanySupportEnd.message=Fi de la compatibilitat amb Dokany +dokanySupportEnd.description=El tipus de volum Dokany ja no és compatible amb Cryptomator. La teva configuració canviarà al tipus de volum predeterminat. Pots veure el tipus de volum predeterminat en la configuració. dokanySupportEnd.preferencesBtn=Obrir les Preferències #Retry If Readonly @@ -567,4 +572,10 @@ shareVault.hub.message=Com compartir una caixa forta al Hub shareVault.hub.description=Per tal de compartir el contingut de la caixa forta amb un altre membre de l'equip, heu de seguir dos passos: shareVault.hub.instruction.1=1. Compartiu l'accés a la carpeta via emmagatzematge en el núvol. shareVault.hub.instruction.2=2. Doneu accés al membre de l'equip a Cryptomator Hub. -shareVault.hub.openHub=Obre Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Obre Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_cs.properties b/src/main/resources/i18n/strings_cs.properties index a5d210dec..248e1a529 100644 --- a/src/main/resources/i18n/strings_cs.properties +++ b/src/main/resources/i18n/strings_cs.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Zrušit ## Button generic.button.apply=Použít generic.button.back=Zpět @@ -272,7 +273,6 @@ preferences.title=Nastavení ## General preferences.general=Obecné preferences.general.startHidden=Skrýt okno Cryptomatoru při spuštění -preferences.general.autoCloseVaults=Zamknout trezory automaticky při ukončení aplikace preferences.general.debugLogging=Ladicí režim preferences.general.debugDirectory=Ukázat soubory se záznamy událostí (log) preferences.general.autoStart=Spustit Cryptomator při spuštění systému @@ -390,7 +390,6 @@ main.vaultDetail.stats=Statistiky trezoru main.vaultDetail.locateEncryptedFileBtn=Najít šifrovaný soubor main.vaultDetail.locateEncryptedFileBtn.tooltip=Vyberte soubor z vašeho trezoru, abyste našli jeho šifrovaný protějšek main.vaultDetail.encryptedPathsCopied=Cesta souboru byla zkopírována do schránky! -main.vaultDetail.filePickerTitle=Vyberte soubor uvnitř trezoru ### Missing main.vaultDetail.missing.info=Cryptomator nemohl najít trezor na této cestě. main.vaultDetail.missing.recheck=Znovu zkontrolovat @@ -534,4 +533,10 @@ shareVault.hub.message=Jak sdílet Hub trezor shareVault.hub.description=Chcete-li sdílet obsah trezoru s jiným členem týmu, musíte provést dva kroky: shareVault.hub.instruction.1=1. Sdílejte přístup ke šifrované složce trezoru prostřednictvím cloudového úložiště. shareVault.hub.instruction.2=2. Udělte přístup členovi týmu v Cryptomator Hubu. -shareVault.hub.openHub=Otevřít Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Otevřít Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_da.properties b/src/main/resources/i18n/strings_da.properties index 0399e5c95..0ffdd6658 100644 --- a/src/main/resources/i18n/strings_da.properties +++ b/src/main/resources/i18n/strings_da.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Luk ## Button generic.button.apply=Anvend generic.button.back=Tilbage @@ -282,7 +283,6 @@ preferences.title=Præferencer ## General preferences.general=Generelt preferences.general.startHidden=Skjul vinduet når Cryptomator starter -preferences.general.autoCloseVaults=Lås åbne bokse automatisk, når programmet afsluttes preferences.general.debugLogging=Aktivér fejllogning preferences.general.debugDirectory=Vis logfiler preferences.general.autoStart=Start Cryptomator automatisk ved opstart @@ -407,7 +407,6 @@ main.vaultDetail.stats=Boks statistik main.vaultDetail.locateEncryptedFileBtn=Find Krypteret Fil main.vaultDetail.locateEncryptedFileBtn.tooltip=Vælg en fil fra din boks for at finde dens krypterede modpart main.vaultDetail.encryptedPathsCopied=Stier kopieret! -main.vaultDetail.filePickerTitle=Vælg fil inde i boks ### Missing main.vaultDetail.missing.info=Cryptomator kunne ikke finde en boks på denne sti. main.vaultDetail.missing.recheck=Kontrollér igen @@ -554,4 +553,10 @@ shareVault.hub.message=Sådan deler du en Hub boks shareVault.hub.description=For at dele indholdet i boksen med et andet holdmedlem skal du udføre to trin: shareVault.hub.instruction.1=1. Del adgang til den krypterede boks-mappe vha. opbevaring i skyen. shareVault.hub.instruction.2=2. Giv adgang til holdmedlem i Cryptomator Hub. -shareVault.hub.openHub=Åben Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Åben Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_de.properties b/src/main/resources/i18n/strings_de.properties index bd328c0a1..e190914d2 100644 --- a/src/main/resources/i18n/strings_de.properties +++ b/src/main/resources/i18n/strings_de.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Verwerfen ## Button generic.button.apply=Übernehmen generic.button.back=Zurück @@ -176,6 +177,7 @@ hub.registerFailed.description.generic=Im Registrierungsprozess ist ein Fehler a hub.registerFailed.description.deviceAlreadyExists=Dieses Gerät ist bereits für einen anderen Benutzer registriert. Ändere das Benutzerkonto oder verwende ein anderes Gerät. ### Unauthorized hub.unauthorized.message=Zugriff verweigert +hub.unauthorized.description=Du bist nicht berechtigt, diesen Tresor zu öffnen. Wende dich an den Tresoreigentümer, um Zugriff zu erhalten. ### Requires Account Initialization hub.requireAccountInit.message=Aktion erforderlich hub.requireAccountInit.description.0=Um fortzufahren, führe bitte die erforderlichen Schritte in deinem @@ -393,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=Tresoroptionen anzeigen main.vaultlist.contextMenu.reveal=Laufwerk anzeigen main.vaultlist.addVaultBtn.menuItemNew=Neuen Tresor erstellen... main.vaultlist.addVaultBtn.menuItemExisting=Bestehenden Tresor öffnen... +main.vaultlist.showEventsButton.tooltip=Ereignis-Ansicht öffnen ##Notificaition main.notification.updateAvailable=Eine neue Version ist verfügbar. main.notification.support=Unterstütze Cryptomator. @@ -421,7 +424,9 @@ main.vaultDetail.stats=Tresorstatistik main.vaultDetail.locateEncryptedFileBtn=Verschlüsselte Datei finden main.vaultDetail.locateEncryptedFileBtn.tooltip=Wähle eine Datei aus deinem Tresor aus, um das verschlüsselte Gegenstück zu finden main.vaultDetail.encryptedPathsCopied=Pfade in Zwischenablage kopiert! -main.vaultDetail.filePickerTitle=Datei im Tresor auswählen +main.vaultDetail.locateEncrypted.filePickerTitle=Datei im Tresor auswählen +main.vaultDetail.decryptName.buttonLabel=Dateiname entschlüsseln +main.vaultDetail.decryptName.tooltip=Wählen eine verschlüsselte Tresordatei, um ihren Namen zu entschlüsseln ### Missing main.vaultDetail.missing.info=Cryptomator konnte keinen Tresor mit diesem Pfad finden. main.vaultDetail.missing.recheck=Erneut prüfen @@ -552,6 +557,10 @@ dokanySupportEnd.description=Der Laufwerkstyp Dokany wird von Cryptomator nicht dokanySupportEnd.preferencesBtn=Einstellungen öffnen #Retry If Readonly +retryIfReadonly.title=Eingeschränkter Tresorzugriff +retryIfReadonly.message=Kein Schreibzugriff auf Tresor +retryIfReadonly.description=Cryptomator kann nicht in das Verzeichnis des Tresors schreiben. Du kannst den Tresor auf schreibgeschützt umstellen und es erneut versuchen. Diese Option kann in den Tresoroptionen deaktiviert werden. +retryIfReadonly.retry=Ändern und wiederholen # Share Vault shareVault.title=Tresor teilen @@ -571,4 +580,43 @@ shareVault.hub.message=Teilen eines Hub-Tresors shareVault.hub.description=Zur Freigabe des Tresorinhalts für ein anderes Teammitglied sind zwei Schritte erforderlich: shareVault.hub.instruction.1=1. Teile den Zugriff auf den verschlüsselten Tresorordner über den Cloud-Speicher. shareVault.hub.instruction.2=2. Füge das Teammitglied in Cryptomator Hub als Tresormitglied hinzu. -shareVault.hub.openHub=Cryptomator Hub öffnen \ No newline at end of file +shareVault.hub.openHub=Cryptomator Hub öffnen + +# Decrypt File Names +decryptNames.title=Dateinamen entschlüsseln +decryptNames.filePicker.title=Verschlüsselte Datei auswählen +decryptNames.filePicker.extensionDescription=Cryptomator-verschlüsselte Datei +decryptNames.copyTable.tooltip=Tabelle kopieren +decryptNames.clearTable.tooltip=Tabelle leeren +decryptNames.copyHint=Inhalt der Zelle mit %s kopieren +decryptNames.dropZone.message=Dateien ablegen oder zum Auswählen klicken +decryptNames.dropZone.error.vaultInternalFiles=Tresor interne Dateien mit nicht-entschlüsselbaren Namen ausgewählt +decryptNames.dropZone.error.foreignFiles=Dateien gehören nicht zum Tresor "%s" +decryptNames.dropZone.error.noDirIdBackup=Verzeichnis der ausgewählten Dateien enthält keine dirId.c9r Datei +decryptNames.dropZone.error.generic=Fehler beim Entschlüsseln der Dateinamen + + +# Event View +eventView.title=Ereignisse +eventView.filter.allVaults=Alle +eventView.clearListButton.tooltip=Liste leeren +## event list entries +eventView.entry.vaultLocked.description=Entsperre "%s" für Details +eventView.entry.conflictResolved.message=Konflikt gelöst +eventView.entry.conflictResolved.showDecrypted=Entschlüsselte Datei anzeigen +eventView.entry.conflictResolved.copyDecrypted=Pfad der entschlüsselten Datei kopieren +eventView.entry.conflict.message=Konfliktlösung fehlgeschlagen +eventView.entry.conflict.showDecrypted=Entschlüsselte, ursprüngliche Datei anzeigen +eventView.entry.conflict.copyDecrypted=Entschlüsselten, ursprünglicher Pfad kopieren +eventView.entry.conflict.showEncrypted=Zeige verschlüsselte Konfliktdatei +eventView.entry.conflict.copyEncrypted=Verschlüsselten Konfliktpfad kopieren +eventView.entry.decryptionFailed.message=Entschlüsselung fehlgeschlagen +eventView.entry.decryptionFailed.showEncrypted=Verschlüsselte Datei anzeigen +eventView.entry.decryptionFailed.copyEncrypted=Pfad der verschlüsselten Datei kopieren +eventView.entry.brokenDirFile.message=Ungültiger Verzeichnislink +eventView.entry.brokenDirFile.showEncrypted=Defekten, verschlüsselten Link anzeigen +eventView.entry.brokenDirFile.copyEncrypted=Pfad des ungültigen Links kopieren +eventView.entry.brokenFileNode.message=Fehlender Dateisystemknoten +eventView.entry.brokenFileNode.showEncrypted=Beschädigten, verschlüsselten Knoten anzeigen +eventView.entry.brokenFileNode.copyEncrypted=Pfad des kaputten, verschlüsselten Knotens kopieren +eventView.entry.brokenFileNode.copyDecrypted=Pfad der entschlüsselten Datei kopieren diff --git a/src/main/resources/i18n/strings_el.properties b/src/main/resources/i18n/strings_el.properties index dedb4e6ef..f8214e9f9 100644 --- a/src/main/resources/i18n/strings_el.properties +++ b/src/main/resources/i18n/strings_el.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Παράβλεψη ## Button generic.button.apply=Εφαρμογή generic.button.back=Πίσω @@ -282,7 +283,7 @@ preferences.title=Προτιμήσεις ## General preferences.general=Γενικά preferences.general.startHidden=Απόκρυψη παραθύρου όταν ξεκινά το Cryptomator -preferences.general.autoCloseVaults=Αυτόματο κλείδωμα ανοιγμένης κρύπτης κατά την έξοδο της εφαρμογής +preferences.general.autoCloseVaults=Κλείδωμα κρυπτών χωρίς ερώτηση κατά την έξοδο από την εφαρμογή preferences.general.debugLogging=Ενεργοποίηση καταγραφής σφαλμάτων preferences.general.debugDirectory=Αποκάλυψη αρχείων καταγραφής preferences.general.autoStart=Εκκίνηση Cryptomator στην εκκίνηση του συστήματος @@ -422,7 +423,6 @@ main.vaultDetail.stats=Στατιστικά Vault main.vaultDetail.locateEncryptedFileBtn=Εντοπισμός Κρυπτογραφημένου Αρχείου main.vaultDetail.locateEncryptedFileBtn.tooltip=Επιλέξτε ένα αρχείο από την κρύπτη σας για να εντοπίσετε το κρυπτογραφημένο αντίστοιχο main.vaultDetail.encryptedPathsCopied=Οι Διαδρομές Αντιγράφηκαν στο Πρόχειρο! -main.vaultDetail.filePickerTitle=Επιλογή Αρχείου Μέσα Στην Κρύπτη ### Missing main.vaultDetail.missing.info=Cryptomator δεν βρήκε vault σε αυτόν τον κατάλογο. main.vaultDetail.missing.recheck=Επανέλεγχος @@ -576,4 +576,11 @@ shareVault.hub.message=Πώς να μοιραστείτε μια κρύπτη Hu shareVault.hub.description=Για να μοιραστείτε το περιεχόμενο της κρύπτης με άλλο μέλος της ομάδας, πρέπει να εκτελέσετε δύο βήματα: shareVault.hub.instruction.1=1. Μοιραστείτε την πρόσβαση στον κρυπτογραφημένο φάκελο κρύπτης μέσω του χώρου αποθήκευσης στο cloud. shareVault.hub.instruction.2=2. Παραχωρήστε πρόσβαση σε μέλος της ομάδας στο Cryptomator Hub. -shareVault.hub.openHub=Ανοίξτε το Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Ανοίξτε το Cryptomator Hub + +# Decrypt File Names + + +# Event View +eventView.filter.allVaults=Όλες +## event list entries diff --git a/src/main/resources/i18n/strings_es.properties b/src/main/resources/i18n/strings_es.properties index 6359cc8a2..65ee6501b 100644 --- a/src/main/resources/i18n/strings_es.properties +++ b/src/main/resources/i18n/strings_es.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Descartar ## Button generic.button.apply=Aplicar generic.button.back=Volver @@ -282,7 +283,7 @@ preferences.title=Preferencias ## General preferences.general=General preferences.general.startHidden=Ocultar ventana al iniciar Cryptomator -preferences.general.autoCloseVaults=Bloquear automáticamente las bóvedas abiertas al salir de la aplicación +preferences.general.autoCloseVaults=Bloquear bóvedas sin preguntar al salir de la aplicación preferences.general.debugLogging=Habilitar registro de depuración preferences.general.debugDirectory=Revelar archivos de registro preferences.general.autoStart=Cargar Cryptomator al iniciar el sistema @@ -394,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=Mostrar opciones de la bóveda main.vaultlist.contextMenu.reveal=Revelar unidad main.vaultlist.addVaultBtn.menuItemNew=Crear Bóveda Nueva... main.vaultlist.addVaultBtn.menuItemExisting=Abrir Bóveda Existente... +main.vaultlist.showEventsButton.tooltip=Abrir vista de evento ##Notificaition main.notification.updateAvailable=Existen actualizaciones disponibles. main.notification.support=Soporte de Cryptomator. @@ -422,7 +424,9 @@ main.vaultDetail.stats=Estadísticas de la bóveda main.vaultDetail.locateEncryptedFileBtn=Ubicar archivo cifrado main.vaultDetail.locateEncryptedFileBtn.tooltip=Elija un archivo de su bóveda para ubicar su contraparte cifrada main.vaultDetail.encryptedPathsCopied=¡Rutas copiadas al portapapeles! -main.vaultDetail.filePickerTitle=Seleccionar archivo dentro de la bóveda +main.vaultDetail.locateEncrypted.filePickerTitle=Seleccionar archivo dentro de la bóveda +main.vaultDetail.decryptName.buttonLabel=Descifrar nombre de archivo +main.vaultDetail.decryptName.tooltip=Elija un archivo de bóveda cifrado para descifrar su nombre ### Missing main.vaultDetail.missing.info=Cryptomator no pudo encontrar una bóveda en esta ruta. main.vaultDetail.missing.recheck=Volver a comprobar @@ -576,4 +580,43 @@ shareVault.hub.message=Cómo compartir una bóveda de Hub shareVault.hub.description=Para compartir el contenido de la bóveda con otro miembro del equipo, tiene que realizar dos pasos: shareVault.hub.instruction.1=1. Comparta el acceso a la carpeta de la bóveda cifrada a través del almacenamiento en la nube. shareVault.hub.instruction.2=2. Conceda el acceso al miembro del equipo en Cryptomator Hub. -shareVault.hub.openHub=Abrir Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Abrir Cryptomator Hub + +# Decrypt File Names +decryptNames.title=Descifrar nombre de archivos +decryptNames.filePicker.title=Seleccione archivo cifrado +decryptNames.filePicker.extensionDescription=Archivo cifrado Cryptomator +decryptNames.copyTable.tooltip=Copiar tabla +decryptNames.clearTable.tooltip=Limpiar tabla +decryptNames.copyHint=Copiar contenido de celda con %s +decryptNames.dropZone.message=Soltar archivos o hacer clic para seleccionar +decryptNames.dropZone.error.vaultInternalFiles=Archivos internos de la bóveda sin nombre descifrable seleccionado +decryptNames.dropZone.error.foreignFiles=Los archivos no pertenecen a la bóveda "%s" +decryptNames.dropZone.error.noDirIdBackup=El directorio de los archivos seleccionados no contiene el archivo dirId.c9r +decryptNames.dropZone.error.generic=Error al descifrar nombre de archivos + + +# Event View +eventView.title=Eventos +eventView.filter.allVaults=Todos +eventView.clearListButton.tooltip=Borrar lista +## event list entries +eventView.entry.vaultLocked.description=Desbloquear "%s" para más detalles +eventView.entry.conflictResolved.message=Conflicto resuelto +eventView.entry.conflictResolved.showDecrypted=Mostrar archivo descifrado +eventView.entry.conflictResolved.copyDecrypted=Copiar ruta descifrada +eventView.entry.conflict.message=Resolución de conflictos fallida +eventView.entry.conflict.showDecrypted=Mostrar archivo descifrado, original +eventView.entry.conflict.copyDecrypted=Copiar ruta descifrada, original +eventView.entry.conflict.showEncrypted=Mostrar archivo en conflicto, cifrado +eventView.entry.conflict.copyEncrypted=Copiar ruta en conflicto, cifrada +eventView.entry.decryptionFailed.message=Desencriptación fallida +eventView.entry.decryptionFailed.showEncrypted=Mostrar archivo cifrado +eventView.entry.decryptionFailed.copyEncrypted=Copiar ruta cifrada +eventView.entry.brokenDirFile.message=Enlace de directorio roto +eventView.entry.brokenDirFile.showEncrypted=Mostrar enlace roto, cifrado +eventView.entry.brokenDirFile.copyEncrypted=Copiar ruta del enlace roto +eventView.entry.brokenFileNode.message=Nodo de sistema de archivos roto +eventView.entry.brokenFileNode.showEncrypted=Mostrar nodo roto, cifrado +eventView.entry.brokenFileNode.copyEncrypted=Copiar ruta del enlace roto, encriptado +eventView.entry.brokenFileNode.copyDecrypted=Copiar ruta descifrada diff --git a/src/main/resources/i18n/strings_fa.properties b/src/main/resources/i18n/strings_fa.properties index 6f8df4fed..d88e3ce7b 100644 --- a/src/main/resources/i18n/strings_fa.properties +++ b/src/main/resources/i18n/strings_fa.properties @@ -1,8 +1,9 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=لغو ## Button -generic.button.apply=درخواست +generic.button.apply=اعمال generic.button.back=بازگشت generic.button.cancel=انصراف generic.button.change=تغییر @@ -106,11 +107,14 @@ addvaultwizard.existing.chooseBtn=انتخاب کنید… unlock.unlockBtn=بازکردن قفل ## Select ## Success +unlock.success.revealBtn=نمایش درایو ## Failure ## Hub +hub.noKeychain.openBtn=باز کردن تنظیمات ### Waiting ### Receive Key ### Register Device +hub.register.message=دستگاه جدید ### Register Device Legacy ### Registration Success hub.registerSuccess.unlockBtn=بازکردن قفل @@ -170,11 +174,13 @@ preferences.updates.upToDate=Cryptomator به روز می باشد. # Main Window ## Vault List main.vaultlist.contextMenu.lock=قفل +main.vaultlist.contextMenu.reveal=نمایش درایو ##Notificaition ## Vault Detail ### Welcome ### Locked ### Unlocked +main.vaultDetail.revealBtn=نمایش درایو main.vaultDetail.lockBtn=قفل ### Missing ### Needs Migration @@ -185,6 +191,7 @@ main.vaultDetail.lockBtn=قفل # Vault Options ## General vaultOptions.general.vaultName=نام گاوصندوق +vaultOptions.general.actionAfterUnlock.reveal=نمایش درایو ## Mount vaultOptions.mount.mountPoint.directoryPickerButton=انتخاب کنید… @@ -209,7 +216,15 @@ vaultOptions.mount.mountPoint.directoryPickerButton=انتخاب کنید… # Update Reminder #Dokany Support End +dokanySupportEnd.preferencesBtn=باز کردن تنظیمات #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_fi.properties b/src/main/resources/i18n/strings_fi.properties index 47d17499f..ee3979bab 100644 --- a/src/main/resources/i18n/strings_fi.properties +++ b/src/main/resources/i18n/strings_fi.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Ohita ## Button generic.button.apply=Käytä generic.button.back=Takaisin @@ -281,7 +282,6 @@ preferences.title=Asetukset ## General preferences.general=Yleiset preferences.general.startHidden=Piilota ikkuna kun Cryptomator käynnistyy -preferences.general.autoCloseVaults=Lukitse avoimet holvit automaattisesti kun ohjelma sammutetaan preferences.general.debugLogging=Ota virheloki käyttöön preferences.general.debugDirectory=Näytä lokitiedostot preferences.general.autoStart=Käynnistä Cryptomator järjestelmän käynnistyessä @@ -419,7 +419,6 @@ main.vaultDetail.stats=Holvin tilastot main.vaultDetail.locateEncryptedFileBtn=Etsi salattu tiedosto main.vaultDetail.locateEncryptedFileBtn.tooltip=Valitse tiedosto holvistasi löytääksesi sen salatun vastineen main.vaultDetail.encryptedPathsCopied=Polut kopioitu leikepöydälle! -main.vaultDetail.filePickerTitle=Valitse tiedosto holvin sisältä ### Missing main.vaultDetail.missing.info=Cryptomator ei löytänyt täältä holvia. main.vaultDetail.missing.recheck=Tarkista uudelleen @@ -559,4 +558,10 @@ shareVault.visitHub=Käy Cryptomator Hubissa shareVault.hub.message=Kuinka jakaa Hub -holvi shareVault.hub.instruction.1=1. Jaa käyttöoikeus salattuun holvikansioon pilvipalvelun kautta. -shareVault.hub.openHub=Avaa Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Avaa Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_fil.properties b/src/main/resources/i18n/strings_fil.properties index 82cc3353a..5067ee7db 100644 --- a/src/main/resources/i18n/strings_fil.properties +++ b/src/main/resources/i18n/strings_fil.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=I-dismiss ## Button generic.button.apply=I-apply generic.button.back=Bumalik @@ -281,7 +282,6 @@ preferences.title=Mga Kagustuhan ## General preferences.general=Heneral preferences.general.startHidden=Itago ang window kapag sinimulan ang Cryptomator -preferences.general.autoCloseVaults=Awtomatikong i-lock ang mga bukas na vault kapag huminto sa aplikasyon preferences.general.debugLogging=Paganahin ang pag-log ng debug preferences.general.debugDirectory=Magbunyag ng mga log file preferences.general.autoStart=Ilunsad ang Cryptomator sa pagsisimula ng system @@ -410,7 +410,6 @@ main.vaultDetail.stats=Mga Istatistika ng Vault main.vaultDetail.locateEncryptedFileBtn=Hanapin ang Naka-encrypt na File main.vaultDetail.locateEncryptedFileBtn.tooltip=Pumili ng file mula sa iyong vault upang mahanap ang naka-encrypt na katapat nito main.vaultDetail.encryptedPathsCopied=Mga Path na Nakopya sa Clipboard! -main.vaultDetail.filePickerTitle=Piliin ang File Inside Vault ### Missing main.vaultDetail.missing.info=Hindi makahanap ng vault ang Cryptomator sa landas na ito. main.vaultDetail.missing.recheck=Suriin muli @@ -560,4 +559,10 @@ shareVault.hub.message=Paano ibahagi ang Hub vault shareVault.hub.description=Upang maibahagi ang nilalaman ng vault sa isa pang miyembro ng koponan, kailangan mong magsagawa ng dalawang hakbang: shareVault.hub.instruction.1=1. Ibahagi ang access ng naka-encrypt na folder ng vault sa pamamagitan ng cloud storage. shareVault.hub.instruction.2=2. Magbigay ng access sa miyembro ng team sa Cryptomator Hub. -shareVault.hub.openHub=Buksan ang Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Buksan ang Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_fr.properties b/src/main/resources/i18n/strings_fr.properties index 5bdaa268a..33dd242c1 100644 --- a/src/main/resources/i18n/strings_fr.properties +++ b/src/main/resources/i18n/strings_fr.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Ignorer ## Button generic.button.apply=Appliquer generic.button.back=Précédent @@ -282,7 +283,7 @@ preferences.title=Préférences ## General preferences.general=Général preferences.general.startHidden=Démarrer Cryptomator en mode caché -preferences.general.autoCloseVaults=Verrouiller automatiquement les coffres ouverts en quittant l'application +preferences.general.autoCloseVaults=Verrouiller les coffres sans confirmation lors de la fermeture de l'application preferences.general.debugLogging=Activer les logs debug preferences.general.debugDirectory=Afficher le journal preferences.general.autoStart=Lancer Cryptomator au démarrage du système @@ -342,7 +343,7 @@ preferences.contribute.sponsor=Parrain ### Remove License Key Dialog removeCert.title=Supprimer le certificat removeCert.message=Supprimer le certificat de soutien ? -removeCert.description= +removeCert.description=Les fonctionnalités principales de Cryptomator ne sont pas affectées par cela. L'accès à vos coffres n'est pas restreint et le niveau de sécurité n'est pas diminué. #<-- Add entries for donations and code/translation/documentation contribution --> ## About @@ -394,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=Afficher les options du volume chiffré main.vaultlist.contextMenu.reveal=Afficher le lecteur main.vaultlist.addVaultBtn.menuItemNew=Créer un nouveau coffre... main.vaultlist.addVaultBtn.menuItemExisting=Ouvrir un coffre existant... +main.vaultlist.showEventsButton.tooltip=Ouvrir la vue Événements ##Notificaition main.notification.updateAvailable=Mise à jour disponible. main.notification.support=Soutenir Cryptomator. @@ -422,7 +424,9 @@ main.vaultDetail.stats=Statistiques du volume chiffré main.vaultDetail.locateEncryptedFileBtn=Localiser le fichier chiffré main.vaultDetail.locateEncryptedFileBtn.tooltip=Choisissez un fichier dans votre coffre pour localiser sa version chiffrée main.vaultDetail.encryptedPathsCopied=Chemins d'accès copiés dans le presse-papier ! -main.vaultDetail.filePickerTitle=Sélectionner le fichier dans le coffre +main.vaultDetail.locateEncrypted.filePickerTitle=Sélectionner le fichier dans le coffre +main.vaultDetail.decryptName.buttonLabel=Déchiffrer le nom d'un fichier +main.vaultDetail.decryptName.tooltip=Choisir un fichier de coffre chiffré pour déchiffrer son nom ### Missing main.vaultDetail.missing.info=Cryptomator n'a pas pu trouver de volume chiffré dans ce chemin d'accès. main.vaultDetail.missing.recheck=Revérifier @@ -576,4 +580,42 @@ shareVault.hub.message=Comment partager un coffre central shareVault.hub.description=Afin de partager le contenu du coffre avec un autre membre de l'équipe, vous devez effectuer deux étapes : shareVault.hub.instruction.1=1. Partagez l'accès du dossier de coffre chiffré via le stockage cloud. shareVault.hub.instruction.2=2. Accorder l'accès au membre de l'équipe dans Cryptomator Hub. -shareVault.hub.openHub=Ouvrir le Hub Cryptomator \ No newline at end of file +shareVault.hub.openHub=Ouvrir le Hub Cryptomator + +# Decrypt File Names +decryptNames.title=Déchiffrer les noms de fichiers +decryptNames.filePicker.title=Sélectionner le fichier chiffré +decryptNames.filePicker.extensionDescription=Fichier chiffré Cryptomator +decryptNames.copyTable.tooltip=Copier le tableau +decryptNames.clearTable.tooltip=Effacer le tableau +decryptNames.copyHint=Copier le contenu de la cellule avec %s +decryptNames.dropZone.message=Déposer des fichiers ou cliquer pour en sélectionner +decryptNames.dropZone.error.foreignFiles=Les fichiers n'appartiennent pas au coffre « %s » +decryptNames.dropZone.error.noDirIdBackup=Le répertoire des fichiers sélectionnés ne contient pas de fichier dirId.c9r +decryptNames.dropZone.error.generic=Impossible de déchiffrer les noms de fichiers + + +# Event View +eventView.title=Événements +eventView.filter.allVaults=Tous +eventView.clearListButton.tooltip=Effacer la liste +## event list entries +eventView.entry.vaultLocked.description=Déverrouillez "%s" pour plus de détails +eventView.entry.conflictResolved.message=Conflit résolu +eventView.entry.conflictResolved.showDecrypted=Afficher le fichier déchiffré +eventView.entry.conflictResolved.copyDecrypted=Copier le chemin déchiffré +eventView.entry.conflict.message=La résolution des conflits a échoué +eventView.entry.conflict.showDecrypted=Afficher le fichier original déchiffré +eventView.entry.conflict.copyDecrypted=Copier le chemin original déchiffré +eventView.entry.conflict.showEncrypted=Afficher le fichier chiffré en conflit +eventView.entry.conflict.copyEncrypted=Copier le chemin chiffré en conflit +eventView.entry.decryptionFailed.message=Le déchiffrement a échoué +eventView.entry.decryptionFailed.showEncrypted=Afficher le fichier chiffré +eventView.entry.decryptionFailed.copyEncrypted=Copier le chemin chiffré +eventView.entry.brokenDirFile.message=Lien de répertoire cassé +eventView.entry.brokenDirFile.showEncrypted=Afficher le lien chiffré cassé +eventView.entry.brokenDirFile.copyEncrypted=Copier le chemin du lien cassé +eventView.entry.brokenFileNode.message=Nœud de système de fichiers cassé +eventView.entry.brokenFileNode.showEncrypted=Afficher le nœud chiffré cassé +eventView.entry.brokenFileNode.copyEncrypted=Copier le chemin du nœud chiffré cassé +eventView.entry.brokenFileNode.copyDecrypted=Copier le chemin déchiffré diff --git a/src/main/resources/i18n/strings_gl.properties b/src/main/resources/i18n/strings_gl.properties index 3cb526fc7..67c6128b6 100644 --- a/src/main/resources/i18n/strings_gl.properties +++ b/src/main/resources/i18n/strings_gl.properties @@ -135,3 +135,10 @@ lock.forced.retryBtn=Tentar de novo #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_he.properties b/src/main/resources/i18n/strings_he.properties index 66e488750..71769b20c 100644 --- a/src/main/resources/i18n/strings_he.properties +++ b/src/main/resources/i18n/strings_he.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=שחרור ## Button generic.button.apply=החל generic.button.back=חזור @@ -48,6 +49,7 @@ addvaultwizard.new.nameInstruction=בחירת שם עבור הכספת addvaultwizard.new.namePrompt=שם הכספת ### Location addvaultwizard.new.locationInstruction=היכן Cryptomator צריך לשמור את הקבצים המוצפנים של הכספת שלך? +addvaultwizard.new.locationLoading=בודק מערכת קבצים מקומית עבור ספריות ברירת מחדל לאחסון ענן… addvaultwizard.new.locationLabel=מיקום אחסון addvaultwizard.new.locationPrompt=… addvaultwizard.new.directoryPickerLabel=מיקום מותאם אישית @@ -260,7 +262,6 @@ preferences.title=העדפות ## General preferences.general=כללי preferences.general.startHidden=הסתר את החלון בהפעלת Cryptomator -preferences.general.autoCloseVaults=נעל vaults פתוחים באופן אוטומטי בעת יציאה מהאפליקציה preferences.general.debugLogging=אפשר רישום יומן באגים preferences.general.debugDirectory=הצג קבצי יומן preferences.general.autoStart=הפעלת Cryptomator עם הפעלת המערכת @@ -378,7 +379,6 @@ main.vaultDetail.stats=סטטיסטיקת הכספת main.vaultDetail.locateEncryptedFileBtn=מצא קבצים מוצפנים main.vaultDetail.locateEncryptedFileBtn.tooltip=בחר קובץ מהכספת שלך על מנת לאתר את הקובץ התואם המוצפן main.vaultDetail.encryptedPathsCopied=הנתיבים הועתקו ללוח! -main.vaultDetail.filePickerTitle=בחר קובץ בתוך הכספת ### Missing main.vaultDetail.missing.info=Cryptomator לא הצליח למצוא כספת בנתיב זה. main.vaultDetail.missing.recheck=בדיקה נוספת @@ -494,3 +494,10 @@ dokanySupportEnd.preferencesBtn=פתח העדפות #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_hi.properties b/src/main/resources/i18n/strings_hi.properties index 1a90e5572..bcc83679d 100644 --- a/src/main/resources/i18n/strings_hi.properties +++ b/src/main/resources/i18n/strings_hi.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=खारिज करें ## Button generic.button.apply=लागू करें generic.button.back=पीछे जाएं @@ -199,7 +200,6 @@ health.result.severityFilter.crit=गंभीर preferences.title=प्राथमिकताएं ## General preferences.general=सामान्य -preferences.general.autoCloseVaults=एप्लीकेशन बंद करते समय खुली हुई वॉल्ट्स को अपने आप लॉक करें preferences.general.autoStart=क्रिप्टोमेटर को सिस्टम स्टार्ट पे खोले ## Interface preferences.interface.theme.automatic=ऑटोमैटिक @@ -317,3 +317,10 @@ dokanySupportEnd.preferencesBtn=प्राथमिकताएँ खोल #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_hr.properties b/src/main/resources/i18n/strings_hr.properties index 108f8ff7a..a698cf329 100644 --- a/src/main/resources/i18n/strings_hr.properties +++ b/src/main/resources/i18n/strings_hr.properties @@ -384,3 +384,10 @@ quit.lockAndQuitBtn=Zaključaj i napusti #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_hu.properties b/src/main/resources/i18n/strings_hu.properties index f67d6f53e..375261975 100644 --- a/src/main/resources/i18n/strings_hu.properties +++ b/src/main/resources/i18n/strings_hu.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Elvet ## Button generic.button.apply=Alkalmaz generic.button.back=Vissza @@ -176,6 +177,7 @@ hub.registerFailed.description.generic=Hiba történt a regisztrációs folyamat hub.registerFailed.description.deviceAlreadyExists=Ez az eszköz már egy másik felhasználóhoz van regisztrálva. Próbáljon meg felhasználói fiókot váltani, vagy használjon egy másik eszközt. ### Unauthorized hub.unauthorized.message=Hozzáférés megtagadva +hub.unauthorized.description=Nincs hozzáférésed a széf megnyitásához. Vedd fel a kapcsolatot a széf tulajdonosával és kérj hozzáférést. ### Requires Account Initialization hub.requireAccountInit.message=Beavatkozás szükséges hub.requireAccountInit.description.0=A folytatáshoz kérlek töltsd ki a szükséges lépéseket a te @@ -281,7 +283,7 @@ preferences.title=Beállítások ## General preferences.general=Általános preferences.general.startHidden=Az ablak elrejtése a Cryptomator indítása után -preferences.general.autoCloseVaults=A széfek automatikus lezárása az alkalmazás bezárásakor +preferences.general.autoCloseVaults=Széfek lezárása megerősítés nélkül az alkalmazás bezárásakor preferences.general.debugLogging=Hibakeresési naplózás engedélyezése preferences.general.debugDirectory=Naplófájlok megjelenítése preferences.general.autoStart=Cryptomator indítása a rendszerrel együtt @@ -421,7 +423,6 @@ main.vaultDetail.stats=Széf statisztika main.vaultDetail.locateEncryptedFileBtn=Titkosított fájl megkeresése main.vaultDetail.locateEncryptedFileBtn.tooltip=Válasszon a széfből egy fájlt a titkosított megfelelőjének megkereséséhez main.vaultDetail.encryptedPathsCopied=Az útvonal a vágólapra került! -main.vaultDetail.filePickerTitle=Fájl választása a széfben ### Missing main.vaultDetail.missing.info=A Cryptomator nem talált széfet ezen az útvonalon. main.vaultDetail.missing.recheck=Ellenőrizze újra @@ -552,6 +553,10 @@ dokanySupportEnd.description=A Cryptomator nem támogatja tovább a Dokany köte dokanySupportEnd.preferencesBtn=Beállítások megnyitása #Retry If Readonly +retryIfReadonly.title=Korlátozott Széf Hozzáférés +retryIfReadonly.message=Nincs írási jogod a széf könyvtárához +retryIfReadonly.description=A Cryptomator nem tud a széf könyvtárába írni. Átrakhatod a széfet írásvédettre és megpróbálhatod újra. Ezt az opciót kikapcsolhatod a széf beállításaiban. +retryIfReadonly.retry=Változtatás és Újrapróbálás # Share Vault shareVault.title=Széf megosztása @@ -571,4 +576,10 @@ shareVault.hub.message=Hogyan osszon meg egy Hub széfet shareVault.hub.description=Ahhoz, hogy megossza a széf tartalmát egy másik csapattaggal, két lépést kell végrehajtania: shareVault.hub.instruction.1=Ossza meg a titkosított széf mappájának hozzáférését felhőalapú tárolón keresztül. shareVault.hub.instruction.2=2. Adjon hozzáférést a csapattagnak a Cryptomator Hubban. -shareVault.hub.openHub=Nyissa meg a Cryptomator Hubot \ No newline at end of file +shareVault.hub.openHub=Nyissa meg a Cryptomator Hubot + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_id.properties b/src/main/resources/i18n/strings_id.properties index 86f70acfb..57fa4ecb5 100644 --- a/src/main/resources/i18n/strings_id.properties +++ b/src/main/resources/i18n/strings_id.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Tutup ## Button generic.button.apply=Terapkan generic.button.back=Kembali @@ -16,19 +17,19 @@ generic.button.print=Cetak generic.button.remove=Hapus # Error -error.message=Terjadi kesalahan %s +error.message=Terjadi kesalahan error.description=Ups! Cryptomator tidak menyangka hal ini terjadi. Anda dapat mencari solusi yang tersedia untuk error ini. Atau jika error ini belum pernah dilaporkan, tidak perlu sungkan untuk melaporkannya. -error.hyperlink.lookup=Cari tahu informasi lebih lanjut tentang suatu kesalahan -error.hyperlink.report=Laporkan error berikut +error.hyperlink.lookup=Cari kesalahan ini +error.hyperlink.report=Laporkan kesalahan ini error.technicalDetails=Rincian: -error.existingSolutionDescription=Maaf, Cryptomator tiba-tiba berhenti. Tapi telah ditemukan sebuah jawaban untuk masalah ini. Lihat tautan berikut. -error.hyperlink.solution=Cari tahu solusi untuk masalah ini +error.existingSolutionDescription=Cryptomator tidak menyangka ini terjadi. Tapi telah ditemukan sebuah solusi untuk kesalahan ini. Lihat tautan berikut. +error.hyperlink.solution=Cari tahu solusinya error.lookupPermissionMessage=Cryptomator bisa mencari solusinya secara daring. Ini akan meneruskan pesan ke direktori masalah kami dengan alamat IP Anda. error.dismiss=Tutup error.lookUpSolution=Cari tahu solusinya # Defaults -defaults.vault.vaultName=Brangkas +defaults.vault.vaultName=Brankas # Tray Menu traymenu.showMainWindow=Tampilkan @@ -44,10 +45,10 @@ addvaultwizard.title=Tambah Brankas ## New addvaultwizard.new.title=Tambah Brankas Baru ### Name -addvaultwizard.new.nameInstruction=Buat sebuah nama untuk brankas +addvaultwizard.new.nameInstruction=Pilih sebuah nama untuk brankas addvaultwizard.new.namePrompt=Nama Brankas ### Location -addvaultwizard.new.locationInstruction=Dimana Cryptomator seharusnya menyimpan vault berisi file enkripsi Anda? +addvaultwizard.new.locationInstruction=Di mana Cryptomator mesti menyimpan berkas terenkripsi dari brankas Anda? addvaultwizard.new.locationLoading=Memeriksa sistem file lokal untuk direktori default penyimpanan cloud… addvaultwizard.new.locationLabel=Lokasi penyimpanan addvaultwizard.new.locationPrompt=… @@ -71,15 +72,15 @@ addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=Buka dokumentasi u addvaultwizard.new.expertSettings.shorteningThreshold.title=Panjang maksimum nama file terenkripsi addvaultwizard.new.expertSettings.shorteningThreshold.valid=Valid ### Password -addvaultwizard.new.createVaultBtn=Buat Brangkas +addvaultwizard.new.createVaultBtn=Buat Brankas addvaultwizard.new.generateRecoveryKeyChoice=Anda tidak dapat mengakses data tanpa kata sandi yang Anda miliki. Apa Anda ingin sebuah kunci pemulihan untuk berjaga-jaga jika seandainya Anda kehilangan kata sandi? addvaultwizard.new.generateRecoveryKeyChoice.yes=Ya tolong, Lebih baik aman daripada menyesal addvaultwizard.new.generateRecoveryKeyChoice.no=Tidak terima kasih, Saya tidak akan kehilangan kata sandi saya ### Information addvault.new.readme.storageLocation.fileName=PENTING.rtf -addvault.new.readme.storageLocation.1=⚠️ FILE VAULT ⚠️ -addvault.new.readme.storageLocation.2=Ini adalah lokasi penyimpanan vault Anda. -addvault.new.readme.storageLocation.3=DILARANG +addvault.new.readme.storageLocation.1=⚠️ BERKAS BRANKAS ⚠️ +addvault.new.readme.storageLocation.2=Ini adalah lokasi penyimpanan brankas Anda. +addvault.new.readme.storageLocation.3=JANGAN addvault.new.readme.storageLocation.4=• mengubah file apapun di direktori ini, atau addvault.new.readme.storageLocation.5=• menyalin file untuk dienkripsi ke dalam direktori ini. addvault.new.readme.storageLocation.6=Jika Anda ingin mengenkripsi file dan melihat isi konten dari vault, lakukan hal berikut: @@ -97,15 +98,15 @@ addvaultwizard.existing.title=Tambahkan Vault yang Ada addvaultwizard.existing.instruction=Pilih file "vault.cryptomator" Anda dari vault yang ada. Jika hanya ada file bernama "masterkey.cryptomator", pilih file tersebut. addvaultwizard.existing.chooseBtn=Pilih… addvaultwizard.existing.filePickerTitle=Pilih File Vault -addvaultwizard.existing.filePickerMimeDesc=Vault Cryptomator +addvaultwizard.existing.filePickerMimeDesc=Brankas Cryptomator ## Success addvaultwizard.success.nextStepsInstructions=Vault "%s" telah dibuat.\nAnda harus membuka kunci vault ini untuk mengakses atau menambahkan konten. Anda juga dapat membuka kunci vault ini kapan saja di kemudian hari. addvaultwizard.success.unlockNow=Buka Kunci Sekarang # Remove Vault -removeVault.title=Hapus Vault -removeVault.message=Hapus vault? -removeVault.description=Tindakan ini hanya akan membuat Cryptomator melupakan vault ini. Anda dapat menambahkan vault ini lagi nanti. File yang telah dienkripsi tidak akan dihapus dari hard drive Anda. +removeVault.title=Hapus "%s" +removeVault.message=Hapus brankas? +removeVault.description=Tindakan ini hanya akan membuat Cryptomator melupakan brankas ini. Anda dapat menambahkan brankas ini lagi nanti. Berkas yang telah dienkripsi tidak akan dihapus dari hard drive Anda. # Change Password changepassword.title=Ubah Kata Sandi @@ -124,14 +125,14 @@ unlock.passwordPrompt=Masukkan kata sandi "%s": unlock.savePassword=Ingat Kata Sandi unlock.unlockBtn=Buka Kunci ## Select -unlock.chooseMasterkey.message=File masterkey tidak ditemukan +unlock.chooseMasterkey.message=Berkas masterkey tidak ditemukan unlock.chooseMasterkey.description=Tidak dapat menemukan file masterkey untuk vault ini pada lokasi yang dicari. Mohon pilih file kunci secara manual. unlock.chooseMasterkey.filePickerTitle=Pilih File Masterkey unlock.chooseMasterkey.filePickerMimeDesc=MasterKey Cryptomator ## Success unlock.success.message=Buka kunci berhasil unlock.success.description="%s" berhasil dibuka! Vault Anda sekarang dapat diakses melalui drive virtual. -unlock.success.rememberChoice=Ingat pilihan saya, jangan perlihatkan lagi +unlock.success.rememberChoice=Ingat pilihan saya, jangan tanyakan lagi unlock.success.revealBtn=Tampilkan Drive ## Failure unlock.error.customPath.message=Tidak dapat memasang vault ke path kustom @@ -142,7 +143,7 @@ unlock.error.customPath.description.hideawayNotDir=File sementara dan tersembuny unlock.error.customPath.description.couldNotBeCleaned=Vault Anda tidak dapat dipasang ke path "%s". Silakan coba lagi atau pilih path yang berbeda. unlock.error.customPath.description.notEmptyDir=Path kustom pemasangan "%s" bukan folder kosong. Silakan pilih folder kosong dan coba lagi. unlock.error.customPath.description.generic=Anda telah memilih kustom path pemasangan untuk vault ini, tetapi gagal menggunakannya dengan pesan:%2$s -unlock.error.restartRequired.message=Tidak dapat membuka kunci vault +unlock.error.restartRequired.message=Tidak dapat membuka kunci brankas unlock.error.restartRequired.description=Ubah jenis volume di opsi vault atau restart Cryptomator. unlock.error.title=Gagal membuka kunci "%s" ## Hub @@ -154,7 +155,7 @@ hub.auth.message=Menunggu otentikasi… hub.auth.description=Anda secara otomatis akan diarahkan ke halaman login. hub.auth.loginLink=Tidak diarahkan? Klik di sini untuk membukanya. ### Receive Key -hub.receive.message=Respons pemrosesan… +hub.receive.message=Memroses respons… hub.receive.description=Cryptomator menerima dan memproses respons dari Hub. Harap tunggu. ### Register Device hub.register.message=Perangkat Baru @@ -176,10 +177,11 @@ hub.registerFailed.description.generic=Kesalahan terjadi dalam proses pendaftara hub.registerFailed.description.deviceAlreadyExists=Perangkat ini sudah terdaftar untuk pengguna lain. Cobalah menggunakan akun pengguna atau perangkat yang berbeda. ### Unauthorized hub.unauthorized.message=Akses ditolak +hub.unauthorized.description=Anda tidak punya otorisasi untuk membuka brankas ini. Hubungi pemilik brankas untuk meminta akses. ### Requires Account Initialization hub.requireAccountInit.message=Tindakan diperlukan -hub.requireAccountInit.description.0=Untuk melanjutkan, silakan lengkapi langkah-langkah yang diperlukan -hub.requireAccountInit.description.1=Hub profil pengguna +hub.requireAccountInit.description.0=Untuk melanjutkan, silakan lengkapi langkah-langkah yang diperlukan dalam +hub.requireAccountInit.description.1=Profil pengguna Hub Anda hub.requireAccountInit.description.2=. ### License Exceeded hub.invalidLicense.message=Lisensi Hub tidak valid @@ -281,7 +283,7 @@ preferences.title=Preferensi ## General preferences.general=Umum preferences.general.startHidden=Sembunyikan jendela saat memulai Cryptomator -preferences.general.autoCloseVaults=Kunci vault yang terbuka secara otomatis saat aplikasi dihentikan +preferences.general.autoCloseVaults=Kunci brankas tanpa bertanya ketika keluar aplikasi preferences.general.debugLogging=Aktifkan pencatatan debug preferences.general.debugDirectory=Perlihatkan file log preferences.general.autoStart=Jalankan Cryptomator saat sistem dimulai @@ -421,7 +423,6 @@ main.vaultDetail.stats=Statistik Vault main.vaultDetail.locateEncryptedFileBtn=Temukan File yang Dienkripsi main.vaultDetail.locateEncryptedFileBtn.tooltip=Pilih file dari vault Anda untuk menemukan mitra enkripsinya main.vaultDetail.encryptedPathsCopied=Path Disalin ke Clipboard! -main.vaultDetail.filePickerTitle=Pilih File Didalam Vault ### Missing main.vaultDetail.missing.info=Cryptomator tidak dapat menemukan vault di path ini. main.vaultDetail.missing.recheck=Periksa kembali @@ -552,6 +553,10 @@ dokanySupportEnd.description=Volume berjenis Dokany sudah tidak didukung oleh Cr dokanySupportEnd.preferencesBtn=Buka Preferensi #Retry If Readonly +retryIfReadonly.title=Akses Brankas Dibatasi +retryIfReadonly.message=Tidak ada akses tulis ke direktori brankas +retryIfReadonly.description=Cryptomator tidak bisa menulis ke direktori brankas. Anda dapat mengubah brankas menjadi hanya-baca dan mencoba lagi. Opsi ini bisa dimatikan dalam opsi brankas. +retryIfReadonly.retry=Ubah dan Coba Lagi # Share Vault shareVault.title=Bagikan Vault @@ -571,4 +576,11 @@ shareVault.hub.message=Cara berbagi Hub vault shareVault.hub.description=Untuk berbagi isi vault dengan anggota tim lain, Anda harus melakukan dua langkah: shareVault.hub.instruction.1=1. Bagikan akses folder vault terenkripsi melalui penyimpanan cloud. shareVault.hub.instruction.2=2. Berikan akses ke anggota tim di Cryptomator Hub. -shareVault.hub.openHub=Buka Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Buka Cryptomator Hub + +# Decrypt File Names + + +# Event View +eventView.filter.allVaults=Semua +## event list entries diff --git a/src/main/resources/i18n/strings_it.properties b/src/main/resources/i18n/strings_it.properties index f04c40d35..a23ad1dad 100644 --- a/src/main/resources/i18n/strings_it.properties +++ b/src/main/resources/i18n/strings_it.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Ignora ## Button generic.button.apply=Applica generic.button.back=Indietro @@ -282,7 +283,7 @@ preferences.title=Preferenze ## General preferences.general=Generale preferences.general.startHidden=Nascondi la finestra avviando Cryptomator -preferences.general.autoCloseVaults=Blocca automaticamente le cassaforti aperte all'uscita dell'applicazione +preferences.general.autoCloseVaults=Alla chiusura dell'applicazione blocca le casseforti senza chiedere conferma preferences.general.debugLogging=Abilita la registrazione di debug preferences.general.debugDirectory=Rivela i file di registro preferences.general.autoStart=Avvia Cryptomator all'avvio del sistema @@ -394,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=Mostra le Opzioni della Cassaforte main.vaultlist.contextMenu.reveal=Rivela Unità main.vaultlist.addVaultBtn.menuItemNew=Crea una nuova cassaforte... main.vaultlist.addVaultBtn.menuItemExisting=Apri una cassaforte esistente... +main.vaultlist.showEventsButton.tooltip=Apri vista eventi ##Notificaition main.notification.updateAvailable=Aggiornamento disponibile. main.notification.support=Supporta Cryptomator. @@ -422,7 +424,9 @@ main.vaultDetail.stats=Statistiche della Cassaforte main.vaultDetail.locateEncryptedFileBtn=Individua File Crittografato main.vaultDetail.locateEncryptedFileBtn.tooltip=Scegli un file dalla cassaforte per individuare la controparte cifrata main.vaultDetail.encryptedPathsCopied=Percorsi copiati negli Appunti! -main.vaultDetail.filePickerTitle=Seleziona File Nella Cassaforte +main.vaultDetail.locateEncrypted.filePickerTitle=Seleziona file nella cassaforte +main.vaultDetail.decryptName.buttonLabel=Decifra nome file +main.vaultDetail.decryptName.tooltip=Seleziona il file di una cassaforte cifrata per decifrarne il nome ### Missing main.vaultDetail.missing.info=Cryptomator non è riuscito a trovare una cassaforte in questo percorso. main.vaultDetail.missing.recheck=Ricontrolla @@ -576,4 +580,43 @@ shareVault.hub.message=Come condividere una cassaforte Hub shareVault.hub.description=Per condividere il contenuto della cassaforte con un altro membro del gruppo, è necessario eseguire due passaggi: shareVault.hub.instruction.1=1. Condividi l'accesso alla cartella della cassaforte crittografata tramite l'archiviazione cloud. shareVault.hub.instruction.2=2. Concedi l'accesso ai membri del gruppo nell'Hub Cryptomator. -shareVault.hub.openHub=Apri l'Hub Cryptomator \ No newline at end of file +shareVault.hub.openHub=Apri l'Hub Cryptomator + +# Decrypt File Names +decryptNames.title=Decifra i nomi dei files +decryptNames.filePicker.title=Seleziona file criptato +decryptNames.filePicker.extensionDescription=File crittografato Cryptomator +decryptNames.copyTable.tooltip=Copia tabella +decryptNames.clearTable.tooltip=Cancella la tabella +decryptNames.copyHint=Copia il contenuto della cella con %s +decryptNames.dropZone.message=Trascina i file o fai clic per selezionare +decryptNames.dropZone.error.vaultInternalFiles=Hai selezionato file di casseforti con il nome non decifrabile +decryptNames.dropZone.error.foreignFiles=I file non appartengono alla cassaforte "%s" +decryptNames.dropZone.error.noDirIdBackup=La directory dei file selezionati non contiene il file dirId.c9r +decryptNames.dropZone.error.generic=Decifratura nomi file non riuscita + + +# Event View +eventView.title=Eventi +eventView.filter.allVaults=Tutti +eventView.clearListButton.tooltip=Cancella elenco +## event list entries +eventView.entry.vaultLocked.description=Sblocca "%s" per i dettagli +eventView.entry.conflictResolved.message=Conflitto risolto +eventView.entry.conflictResolved.showDecrypted=Mostra file decifrato +eventView.entry.conflictResolved.copyDecrypted=Copia percorso decriptato +eventView.entry.conflict.message=Risoluzione dei conflitti fallita +eventView.entry.conflict.showDecrypted=Mostra il file originale decifrato +eventView.entry.conflict.copyDecrypted=Copia il percorso originale decifrato +eventView.entry.conflict.showEncrypted=Mostra file cifrato in conflitto +eventView.entry.conflict.copyEncrypted=Copia il percorso cifrato in conflitto +eventView.entry.decryptionFailed.message=Decifratura fallita +eventView.entry.decryptionFailed.showEncrypted=Mostra file cifrato +eventView.entry.decryptionFailed.copyEncrypted=Copia percorso cifrato +eventView.entry.brokenDirFile.message=Collegamento directory errato +eventView.entry.brokenDirFile.showEncrypted=Visualizza il link cifrato errato +eventView.entry.brokenDirFile.copyEncrypted=Copia il percorso errato del file cifrato +eventView.entry.brokenFileNode.message=Nodo file errato +eventView.entry.brokenFileNode.showEncrypted=Visualizza il nodo file cifrato errato +eventView.entry.brokenFileNode.copyEncrypted=Copia il percorso errato del nodo file cifrato +eventView.entry.brokenFileNode.copyDecrypted=Copia il percorso decifrato diff --git a/src/main/resources/i18n/strings_ja.properties b/src/main/resources/i18n/strings_ja.properties index 1122eeb8c..b1559d5e4 100644 --- a/src/main/resources/i18n/strings_ja.properties +++ b/src/main/resources/i18n/strings_ja.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=閉じる ## Button generic.button.apply=適用 generic.button.back=戻る @@ -176,6 +177,7 @@ hub.registerFailed.description.generic=登録中にエラーが発生しまし hub.registerFailed.description.deviceAlreadyExists=このデバイスは既に別のユーザーに登録されています。ユーザーアカウントを変更するか、別のデバイスを使用してください。 ### Unauthorized hub.unauthorized.message=アクセスが拒否されました +hub.unauthorized.description=この金庫を開く権限がありません。金庫の所有者からアクセス許可を貰って下さい。 ### Requires Account Initialization hub.requireAccountInit.message=アクションが必要です hub.requireAccountInit.description.0=続行するには以下のサイトで必要な手順を完了してください @@ -281,7 +283,7 @@ preferences.title=環境設定 ## General preferences.general=基本設定 preferences.general.startHidden=Cryptomator を開始したときウィンドウを隠す -preferences.general.autoCloseVaults=アプリケーションを終了するときに自動的に開いている金庫を施錠する +preferences.general.autoCloseVaults=アプリケーション終了時に質問せずに金庫をロックする preferences.general.debugLogging=ログを有効にする preferences.general.debugDirectory=ログ ファイルを表示 preferences.general.autoStart=システム開始時に Cryptomator を起動する @@ -333,8 +335,12 @@ preferences.contribute.registeredFor=サポート証明書 (%s) が登録され preferences.contribute.noCertificate=Cryptomator を支援し、サポーター証明書を受け取りましょう。ライセンスキーに似ていますがフリーソフトを使う寄付者向けのキーです。 ;-) preferences.contribute.getCertificate=まだ証明書を手に入れていませんか? 詳細はこちらから確認できます。 preferences.contribute.promptText=サポーター証明書をここに張り付けてください +preferences.contribute.donate=寄付する +preferences.contribute.sponsor=スポンサー ### Remove License Key Dialog +removeCert.title=証明書を削除 +removeCert.message=サポーター証明書を削除しますか? #<-- Add entries for donations and code/translation/documentation contribution --> ## About @@ -384,7 +390,11 @@ main.vaultlist.contextMenu.unlock=解錠... main.vaultlist.contextMenu.unlockNow=今すぐ解錠 main.vaultlist.contextMenu.vaultoptions=金庫のオプションを表示 main.vaultlist.contextMenu.reveal=ドライブを表示 +main.vaultlist.addVaultBtn.menuItemNew=新しい金庫を作成… +main.vaultlist.addVaultBtn.menuItemExisting=既存の金庫を開く… ##Notificaition +main.notification.updateAvailable=アップデートがあります。 +main.notification.support=Cryptomator を支援する。 ## Vault Detail ### Welcome main.vaultDetail.welcomeOnboarding=ファイルを保護するために Cryptomator を選んでいただきありがとうございます。ヘルプが必要であれば、スタートガイドをご覧ください: @@ -410,7 +420,9 @@ main.vaultDetail.stats=金庫の統計情報 main.vaultDetail.locateEncryptedFileBtn=暗号化されたファイルの場所 main.vaultDetail.locateEncryptedFileBtn.tooltip=暗号化された金庫のファイルを選択してください main.vaultDetail.encryptedPathsCopied=パスをクリップボードにコピーしました! -main.vaultDetail.filePickerTitle=金庫内のファイルを選択 +main.vaultDetail.locateEncrypted.filePickerTitle=金庫内のファイルを選択 +main.vaultDetail.decryptName.buttonLabel=ファイル名の復号化 +main.vaultDetail.decryptName.tooltip=名前を復号化する暗号化金庫を選択してください ### Missing main.vaultDetail.missing.info=Cryptomator はこの場所に金庫を見つけることができませんでした。 main.vaultDetail.missing.recheck=再確認 @@ -541,6 +553,9 @@ dokanySupportEnd.description=ボリュームタイプ dokanySupportEnd.preferencesBtn=環境設定を開く #Retry If Readonly +retryIfReadonly.title=制限された金庫へのアクセス +retryIfReadonly.message=金庫のディレクトリに書き込み権限がありません +retryIfReadonly.retry=変更して再試行 # Share Vault shareVault.title=保管庫を共有する @@ -560,4 +575,21 @@ shareVault.hub.message=ハブ保管庫の共有方法 shareVault.hub.description=データ保管庫のコンテンツを他のチームメンバーと共有するには、2つの手順を実行する必要があります: shareVault.hub.instruction.1=1. クラウドストレージ経由で暗号化された保管庫フォルダへのアクセスを共有します。 shareVault.hub.instruction.2=2. Cryptomator Hubでチームメンバーにアクセスを許可する。 -shareVault.hub.openHub=Cryptomator Hubを開く \ No newline at end of file +shareVault.hub.openHub=Cryptomator Hubを開く + +# Decrypt File Names +decryptNames.title=ファイル名の復号化 +decryptNames.filePicker.title=暗号化されたファイルを選択 +decryptNames.filePicker.extensionDescription=Cryptomator で暗号化したファイル +decryptNames.copyTable.tooltip=表をコピー +decryptNames.clearTable.tooltip=表をクリア +decryptNames.copyHint=%s でセルの内容をコピー +decryptNames.dropZone.message=ファイルをドロップまたはクリックして選択 +decryptNames.dropZone.error.vaultInternalFiles=選択された金庫の内部ファイルに復号化できる名前がありません +decryptNames.dropZone.error.foreignFiles=ファイルは金庫 "%s" に属していません +decryptNames.dropZone.error.noDirIdBackup=選択したファイルのディレクトリにdirId.c9rファイルが含まれていません +decryptNames.dropZone.error.generic=ファイル名の復号化に失敗しました + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_ko.properties b/src/main/resources/i18n/strings_ko.properties index 9f11d6b20..ab03e9c6c 100644 --- a/src/main/resources/i18n/strings_ko.properties +++ b/src/main/resources/i18n/strings_ko.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=무시 ## Button generic.button.apply=적용 generic.button.back=이전 @@ -33,10 +34,10 @@ defaults.vault.vaultName=Vault # Tray Menu traymenu.showMainWindow=보기 traymenu.showPreferencesWindow=환경설정 -traymenu.lockAllVaults=모든 Vault 잠금 +traymenu.lockAllVaults=모두 잠그기 traymenu.quitApplication=종료 -traymenu.vault.unlock=잠금해제 -traymenu.vault.lock=잠금 +traymenu.vault.unlock=잠금 해제 +traymenu.vault.lock=잠그기 traymenu.vault.reveal=표시 # Add Vault Wizard @@ -109,7 +110,7 @@ removeVault.description=이 행위는 Cryptomator에서만 이 Vault를 지웁 # Change Password changepassword.title=비밀번호 변경 -changepassword.enterOldPassword="%s"의 비밀번호를 입력하여 주십시요. +changepassword.enterOldPassword="%s"의 비밀번호를 입력하여 주십시오. changepassword.finalConfirmation=비밀번호를 잊어버리면, 데이터에 접근할 수 없다는 것을 이해했습니다. # Forget Password @@ -135,7 +136,7 @@ unlock.success.rememberChoice=선택 기억하기, 다시 묻지 않음 unlock.success.revealBtn=드라이브 표시 ## Failure unlock.error.customPath.message=Vault를 사용자 정의 경로에 마운트할 수 없습니다. -unlock.error.customPath.description.notSupported=사용자 지정 경로를 계속 사용하려면 설정으로 이동하여 이를 지원하는 볼륨 유형을 선택하십시오. 그렇지 않으면 볼트 옵션으로 이동하여 지원되는 마운트 지점을 선택하십시오. +unlock.error.customPath.description.notSupported=사용자 지정 경로를 계속 사용하려면 설정으로 이동하여 이를 지원하는 볼륨 유형을 선택하십시오. 또는, Vault 설정으로 이동하여 지원되는 마운트 지점을 선택하십시오. unlock.error.customPath.description.notExists=사용자 정의 마운트 경로가 존재하지 않습니다. 로컬 파일 시스템에서 생성하거나 볼트 옵션에서 변경하세요. unlock.error.customPath.description.inUse=드라이브 문자 또는 사용자 정의 마운트 경로 "%s"가 이미 사용 중입니다. unlock.error.customPath.description.hideawayNotDir=잠금 해제에 사용된 임시 숨김 파일 "%3$s"을 제거할 수 없습니다. 파일을 확인한 후 수동으로 삭제해 주세요. @@ -194,7 +195,7 @@ lock.forced.retryBtn=재시도 lock.forced.forceBtn=강제 잠금 ## Failure lock.fail.message=Vault 잠금에 실패하였습니다. -lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료 하십시요. +lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료 하십시오. # Migration migration.title=Vault 업그레이드 @@ -207,7 +208,7 @@ migration.start.remarkCanRun=이 vault를 열 때 사용하는 모든 장치가 migration.start.remarkSynced=업그레이드하기 전에 해당 vault가 모든 기기에 정상적으로 동기화되어야 합니다. migration.start.confirm=나는 위 정보를 읽고 정말 이해했습니다. ## Run -migration.run.enterPassword="%s"의 비밀번호를 입력하십시요. +migration.run.enterPassword="%s"의 비밀번호를 입력하십시오. migration.run.startMigrationBtn=Vault 마이그레이션 migration.run.progressHint=이 작업은 시간이 조금 소요됩니다. ## Success @@ -223,7 +224,7 @@ migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=파일 시스 ## Impossible migration.impossible.heading=Vault를 마이그레이션 할 수 없습니다. migration.impossible.reason=저장소 위치 또는 접근 지점이 호환되지 않아 Vault를 자동으로 마이그레이션 할 수 없습니다. -migration.impossible.moreInfo=Vault를 이전 버전으로 계속 열수 있습니다. Vault를 직접 마이그레이션 하는 설명을 보시려면, 다음을 방문하십시요. +migration.impossible.moreInfo=Vault를 이전 버전으로 계속 열수 있습니다. Vault를 직접 마이그레이션 하는 설명을 보시려면, 다음을 방문하십시오. # Health Check ## Start @@ -275,14 +276,14 @@ health.result.fixStateFilter.fixFailed=문제 해결 실패 ## Fix Application health.fix.fixBtn=문제 해결 health.fix.successTip=문제 해결이 성공적으로 완료되었습니다 -health.fix.failTip=문제 해결 실패, 상세 정보는 로그를 참조하십시요. +health.fix.failTip=문제 해결 실패, 상세 정보는 로그를 참조하십시오. # Preferences preferences.title=환경설정 ## General preferences.general=일반 preferences.general.startHidden=Cryptomator를 시작할 때 창 숨김 -preferences.general.autoCloseVaults=애플리케이션을 닫을 때 자동으로 열린 Vault를 잠그기. +preferences.general.autoCloseVaults=응용 프로그램을 종료할 때 묻지 않고 Vault를 잠그기 preferences.general.debugLogging=디버그 로깅 활성화 preferences.general.debugDirectory=로그 파일 표시 preferences.general.autoStart=시스템 시작 시 Cryptomator 실행 @@ -332,7 +333,7 @@ preferences.updates.upToDate=현재 최신 버전의 Cryptomator를 사용하고 ## Contribution preferences.contribute=후원하기 preferences.contribute.registeredFor=%s(으)로 후원자 인증 등록됨 -preferences.contribute.noCertificate=Cryptomator를 후원하시고 후원자 인증을 받으십시요. 라이선스 키와 비슷하지만 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-) +preferences.contribute.noCertificate=Cryptomator를 후원하시고 후원자 인증을 받으십시오. 라이선스 키와 비슷하지만 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-) preferences.contribute.getCertificate=아직 후원자 인증이 없으신가요? 어떻게 얻는지 배울 수 있습니다. preferences.contribute.promptText=후원자 인증코드를 여기에 붙여넣기 preferences.contribute.thankYou=Cryptomator의 오픈 소스 개발을 지원해 주셔서 감사합니다! @@ -394,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=Vault 옵션 보기 main.vaultlist.contextMenu.reveal=드라이브 표시 main.vaultlist.addVaultBtn.menuItemNew=새 Vault 생성... main.vaultlist.addVaultBtn.menuItemExisting=기존 Vault 열기... +main.vaultlist.showEventsButton.tooltip=이벤트 뷰어 열기 ##Notificaition main.notification.updateAvailable=업데이트가 있습니다. main.notification.support=Cryptomator 지원하기. @@ -422,7 +424,6 @@ main.vaultDetail.stats=Vault 통계 main.vaultDetail.locateEncryptedFileBtn=암호화된 파일 위치 main.vaultDetail.locateEncryptedFileBtn.tooltip=암호화된 파일을 보기 위해 Vault에서 파일을 선택하십시오. main.vaultDetail.encryptedPathsCopied=클립보드에 복사됨! -main.vaultDetail.filePickerTitle=Vault 내부에서 파일 선택 ### Missing main.vaultDetail.missing.info=Cryptomator가 이 경로에 있는 Vault를 찾지 못했습니다. main.vaultDetail.missing.recheck=다시 시도 @@ -497,7 +498,7 @@ recoveryKey.display.StorageHints=매우 안전한곳에 보관하십시오. 예 ## Reset Password ### Enter Recovery Key recoveryKey.recover.title=비밀번호 바꾸기 -recoveryKey.recover.prompt="%s"의 복구키를 입력하십시요: +recoveryKey.recover.prompt="%s"의 복구키를 입력하십시오: recoveryKey.recover.correctKey=올바른 복구 키 입니다 recoveryKey.recover.wrongKey=이 복구 키는 다른 vault의 키입니다 recoveryKey.recover.invalidKey=해당 복구 키는 유효하지 않습니다 @@ -576,4 +577,31 @@ shareVault.hub.message=Hub Vault를 공유하는법 shareVault.hub.description=다른 팀 구성원과 Vault를 공유하기 위해서는 다음 단계를 따르십시오: shareVault.hub.instruction.1=1. 암호화된 Vault 폴더를 클라우드 스토리지를 통해 공유하십시오. shareVault.hub.instruction.2=2. Cryptomator Hub에서 팀 구성원에 접근을 허가하기 -shareVault.hub.openHub=Cryptomator Hub 열기 \ No newline at end of file +shareVault.hub.openHub=Cryptomator Hub 열기 + +# Decrypt File Names + + +# Event View +eventView.title=이벤트 +eventView.filter.allVaults=전체 +## event list entries +eventView.entry.vaultLocked.description="%s"를 잠금 해제하여 세부정보 보기 +eventView.entry.conflictResolved.message=해결된 충돌 +eventView.entry.conflictResolved.showDecrypted=복호화된 파일 보기 +eventView.entry.conflictResolved.copyDecrypted=복호화된 경로 복사하기 +eventView.entry.conflict.message=충돌 해결 실패 +eventView.entry.conflict.showDecrypted=복호화된 원본 파일 보기 +eventView.entry.conflict.copyDecrypted=복호화된 원본 경로 복사하기 +eventView.entry.conflict.showEncrypted=충돌하는 암호화된 파일 보기 +eventView.entry.conflict.copyEncrypted=충돌하는 암호화된 경로 복사하기 +eventView.entry.decryptionFailed.message=복호화 실패 +eventView.entry.decryptionFailed.showEncrypted=암호화된 파일 보기 +eventView.entry.decryptionFailed.copyEncrypted=암호화된 경로 복사하기 +eventView.entry.brokenDirFile.message=망가진 디렉터리 링크 +eventView.entry.brokenDirFile.showEncrypted=망가진 암호화된 링크 보기 +eventView.entry.brokenDirFile.copyEncrypted=망가진 링크의 경로 복사하리 +eventView.entry.brokenFileNode.message=망가진 파일시스템 노드 +eventView.entry.brokenFileNode.showEncrypted=망가진 암호화된 노드 보기 +eventView.entry.brokenFileNode.copyEncrypted=망가진 암호화된 노드의 경로 복사하기 +eventView.entry.brokenFileNode.copyDecrypted=복호화된 경로 복사하기 diff --git a/src/main/resources/i18n/strings_lv.properties b/src/main/resources/i18n/strings_lv.properties index e2b976468..46ed07b31 100644 --- a/src/main/resources/i18n/strings_lv.properties +++ b/src/main/resources/i18n/strings_lv.properties @@ -1,26 +1,32 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Atmest ## Button generic.button.apply=Pielietot generic.button.back=Atpakaļ generic.button.cancel=Atcelt generic.button.change=Mainīt -generic.button.choose=Izvēlies... +generic.button.choose=Izvēlēties… generic.button.close=Aizvērt -generic.button.copy=Kopēt -generic.button.copied=Nokopēts! +generic.button.copy=Ievietot starpliktuvē +generic.button.copied=Ievietots starpliktuvē generic.button.done=Darīts generic.button.next=Tālāk generic.button.print=Drukāt +generic.button.remove=Noņemt # Error -error.message=Radās kļūda -error.description=Cryptomator negaidīja, ka tas notiks. Varat meklēt esošos šīs kļūdas risinājumus. Vai arī, ja par to vēl nav ziņots, droši dariet to. -error.hyperlink.lookup=Meklējiet šo kļūdu -error.hyperlink.report=Ziņojiet par šo kļūdu +error.message=Atgadījās kļūda +error.description=Cryptomator negaidīja, ka tas notiks. Var uzmeklēt esošus šīs kļūdas risinājumus. Vai arī, ja par to vēl nav ziņots, var droši darīt to. +error.hyperlink.lookup=Uzmeklēt šo kļūdu +error.hyperlink.report=Ziņot par šo kļūdu error.technicalDetails=Detaļas: -error.existingSolutionDescription=Cryptomator negaidīja, ka tas notiks. Bet mēs atradām esošu risinājumu šai kļūdai. Lūdzu, apskatiet tālāk norādīto saiti. +error.existingSolutionDescription=Cryptomator negaidīja, ka tas notiks. Bet mēs šai kļūdai atradām esošu risinājumu. Lūgums apskatīt zemāk esošo saiti. +error.hyperlink.solution=Uzmeklēt risinājumu +error.lookupPermissionMessage=Cryptomator var tiešsaistē uzmeklēt šī sarežģījuma risinājumu. No pašreizējās IP adreses tiks nosūtīts pieprasījums uz mūsu sarežģījumu datubāzi. +error.dismiss=Atmest +error.lookUpSolution=Uzmeklēt risinājumu # Defaults defaults.vault.vaultName=Glabātava @@ -28,243 +34,497 @@ defaults.vault.vaultName=Glabātava # Tray Menu traymenu.showMainWindow=Rādīt traymenu.showPreferencesWindow=Iestatījumi -traymenu.lockAllVaults=Aizslēgt visu +traymenu.lockAllVaults=Aizslēgt visas traymenu.quitApplication=Iziet traymenu.vault.unlock=Atslēgt traymenu.vault.lock=Aizslēgt traymenu.vault.reveal=Atklāt # Add Vault Wizard -addvaultwizard.title=Pievienot glabātuvi +addvaultwizard.title=Pievienot glabātavu ## New +addvaultwizard.new.title=Pievienot jaunu glabātavu ### Name -addvaultwizard.new.nameInstruction=Izvēlies glabātuves nosaukumu -addvaultwizard.new.namePrompt=Glabātuves nosaukums +addvaultwizard.new.nameInstruction=Jāizvēlas glabātavas nosaukums +addvaultwizard.new.namePrompt=Glabātavas nosaukums ### Location -addvaultwizard.new.locationInstruction=Kur Cryptomator vajadzētu glabāt jūsu glabātuves šifrētos failus? +addvaultwizard.new.locationInstruction=Kur Cryptomator vajadzētu saglabāt glabātavs šifrētās datnes? +addvaultwizard.new.locationLoading=Pārbauda noklusējuma mākoņkrātuvju mapes vietējā datņu sistēmā… addvaultwizard.new.locationLabel=Krātuves atrašanās vieta addvaultwizard.new.locationPrompt=… addvaultwizard.new.directoryPickerLabel=Pielāgota atrašanās vieta -addvaultwizard.new.directoryPickerButton=Izvēlies... -addvaultwizard.new.directoryPickerTitle=Izvēlēties mapi -addvaultwizard.new.invalidName=Nederīgs krātuves nosaukums -addvaultwizard.new.validName=Derīgs krātuves nosaukums -addvaultwizard.new.validCharacters.message=Krātuves nosaukums var saturēt šādas rakstzīmes: +addvaultwizard.new.directoryPickerButton=Izvēlēties… +addvaultwizard.new.directoryPickerTitle=Atlasīt mapi +addvaultwizard.new.fileAlreadyExists=Jau pastāv datne vai mape ar glabātavas nosaukumu +addvaultwizard.new.locationDoesNotExist=Mape norādītajā ceļā nepastāv vai tai nevar piekļūt +addvaultwizard.new.locationIsNotWritable=Norādītajā ceļā nav rakstīšanas piekļuves +addvaultwizard.new.locationIsOk=Glabātavai piemērota atrašanās vieta +addvaultwizard.new.invalidName=Nederīgs glabātavas nosaukums +addvaultwizard.new.validName=Derīgs glabātavas nosaukums +addvaultwizard.new.validCharacters.message=Glabātavas nosaukums var saturēt šādas rakstzīmes: addvaultwizard.new.validCharacters.chars=Burtus (piemēram: a, ж vai 수) addvaultwizard.new.validCharacters.numbers=Skaitļus -addvaultwizard.new.validCharacters.dashes=Defise (%s) vai pasvītra (%s) +addvaultwizard.new.validCharacters.dashes=Savienojuma zīme (%s) vai pasvītra (%s) ### Expert Settings +addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=Iespējot lietpratēju iestatījumus +addvaultwizard.new.expertSettings.shorteningThreshold.invalid=Jāievada vērtība starp 36 un 220 (pēc noklusējuma 220) +addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=Atvērt dokumentāciju, lai uzzinātu vairāk. +addvaultwizard.new.expertSettings.shorteningThreshold.title=Lielākais pieļaujamais šifrēto datņu nosaukumu garums +addvaultwizard.new.expertSettings.shorteningThreshold.valid=Derīgs ### Password -addvaultwizard.new.createVaultBtn=Izveidot glabātuvi -addvaultwizard.new.generateRecoveryKeyChoice=Jūs nevarēsiet piekļūt saviem datiem bez paroles. Vai vēlaties atkopšanas atslēgu gadījumam, kad esat pazaudējis paroli? -addvaultwizard.new.generateRecoveryKeyChoice.yes=Jā, lūdzu, labāk droši nekā nekā +addvaultwizard.new.createVaultBtn=Izveidot glabātavu +addvaultwizard.new.generateRecoveryKeyChoice=Bez paroles nebūs iespējams piekļūt saviem datiem. Vai ir nepieciešama atkopes atslēga gadījumam, ja tiks pazaudēta parole? +addvaultwizard.new.generateRecoveryKeyChoice.yes=Jā, lūdzu, labāk droši nekā pēc tam nožēlot addvaultwizard.new.generateRecoveryKeyChoice.no=Nē, paldies, es nezaudēšu savu paroli ### Information -addvault.new.readme.storageLocation.fileName=IMPORTANT.rtf -addvault.new.readme.storageLocation.1=⚠️ GLABĀTUVES DATNES ⚠️ -addvault.new.readme.storageLocation.2=Šī ir jūsu glabātuves atrašanās vieta. -addvault.new.readme.storageLocation.3=Nekādā gadījumā -addvault.new.readme.storageLocation.4=mainīt visas datnes šajā mapē vai -addvault.new.readme.storageLocation.5=ielīmējiet visas datnes šifrēšanai šajā mapē. -addvault.new.readme.storageLocation.6=Ja vēlaties šifrēt datnes un skatīt glabātuves saturu, rīkojieties šādi: -addvault.new.readme.storageLocation.7=Pievienot šo glabātuvi Cryptomator. -addvault.new.readme.storageLocation.8=Atslēgt glabātuvi ar Cryptomator. -addvault.new.readme.storageLocation.9=Atveriet piekļuves vietu, noklikšķinot uz pogas "Atklāt". -addvault.new.readme.storageLocation.10=Ja hums ir nepieciešama palīdzība, apmeklējiet dokumentāciju: %s +addvault.new.readme.storageLocation.fileName=SVARĪGI.rtf +addvault.new.readme.storageLocation.1=⚠️ GLABĀTAVAS DATNES ⚠️ +addvault.new.readme.storageLocation.2=Šī ir glabātavas atrašanās vieta. +addvault.new.readme.storageLocation.3=Nekādā gadījumā šajā mapē +addvault.new.readme.storageLocation.4=• neizmainīt jebkādas datnes vai +addvault.new.readme.storageLocation.5=• neielīmēt jebkādas datnes šifrēšanai. +addvault.new.readme.storageLocation.6=Ja ir vēlme šifrēt datnes un apskatīt glabātavas saturu, jārīkojas šādi: +addvault.new.readme.storageLocation.7=1. Jāpievieno šī glabātava Cryptomator. +addvault.new.readme.storageLocation.8=2. Jāatslēdz glabātava Cryptomator. +addvault.new.readme.storageLocation.9=3. Jāatver piekļuves vieta ar pogu "Atklāt". +addvault.new.readme.storageLocation.10=Ja ir nepieciešama palīdzība, jāapmeklē dokumentācija: %s addvault.new.readme.accessLocation.fileName=WELCOME.rtf addvault.new.readme.accessLocation.1=🔐️ ŠIFRĒTS SĒJUMS 🔐️ -addvault.new.readme.accessLocation.2=Šī ir jūsu glabātuves piekļuves vieta. -addvault.new.readme.accessLocation.3=Visas šeit pievienotās datnes tiks šifrētas ar Cryptomator. Jūs variet ar to strādāt kā ar jebkuru citu disku/mapi. Šis it tikai atšifrēts satura skats, jūsu dati diskā visu laiku ir šifrēti. -addvault.new.readme.accessLocation.4=Jūs variet droši noņemt šo datni. +addvault.new.readme.accessLocation.2=Šī ir glabātavas piekļuves vieta. +addvault.new.readme.accessLocation.3=Cryptomator šifrēs jebkuru šim sējumam pievienoto datni. Ar to var strādāt kā ar jebkuru citu diskzini/mapi. Šis ir tikai atšifrēta satura skats, datnes cietajā diskā visu laiku ir šifrētas. +addvault.new.readme.accessLocation.4=Šo datni var droši noņemt. ## Existing -addvaultwizard.existing.chooseBtn=Izvēlies... +addvaultwizard.existing.title=Pievienot esošu glabātavu +addvaultwizard.existing.instruction=Jāizvēlas esošas glabātavas datne "vault.cryptomator". Ja pastāv tikai datne ar nosaukumu "masterkey.cryptomator", tad jāatlasā tā. +addvaultwizard.existing.chooseBtn=Izvēlēties… +addvaultwizard.existing.filePickerTitle=Atlasīt glabātavas datni +addvaultwizard.existing.filePickerMimeDesc=Cryptomator glabātava ## Success -addvaultwizard.success.nextStepsInstructions=Pievienota "%s" glabātuve.\nlai piekļūtu vai pievienotu datus, jums šo glabātuvi ir jāatslēdz. Vai arī jūs to variet atslēgt vēlāk jebkurā laikā. +addvaultwizard.success.nextStepsInstructions=Pievienota glabātava "%s".\nIr nepieciešams atslēgt šo glabātavu, lai piekļūtu vai pievienotu saturu. To var arī darīt jebkurā brīdi vēlāk. addvaultwizard.success.unlockNow=Atslēgt tagad # Remove Vault -removeVault.title=Noņemt glabātuvi -removeVault.description=Šis tikai liks Cryptomator aizmirst šo glabātuvi. Jūs to variet pievienot vēlāk atkārtoti. Nekādi šifrētie dati no diska netiks dzēsti. +removeVault.title=Noņemt "%s" +removeVault.message=Noņemt glabātavu? +removeVault.description=Šis tikai liks Cryptomator aizmirst šo glabātavu. To var pievienot atkārtoti. Nekādi šifrētie dati no cietā diska netiks izdzēsti. # Change Password -changepassword.title=Mainīt paroli -changepassword.enterOldPassword=Ievadiet "%s" pašreizējo paroli -changepassword.finalConfirmation=Es saprotu, ka paroles aizmiršanas gadījumā, es vairs nevarēšu piekļūt saviem datiem +changepassword.title=Nomainīt paroli +changepassword.enterOldPassword=Jāievada "%s" pašreizējā parole +changepassword.finalConfirmation=Es saprotu, ka es vairs nevarēšu piekļūt saviem datiem, ja aizmirsīšu savu paroli # Forget Password forgetPassword.title=Aizmirst paroli -forgetPassword.description=Tas dzēsīs saglabāto glabātuves paroli no jūsu sistēmas atslēgu krātuves. +forgetPassword.message=Aizmirst saglabāto paroli? +forgetPassword.description=Šī darbība izdzēsīs saglabāto glabātavas paroli no sistēmas atslēgu saišķa. forgetPassword.confirmBtn=Aizmirst paroli # Unlock -unlock.passwordPrompt=Ievadiet "%s" paroli: +unlock.title=Atslēgt "%s" +unlock.passwordPrompt=Jāievada "%s" parole: +unlock.savePassword=Atcerēties paroli unlock.unlockBtn=Atslēgt ## Select -unlock.chooseMasterkey.filePickerTitle=Atlasīt galveno atslēgas datni +unlock.chooseMasterkey.message=Galvenās atslēgas datne nav atrasta +unlock.chooseMasterkey.description=Cryptomator nevarēja atrast galvenās atslēgas datni glabātavai "%s". Lūgums pašrocīgi izvēlēties atslēgas datni. +unlock.chooseMasterkey.filePickerTitle=Atlasīt galvenās atslēgas datni +unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator galvenā atslēga ## Success +unlock.success.message=Atslēgšana sekmīga +unlock.success.description=Glabātavas "%s" saturs tagad ir pieejams tās piemontēšanas vietā. +unlock.success.rememberChoice=Atcerēties manu izvēli, vairs nevaicāt unlock.success.revealBtn=Atklāt disku ## Failure +unlock.error.customPath.message=Nav iespējams piemontēt glabātavu pielāgotam ceļam +unlock.error.customPath.description.notSupported=Ja ir vēlēšanās turpināt izmantot pielāgoto ceļu, lūgums doties uz iestatījumiem un atlasīt sējuma veidu, kas to nodrošina. Pretējā gadījumā jādodas uz glabātavas iespējam un jāizvēlas atbalstīts piemontēšanas punkts. +unlock.error.customPath.description.notExists=Pielāgotais piemontēšanas ceļš nepastāv. Vai nu tas ir jāizveido vietējā datņu sistēmā, vai arī jānomaina glabātavas iespējās. +unlock.error.customPath.description.inUse=Diska burts vai pielāgotais piemontēšanas ceļš "%s" jau tiek izmantots. +unlock.error.customPath.description.hideawayNotDir=Pagaidu slēpto datni "%3$s", ko izmanto atslēgšanai, nevarēja noņemt. Lūgums pārbaudīt datni un tad to pašrocīgi izdzēst. +unlock.error.customPath.description.couldNotBeCleaned=Glabātavu nevarēja piemontēt ceļā "%s". Lūgums mēģināt vēlreiz vai izvēlēties citu ceļu. +unlock.error.customPath.description.notEmptyDir=Pielāgotais piemontēšanas ceļš "%s" nav tukša mape. Lūgums izvēlēties tukšu mapi un mēģināt vēlreiz. +unlock.error.customPath.description.generic=Šai glabātavai tika atlasīts pielāgots piemontēšanas ceļš, bet tās izmantošana neizdevās un beidzās ar ziņojumu: %2$s +unlock.error.restartRequired.message=Neizdevās atslēgt glabātavu +unlock.error.restartRequired.description=Jāmaina sējuma veids glabātavas iespējās vai Cryptomator atkārtoti jāpalaiž. +unlock.error.title="%s" atslēgšana neizdevās ## Hub +hub.noKeychain.message=Nebija iespējams piekļūt ierīces atslēgai +hub.noKeychain.description=Lai atslēgtu Hub glabātavas, ir nepieciešama ierīces atslēga, kas tiek droši uzglabāta atslēgu saišķī. Lai tuprinātu, iestatījumos jāiespējo "%s" un jāatlasa atslēgu saišķis. +hub.noKeychain.openBtn=Atvērt iestatījumus ### Waiting +hub.auth.message=Gaida autentificēšanu… +hub.auth.description=Vajadzētu notikt automātiskai pārvirzīšanai uz pieteikšanās lapu. +hub.auth.loginLink=Pārvirzīšana nenotika? Jāklikšķina šeit, lai atvērtu. ### Receive Key +hub.receive.message=Apstrādā atbildi… +hub.receive.description=Cryptomator saņem un apstrādā atbildi no Hub. Lūgums uzgaidīt. ### Register Device +hub.register.message=Jauna ierīce +hub.register.description=Šī ir pirmā piekļuve Hub no šīs ierīces. Lūgums to reģistrēt ar savu konta atslēgu. hub.register.nameLabel=Ierīces nosaukums +hub.register.invalidAccountKeyLabel=Nederīga konta atslēga +hub.register.registerBtn=Reģistrēties ### Register Device Legacy +hub.register.legacy.occupiedMsg=Nosaukums jau tiek izmantots +hub.register.legacy.description=Šī ir pirmā piekļuve Hub no šīs ierīces. Lūgums to reģistrēt. ### Registration Success +hub.registerSuccess.message=Ierīce reģistrēta +hub.registerSuccess.description=Ierīce tika sekmīgi reģistrēta. Tagad var turpināt ar glabātavas atslēgšanu. hub.registerSuccess.unlockBtn=Atslēgt +hub.registerSuccess.legacy.description=Lai piekļūtu glabātavai, tās īpašniekam ir papildus jāpilnvaro ierīce. ### Registration Failed +hub.registerFailed.message=Ierīces reģistrācija neizdevās +hub.registerFailed.description.generic=Reģistrēšanās laikā atgadījās kļūda. Vairāk informācijas ir atrodama lietotnes žurnālā. +hub.registerFailed.description.deviceAlreadyExists=Šī ierīce jau ir reģistrēta citam lietotājam. Jamēģina nomainīt lietotāja kontu vai izmantot citu ierīci. ### Unauthorized +hub.unauthorized.message=Piekļuve atteikta +hub.unauthorized.description=Nav pilnvaras atvērt šo glabātavu. Jāsazinās ar glabātavas īpašnieku, lai pieprasītu piekļuvi. ### Requires Account Initialization +hub.requireAccountInit.message=Nepieciešama darbība +hub.requireAccountInit.description.0=Lai turpinātu, lūgums pabeigt nepieciešamos soļus savā +hub.requireAccountInit.description.1=Hub lietotāja profilā +hub.requireAccountInit.description.2=. ### License Exceeded +hub.invalidLicense.message=Nederīga Hub licence +hub.invalidLicense.description=Cryptomator Hub instancei ir nederīga licence. Lūgums ziņot Hub pārvaldītājam, lai uzlabo vai atjauno licenci. # Lock ## Force +lock.forced.message=Aizslēgšana neizdevās +lock.forced.description="%s" aizslēgšana tika aizturēta nepabeigtu darbību vai atvērtu datņu dēļ. Var veikt šīs glabātavas piespiedu aizslēgšanu, tomēr datņu sistēmas darbību pārtraukšana var beigties ar nesaglabāttiem vai zaudētiem datiem. +lock.forced.retryBtn=Mēģināt vēlreiz +lock.forced.forceBtn=Piespiedu aizslēgšana ## Failure +lock.fail.message=Glabātavas aizslēgšana neizdevās +lock.fail.description=Glabātavu "%s" nevarēja aizslēgt. Jānodrošina, ka nesaglabātais darbs ir saglabāts kaut kur citur un svarīgas lasīšanas/rakstīšanas darbības ir pabeigtas. Lai varētu aizvērt glabātavu, jānobeidz Cryptomator process. # Migration -migration.title=Jaunināt glabātuvi +migration.title=Jaunināt glabātavu ## Start -migration.start.header=Jaunināt glabātuvi +migration.start.header=Jaunināt glabātavu +migration.start.text=Lai atvērtu savu glabātavu "%s" šajā jaunajā Cryptomator versijā, to ir nepieciešams jaunināt uz jaunāku veidolu. Pirms to darīt, ir jāzina šis: +migration.start.remarkUndone=Šo jauninājumu nevar atsaukt. +migration.start.remarkVersions=Vecākās Cryptomator versijās nevarēs atvērt jaunināto glabātavu. +migration.start.remarkCanRun=Jāpārliecinās, ka katrā ierīcē, kurā piekļūst glabātavai, var darboties šī Cryptomator versija. +migration.start.remarkSynced=Jāpārliecinās, ka glabātava šajā un citās ierīcēs ir pilnībā sinhronizēta, pirms to jaunina. +migration.start.confirm=Izlasīju un sapratu augstāk esošo informāciju ## Run -migration.run.enterPassword=Ievadiet "%s" paroli -migration.run.startMigrationBtn=Migrēt glabātuvi +migration.run.enterPassword=Jāievada "%s" parole +migration.run.startMigrationBtn=Pārcelt glabātavu +migration.run.progressHint=Tas varētu aizņemt kādu laiku… ## Success -migration.success.nextStepsInstructions="%s" sekmīgi migrēta.\nJūs tagad variet atslēgt jūsu glabātuvi. +migration.success.nextStepsInstructions="%s" sekmīgi pārcelta.\nTagad var atslēgt savu glabātavu. migration.success.unlockNow=Atslēgt tagad ## Missing file system capabilities migration.error.missingFileSystemCapabilities.title=Neatbalstīta datņu sistēma -migration.error.missingFileSystemCapabilities.description=Migrācija netika uzsāta, jo jūsu glabātuve atrodās neadekvātā datņu sistēmā. +migration.error.missingFileSystemCapabilities.description=Pārcelšana netika uzsākta, jo glabātava atrodas neatbilstošā datņu sistēmā. migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Datņu sistēma neatbalsta garus datņu nosaukumus. migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Datņu sistēma neatbalsta garus ceļu nosaukumus. -migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Nav atļaujas lasīt no datņu sistēmas. -migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Nav atļaujas rakstīt datņu sistēmā. +migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Datņu sistēma nav lasāma. +migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Datņu sistēma nav rakstāma. ## Impossible +migration.impossible.heading=Nebija iespējams pārcelt glabātavu +migration.impossible.reason=Glabātavu nevar automātiski pārcelt, jo tās glabāšanas vieta vai piekļuves punkts nav saderīgs. +migration.impossible.moreInfo=Glabātavu joprojām var atvērt ar vecāku versiju. Lai iegūdu norādes par to, kā pašrocīgi pārcelt glabātavu, jāapmeklē # Health Check ## Start +health.title="%s" veseluma pārbaude +health.intro.header=Veseluma pārbaude +health.intro.text=Veseluma pārbaude ir pārbaužu kopums, lai noteiktu un iespējami atrisinātu nebūšanas glabātavas iekšējā uzbūvē. Lūgums paturēt prātā: +health.intro.remarkSync=Jānodrošina, ka visas ierīces ir pilnībā sinhronizētas, tas atrisina vairumu sarežģījumu. +health.intro.remarkFix=Ne visus sarežģījumus var atrisināt. +health.intro.remarkBackup=Ja dati ir bojāti, līdzēt var tikai rezerves kopija. +health.intro.affirmation=Es izlasīju un sapratu augstāk esošo informāciju ## Start Failure +health.fail.header=Kļūda glabātavas konfigurācijas ielādēšanā +health.fail.ioError=Atgadījās kļūda konfigurācijas datnes piekļūšanas un lasīšanas laikā. +health.fail.parseError=Glabātavas konfigurācijas apstrādes laikā atgadījās kļūda. +health.fail.moreInfo=Vairāk informācijas ## Check Selection +health.checkList.description=Jāatlasa pārbaudes kreisajā pusē esošajā sarakstā vai jāizmanto zemāk esošās pogas. +health.checkList.selectAllButton=Atlasīt visas pārbaudes +health.checkList.deselectAllButton=Atcelt visu pārbaužu atlasīšanu +health.check.runBatchBtn=Izpildīt atlasītās pārbaudes ## Detail view +health.check.detail.noSelectedCheck=Jāatlasa pabeigta veselības pārbaude kreisajā pusē esošajā sarakstā, lai apskatītu iznākumu. +health.check.detail.checkScheduled=Pārbaude ir ieplānota. +health.check.detail.checkRunning=Pārbaude pašlaik izpildās… +health.check.detail.checkSkipped=Pārbaude netika atlasīta izpildīšanai. +health.check.detail.checkFinished=Pārbaude beidzās sekmīgi. +health.check.detail.checkFinishedAndFound=Pārbaude beidza izpildīteis. Lūgums pārskatīt iznākumu. +health.check.detail.checkFailed=Pārbaude beidza darboties kļūdas dēļ. +health.check.detail.checkCancelled=Pārbaude tika atcelta. +health.check.detail.listFilters.label=Atlasīt +health.check.detail.fixAllSpecificBtn=Salabot visu ar veidu +health.check.exportBtn=Izgūt pārskatu ## Result view +health.result.severityFilter.all=Nozīmīgums - Visi +health.result.severityFilter.good=Labi +health.result.severityFilter.info=Uzziņa +health.result.severityFilter.warn=Brīdinājums +health.result.severityFilter.crit=Kritiski +health.result.severityTip.good=Nozīmīgums: labi\nGlabātavas uzbūve ir kārtībā. +health.result.severityTip.info=Nozīmīgums: uzziņa\nGlabātavas uzbūve ir neskarta; ieteicama labošana. +health.result.severityTip.warn=Nozīmīgums: brīdinājums\nGlabātavas uzbūve ir bojāta; labošana ir ļoti ieteicama. +health.result.severityTip.crit=Nozīmīgums: kritiski\nGlabātavas uzbūve ir bojāta, noteikti datu zaudējumi. +health.result.fixStateFilter.all=Labošanas stāvoklis - Viss +health.result.fixStateFilter.fixable=Labojams +health.result.fixStateFilter.notFixable=Nav salabojams +health.result.fixStateFilter.fixing=Labo… +health.result.fixStateFilter.fixed=Salabots +health.result.fixStateFilter.fixFailed=Labošana neizdevās ## Fix Application +health.fix.fixBtn=Salabot +health.fix.successTip=Labojums sekmīgs +health.fix.failTip=Salabošana neizdevās. Jāskatās žurnālā, lai iegūtu vairāk informācijas # Preferences preferences.title=Iestatījumi ## General preferences.general=Vispārēji -preferences.general.startHidden=Paslēpt logu, kad startē Cryptomator +preferences.general.startHidden=Paslēpt logu Cryptomator palaišanas laikā +preferences.general.autoCloseVaults=Aizslēgt glabātavas bez vaicāšanas, kad iziet no lietotnes preferences.general.debugLogging=Iespējot atkļūdošanas žurnalēšanu -preferences.general.autoStart=Palaist Cryptomator pie sistēmas startēšanas +preferences.general.debugDirectory=Atklāt žurnāla datnes +preferences.general.autoStart=Palaist Cryptomator pēc sistēmas uzsākšanās +preferences.general.keychainBackend=Glabāt paroles ar +preferences.general.quickAccessService=Pievienot atslēgtās glabātavas ātrās piekļuves apgabalam ## Interface +preferences.interface=Saskarne +preferences.interface.theme=Izskats un izjūta +preferences.interface.theme.automatic=Automātisks preferences.interface.theme.dark=Tumšs preferences.interface.theme.light=Gaišs +preferences.interface.unlockThemes=Atslēgt tumšo izskatu +preferences.interface.language=Valoda (nepieciešama atkārtota palaišana) +preferences.interface.language.auto=Sistēmas noklusējums +preferences.interface.interfaceOrientation=Saskarnes novietojums +preferences.interface.interfaceOrientation.ltr=No kreisās uz labo +preferences.interface.interfaceOrientation.rtl=No labās uz kreiso +preferences.interface.showTrayIcon=Rādīt teknes ikonu (nepieciešama atkārtota palaišana) +preferences.interface.compactMode=Iespējot blīvu glabātavu sarakstu ## Volume -preferences.volume=Virtuāls disks +preferences.volume=Virtuālais disks +preferences.volume.type=Noklusējuma sējuma veids +preferences.volume.type.automatic=Automātisks +preferences.volume.docsTooltip=Jāatver dokumentācija, lai uzzinātu vairāk par dažādiem sējumu veidiem. +preferences.volume.fuseRestartRequired=Lai pielietotu izmaiņas, Cryptomator ir nepieciešams palaist atkārtoti. +preferences.volume.tcp.port=Noklusējuma TCP ports +preferences.volume.supportedFeatures=Izvēlētais sējuma veids nodrošina šādas iespējas: +preferences.volume.feature.mountAuto=Automātisku piemontēšanas vietas atlasīšanu +preferences.volume.feature.mountToDir=Pielāgotu mapi kā piemontēšanas vietu +preferences.volume.feature.mountToDriveLetter=Diska burts kā piemontēšanas vieta +preferences.volume.feature.mountFlags=Pielāgotas piemontēšanas iespējas +preferences.volume.feature.readOnly=Tikai lasāma piemontēšana ## Updates preferences.updates=Atjauninājumi preferences.updates.currentVersion=Pašreizējā versija: %s preferences.updates.autoUpdateCheck=Automātiski pārbaudīt atjauninājumus preferences.updates.checkNowBtn=Pārbaudīt tagad preferences.updates.updateAvailable=Pieejams atjauninājums uz versiju %s. +preferences.updates.lastUpdateCheck=Pēdējā pārbaude: %s +preferences.updates.lastUpdateCheck.never=nekad +preferences.updates.lastUpdateCheck.recently=nesen +preferences.updates.lastUpdateCheck.daysAgo=Pirms %s dienām +preferences.updates.lastUpdateCheck.hoursAgo=Pirms %s stundām +preferences.updates.checkFailed=Atjauninājumu uzmeklēšana neizdevās. Lūgums pārbaudīt savu interneta savienojumu vai vēlāk mēģināt vēlreiz. +preferences.updates.upToDate=Cryptomator ir jaunākā versija. ## Contribution +preferences.contribute=Atbalstīt mūs +preferences.contribute.registeredFor=Atbalstītāja sertifikāts ir reģistrēts %s +preferences.contribute.noCertificate=Atbalsti Cryptomator un saņem atbalstītāja sertifikātu! Tas ir kā licences atslēga, bet lieliskiem cilvēkiem, kuri izmanto brīvo programmatūru. ;-) +preferences.contribute.getCertificate=Vēl nav tāda? Uzzini, kā to var iegūt! +preferences.contribute.promptText=Šeit jāielīmē atbalstītāja sertifikāta kods +preferences.contribute.thankYou=Paldies par Cryptomator atvērtā pirmkoda izstrādes atbalstīšanu! +preferences.contribute.donate=Ziedot +preferences.contribute.sponsor=Pabalstīt ### Remove License Key Dialog +removeCert.title=Noņemt sertifikātu +removeCert.message=Noņemt atbalstītāja sertifikātu? +removeCert.description=Šis neietekmē Cryptomator pamata iespējas. Nav ne ierobežota piekļuve glabātavām, ne pazemināts drošības līmenis. #<-- Add entries for donations and code/translation/documentation contribution --> ## About -preferences.about=Par lietotni +preferences.about=Par # Vault Statistics +stats.title=%s pārskats +stats.cacheHitRate=Kešatmiņas trāpījumu attiecība ## Read +stats.read.throughput.idle=Lasīšana: dīkstāvē +stats.read.throughput.kibs=Lasīšana: %.2f KiB/s +stats.read.throughput.mibs=Lasīšana: %.2f MiB/s +stats.read.total.data.none=Lasītie dati: - +stats.read.total.data.kib=Lasītie dati: %.1f KiB +stats.read.total.data.mib=Lasītie dati: %.1f MiB +stats.read.total.data.gib=Lasītie dati: %.1f GiB +stats.decr.total.data.none=Atšifrētie dati: - +stats.decr.total.data.kib=Atšifrētie dati: %.1f KiB +stats.decr.total.data.mib=Atšifrētie dati: %.1f MiB +stats.decr.total.data.gib=Atšifrētie dati: %.1f GiB +stats.read.accessCount=Lasīšanas pavisam: %d ## Write +stats.write.throughput.idle=Rakstīšana: dīkstāvē +stats.write.throughput.kibs=Rakstīšana: %.2f KiB/s +stats.write.throughput.mibs=Rakstīšana: %.2f MiB/s +stats.write.total.data.none=Rakstītie dati: - +stats.write.total.data.kib=Rakstītie dati: %.1f KiB +stats.write.total.data.mib=Rakstītie dati: %.1f MiB +stats.write.total.data.gib=Rakstītie dati: %.1f GiB +stats.encr.total.data.none=Šifrētie dati: - +stats.encr.total.data.kib=Šifrētie dati: %.1f KiB +stats.encr.total.data.mib=Šifrētie dati: %.1f MiB +stats.encr.total.data.gib=Šifrētie dati: %.1f GiB +stats.write.accessCount=Rakstīšanas pavisam: %d ## Accesses +stats.access.current=Piekļuves: %d +stats.access.total=Piekļuves pavisam: %d # Main Window ## Vault List -main.vaultlist.emptyList.onboardingInstruction=Spied šeit, lai pievienotu glabātuvi +main.vaultlist.emptyList.onboardingInstruction=Klikšķināt šeit, lai pievienotu glabātavu +main.vaultlist.contextMenu.remove=Noņemt… main.vaultlist.contextMenu.lock=Aizslēgt +main.vaultlist.contextMenu.unlock=Atslēgt… main.vaultlist.contextMenu.unlockNow=Atslēgt tagad +main.vaultlist.contextMenu.vaultoptions=Rādīt glabātavas iespējas main.vaultlist.contextMenu.reveal=Atklāt disku +main.vaultlist.addVaultBtn.menuItemNew=Izveidot jaunu glabātavu... +main.vaultlist.addVaultBtn.menuItemExisting=Atvērt esošu glabātavu... +main.vaultlist.showEventsButton.tooltip=Atvērt notikumu skatu ##Notificaition +main.notification.updateAvailable=Ir pieejams atjauninājums. +main.notification.support=Atbalstīt Cryptomator. ## Vault Detail ### Welcome -main.vaultDetail.welcomeOnboarding=Paldies, ka izvēlējāties Cryptomator lai aizsargātu jūsu datus. Ja jums nepieciešama palīdzība, iepazīstieties ar mūsu darba sākšanas ceļvežiem: +main.vaultDetail.welcomeOnboarding=Paldies par izvēlēšanos izmantot Cryptomator, lai aizsargātu savas datnes! Ja ir nepieciešama jebkāda palīdzība, ir vērts ieskatīties mūsu darba uzsākšanas norādēs: ### Locked -main.vaultDetail.lockedStatus=AIZSLĒGTS +main.vaultDetail.lockedStatus=AIZSLĒGTA +main.vaultDetail.unlockBtn=Atslēgt… main.vaultDetail.unlockNowBtn=Atslēgt tagad -main.vaultDetail.optionsBtn=Glabātuves opcijas +main.vaultDetail.optionsBtn=Glabātavas iespējas +main.vaultDetail.passwordSavedInKeychain=Parole saglabāta +main.vaultDetail.share=Kopīgot… ### Unlocked main.vaultDetail.unlockedStatus=ATSLĒGTS -main.vaultDetail.accessLocation=Jūsu glabātuves saturs ir pieejams šeit: +main.vaultDetail.accessLocation=Glabātavas saturs ir pieejams šeit: main.vaultDetail.revealBtn=Atklāt disku +main.vaultDetail.copyUri=Ievietot URI starpliktuvē main.vaultDetail.lockBtn=Aizslēgt -main.vaultDetail.bytesPerSecondRead=Nolasīts: +main.vaultDetail.bytesPerSecondRead=Lasīšana: +main.vaultDetail.bytesPerSecondWritten=Rakstīšana: main.vaultDetail.throughput.idle=dīkstāvē +main.vaultDetail.throughput.kbps=%.1f KiB/s main.vaultDetail.throughput.mbps=%.1f MiB/s +main.vaultDetail.stats=Glabātavas pārskats +main.vaultDetail.locateEncryptedFileBtn=Noteikt šifrētas datnes atrašanās vietu +main.vaultDetail.locateEncryptedFileBtn.tooltip=Jāizvēlas datne no savas glabātavas, lai noteiktu tai atbilstošās šifrētās datnes atrašanās vietu +main.vaultDetail.encryptedPathsCopied=Ceļi ievietoti starpliktuvē. +main.vaultDetail.locateEncrypted.filePickerTitle=Atlasīt glabātavā esošu datni +main.vaultDetail.decryptName.buttonLabel=Atšifrēt datnes nosaukumu +main.vaultDetail.decryptName.tooltip=Izvēlēties šifrētu glabātavu, lai atšifrētu tās nosaukumu ### Missing -main.vaultDetail.missing.info=Cryptomator šajā ceļā nevarēja atrast glabātuvi. +main.vaultDetail.missing.info=Cryptomator šajā ceļā nevarēja atrast glabātavu. +main.vaultDetail.missing.recheck=Pārbaudīt atkārtoti +main.vaultDetail.missing.remove=Noņemt no glabātavu saraksta… +main.vaultDetail.missing.changeLocation=Mainīt glabātavas atrašanās vietu… ### Needs Migration -main.vaultDetail.migrateButton=Jaunināt glabātuvi -main.vaultDetail.migratePrompt=Lai jūsu varētu piekļūt glabātuvei to ir nepieciešms jaunināt uz jaunu formātu +main.vaultDetail.migrateButton=Jaunināt glabātavu +main.vaultDetail.migratePrompt=Glabātavu ir nepieciešams jaunināt uz jaunu veidolu, pirms tai varēs piekļūt ### Error +main.vaultDetail.error.info=Glabātavas ielādēšanas no diska laikā atgadījās kļūda. +main.vaultDetail.error.reload=Pārlādēt +main.vaultDetail.error.windowTitle=Kļūda glabātavas ielādēšanā # Wrong File Alert wrongFileAlert.title=Kā šifrēt datnes -wrongFileAlert.message=Vai jūs mēģinājāt šifrēt šīs datnes? -wrongFileAlert.description=Šim nolūkam Cryptomator jūsu sistēmas datņu pārvaldniekā nodrošina sējumus. -wrongFileAlert.instruction.0=Lai šifrētu datnes, sekojiet šiem soļiem: -wrongFileAlert.instruction.1=1. Atslēdziet jūsu glabātuvi. -wrongFileAlert.instruction.2=2. Spiediet uz "Atklāt", lai atvērtu sējumu jūsu datņu pārvaldniekā. -wrongFileAlert.instruction.3=Šim sējumam pievienojiet jūsu datnes. -wrongFileAlert.link=Lai iegūtu turpmāku palīdzību, apmeklējiet +wrongFileAlert.message=Vai mēģināji šifrēt šīs datnes? +wrongFileAlert.description=Šim nolūkam Cryptomator sistēmas datņu pārvaldniekā nodrošina sējumus. +wrongFileAlert.instruction.0=Lai šifrētu datnes, jāizpilda šādas darbības: +wrongFileAlert.instruction.1=1. Jāatslēdz sava glabātava. +wrongFileAlert.instruction.2=2. Jāklikšķina uz "Atklāt", lai atvērtu sējumu datņu pārvaldniekā. +wrongFileAlert.instruction.3=3. Šajā sējumā jāpievieno savas datnes. +wrongFileAlert.link=Lai iegūtu turpmāku palīdzību, jāapmeklē # Vault Options ## General vaultOptions.general=Vispārēji -vaultOptions.general.vaultName=Glabātuves nosaukums -vaultOptions.general.unlockAfterStartup=Atslēgt glabātuvi startējot Cryptomator +vaultOptions.general.vaultName=Glabātavas nosaukums +vaultOptions.general.autoLock.lockAfterTimePart1=Aizslēgt, kad dīkstāvē +vaultOptions.general.autoLock.lockAfterTimePart2=minūtes +vaultOptions.general.unlockAfterStartup=Atslēgt glabātavu pēc Cryptomator palaišanas +vaultOptions.general.actionAfterUnlock=Pēc sekmīgas atslēgšanas +vaultOptions.general.actionAfterUnlock.ignore=Nedarīt neko vaultOptions.general.actionAfterUnlock.reveal=Atklāt disku +vaultOptions.general.actionAfterUnlock.ask=Vaicāt +vaultOptions.general.startHealthCheckBtn=Uzsākt veseluma pārbaudi ## Mount -vaultOptions.mount=Montē -vaultOptions.mount.readonly=Tikai lasīt -vaultOptions.mount.customMountFlags=Pielāgoti montēšanas parametri +vaultOptions.mount=Piemontēšana +vaultOptions.mount.info=Atvērt virtuālā diska iestatījumus, lai mainītu noklusējuma vērtības. +vaultOptions.mount.readonly=Tikai lasāms +vaultOptions.mount.customMountFlags=Pielāgoti piemontēšanas karodziņi vaultOptions.mount.winDriveLetterOccupied=aizņemts -vaultOptions.mount.mountPoint=Montēšanas vieta -vaultOptions.mount.mountPoint.auto=Automātiski izvēlieties piemērotu vietu +vaultOptions.mount.mountPoint=Piemontēšanas vieta +vaultOptions.mount.mountPoint.auto=Automātiski izvēlēties piemērotu atrašanās vietu vaultOptions.mount.mountPoint.driveLetter=Izmantot piešķirtu diska burtu -vaultOptions.mount.mountPoint.directoryPickerButton=Izvēlies... +vaultOptions.mount.mountPoint.custom=Izmantot izvēlēto mapi +vaultOptions.mount.mountPoint.directoryPickerButton=Izvēlēties… +vaultOptions.mount.mountPoint.directoryPickerTitle=Izvēlēties mapi +vaultOptions.mount.volumeType.default=Noklusējums (%s) +vaultOptions.mount.volumeType.restartRequired=Lai izmantotu šo sējuma veidu, Cryptomator ir nepieciešams palaist atkārtoti. +vaultOptions.mount.volume.tcp.port=TCP ports +vaultOptions.mount.volume.type=Sējuma veids ## Master Key vaultOptions.masterkey=Parole -vaultOptions.masterkey.changePasswordBtn=Mainīt paroli -vaultOptions.masterkey.recoveryKeyExplanation=Atkopšanas atslēga ir jūsu vienīgais līdzeklis, lai atjaunotu piekļuvi glabātuvei, ja pazaudējat paroli. -vaultOptions.masterkey.showRecoveryKeyBtn=Rādīt atkopšanas atslēgu +vaultOptions.masterkey.changePasswordBtn=Nomainīt paroli +vaultOptions.masterkey.forgetSavedPasswordBtn=Aizmirst saglabāto paroli +vaultOptions.masterkey.recoveryKeyExplanation=Atkopes atslēga ir vienīgais līdzeklis, lai atjaunotu piekļuvi glabātuvei, ja tiek pazaudēta parole. +vaultOptions.masterkey.showRecoveryKeyBtn=Parādīt atkopes atslēgu +vaultOptions.masterkey.recoverPasswordBtn=Atiestatīt paroli ## Hub +vaultOptions.hub=Atkope +vaultOptions.hub.convertInfo=Atkopes atslēgu var imantot, lai ārkārtas gadījumā pārveidotu šo Hub glabātavu par uz paroli balstītu glabātavu. +vaultOptions.hub.convertBtn=Pārveidot par uz paroli balstītu glabātavu # Recovery Key ## Display Recovery Key -recoveryKey.create.description=Lai parādītu "%s" atjaunošanas atslēgu, ievadiet paroli: -recoveryKey.display.description=Šī atjaunošanas atslēga var tikt izmantota, lai atjaunotu "%s": -recoveryKey.display.StorageHints=Glabājiet to drošā vietā, piemēram:\n  • Uzglabājiet to, izmantojot paroļu pārvaldnieku\n  • Saglabājiet to USB zibatmiņā\n  • Izdrukājiet to uz papīra +recoveryKey.display.title=Parādīt atkopes atslēgu +recoveryKey.create.message=Nepieciešama parole +recoveryKey.create.description=Jāievada "%s" parole, lai parādītu tās atkopes atslēgu. +recoveryKey.display.description=Zemāk esošā atkopes atslēga var tikt izmantota, lai atjaunotu piekļuvi "%s": +recoveryKey.display.StorageHints=Tā ir jātur ļoti drošā vietā, piemēram:\n • jāglabā paroļu pārvaldniekā;\n • jāsaglabā USB zibatmiņā;\n • jāizdrukā uz papīra. ## Reset Password ### Enter Recovery Key -recoveryKey.recover.correctKey=Šī ir derīga atjaunošanas atslēga -recoveryKey.printout.heading=Cryptomator atjaunošanas atslēga\n"%s" \n +recoveryKey.recover.title=Atiestatīt paroli +recoveryKey.recover.prompt=Jāievada "%s" atkopes atslēga: +recoveryKey.recover.correctKey=Šī ir derīga atkopes atslēga +recoveryKey.recover.wrongKey=Šī ir citas glabātavas atkopes atslēga +recoveryKey.recover.invalidKey=Šī atkopes atslēga nav derīga +recoveryKey.printout.heading=Cryptomator atkopes atslēga\n"%s" \n ### Reset Password recoveryKey.recover.resetBtn=Atiestatīt ### Recovery Key Password Reset Success +recoveryKey.recover.resetSuccess.message=Paroles atiestatīšana sekmīga +recoveryKey.recover.resetSuccess.description=Savu glabātavu var atslēgt ar jauno paroli. # Convert Vault +convertVault.title=Pārveidot glabātavu +convertVault.convert.convertBtn.before=Pārveidot +convertVault.convert.convertBtn.processing=Pārveido… +convertVault.success.message=Pārveidošana sekmīga +convertVault.hubToPassword.success.description=Glabātavu tagad var atslēgt ar izvēlēto paroli bez nepieciešamības pēc Hub piekļuves. # New Password -newPassword.promptText=Ievadiet jauno paroli -newPassword.reenterPassword=Apstipriniet jauno paroli +newPassword.promptText=Jāievada jaunā parole +newPassword.reenterPassword=Apstiprināt jauno paroli newPassword.passwordsMatch=Parole sakrīt! newPassword.passwordsDoNotMatch=Paroles nesakrīt -passwordStrength.messageLabel.tooShort=Izmantojiet vismaz %d burtus +passwordStrength.messageLabel.tooShort=Jāizmanto vismaz %d rakstzīmes passwordStrength.messageLabel.0=Ļoti vāja passwordStrength.messageLabel.1=Vāja passwordStrength.messageLabel.2=Vidēja @@ -272,14 +532,91 @@ passwordStrength.messageLabel.3=Stipra passwordStrength.messageLabel.4=Ļoti stipra # Quit +quit.title=Iziet no lietotnes +quit.message=Nav atslēgtu glabātavu +quit.description=Lūgums apstiprināt iziešanu. Cryptomator kārtīgi aizslēgts visas atslēgtās glabātavas, lai novērstu datu zudumu. quit.lockAndQuitBtn=Aizslēgt un aizvērt # Forced Quit +quit.forced.message=Dažas glabātavas nevarēja aizslēgt +quit.forced.description=Glabātavu aizslēgšana tika aizturēta nepabeigtu darbību vai atvērtu datņu dēļ. Var veikt atlikušo glabātavu piespiedu aizslēgšanu, tomēr datņu sistēmas darbību pārtraukšana var beigties ar nesaglabāttiem vai zaudētiem datiem. +quit.forced.forceAndQuitBtn=Piespiest un aizvērt # Update Reminder +updateReminder.title=Atjauninājuma meklēšana +updateReminder.message=Meklēt atjauninājumus? +updateReminder.description=Esi lietas kursā par jaunām iespējām, kļūdu labojumiem un drošības uzlabojumiem! Mēs iesakām automātiski pārbaudīt, vai ir atjauninājumi. +updateReminder.notNow=Ne tagad +updateReminder.yesOnce=Jā, vienreiz +updateReminder.yesAutomatically=Jā, automātiski #Dokany Support End +dokanySupportEnd.title=Paziņojums par izbeigšanu +dokanySupportEnd.message=Dokany atbalsta beigas +dokanySupportEnd.description=Cryptomator vairs neatbalsta sējuma veidu Dokany. Iestatījumi tika pielāgoti, lai tagad izmantotu noklusējuma sējuma veidu. Noklusējuma veidu var apskatīt iestatījumos. +dokanySupportEnd.preferencesBtn=Atvērt iestatījumus #Retry If Readonly +retryIfReadonly.title=Ierobežota piekļuve glabātavai +retryIfReadonly.message=Nav rakstīšanas piekļuves glabātavas mapei +retryIfReadonly.description=Cryptomator nevar rakstīt glabātavas mapē. Var mainīt glabātavu, lai tā būtu tikai lasāma, un mēģināt vēlreiz. Šo iespēju var atspējot glabātavas iespējās. +retryIfReadonly.retry=Nomainīt un mēģināt vēlreiz # Share Vault +shareVault.title=Kopīgot glabātavu +shareVault.message=Vai Tu vēlētos kopīgot savu glabātavu ar citiem? +shareVault.description=Vienmēr jābūt uzmanīgam, kad kopīgo savu glabātavu ar citiem cilvēkiem. Īsumā - jāievēro šīs norādes: +shareVault.instruction.1=1. piekļuve šifrētajai glabātavas mapei jākopīgo caur mākoņkrātuvi; +shareVault.instruction.2=2. glabātavas parole jākopīgo drošā veidā. +shareVault.remarkBestPractices=Lai uzzinātu vairāk, jāieskatās labās prakses ieteikumos mūsu dokumentācijā. +shareVault.docsTooltip=Jāatver dokumentācija, lai uzzinātu vairāk par glabātavu kopīgošanu. +shareVault.hubAd.description=Drošs veids, kā darboties komandās +shareVault.hubAd.keyManagement=• Nulles zināšanu atslēgu pārvaldīšana +shareVault.hubAd.authentication=• Spēcīga autentificēšana +shareVault.hubAd.encryption=• Pilnīga šifrēšana +shareVault.visitHub=Apmeklēt Cryptomator Hub + +shareVault.hub.message=Kā kopīgot Hub glabātavu +shareVault.hub.description=Lai varētu kopīgot glabātavas saturu ar citu komandas dalībnieku, jāizpilda divi soļi: +shareVault.hub.instruction.1=1. Piekļuve šifrētajai glabātavas mapei jākopīgo caur mākoņkrātuvi. +shareVault.hub.instruction.2=2. Cryptomator Hub jānodrošina piekļuve komandas dalībniekam. +shareVault.hub.openHub=Atvērt Cryptomator Hub + +# Decrypt File Names +decryptNames.title=Atšifrēt datņu nosaukumus +decryptNames.filePicker.title=Atlasīt šifrētu datni +decryptNames.filePicker.extensionDescription=Cryptomator šifrēta datne +decryptNames.copyTable.tooltip=Ievietot tabulu starpliktuvē +decryptNames.clearTable.tooltip=Notīrīt tabulu +decryptNames.copyHint=Ievietot šūnas saturu starpliktuvē ar %s +decryptNames.dropZone.message=Nomest datnes vai klikšķināt, lai atlasītu +decryptNames.dropZone.error.vaultInternalFiles=Atlasītas glabātavas iekšējās datnes ar neatšifrējamu nosaukumu +decryptNames.dropZone.error.foreignFiles=Datnes nepieder glabātavai "%s" +decryptNames.dropZone.error.noDirIdBackup=Atlasīto datņu mapē nav datnes dirId.c9r +decryptNames.dropZone.error.generic=Neizdevās atšifrēt datņu nosaukumus + + +# Event View +eventView.title=Notikumi +eventView.filter.allVaults=Viss +eventView.clearListButton.tooltip=Notīrīt sarakstu +## event list entries +eventView.entry.vaultLocked.description=Atslēgt "%s", lai redzētu informāciju +eventView.entry.conflictResolved.message=Atrisināta nesaderība +eventView.entry.conflictResolved.showDecrypted=Parādīt atšifrēto datni +eventView.entry.conflictResolved.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu +eventView.entry.conflict.message=Nesaderības atrisināšana neizdevās +eventView.entry.conflict.showDecrypted=Parādīt atšifrēto, sākotnējo datni +eventView.entry.conflict.copyDecrypted=Ievietot starpliktuvē atšifrēto, sākotnējo datni +eventView.entry.conflict.showEncrypted=Parādīt nesaderīgo, šifrēto datni +eventView.entry.conflict.copyEncrypted=Ievietot starpliktuvē nesaderīgo, šifrēto ceļu +eventView.entry.decryptionFailed.message=Atšifrēšana neizdevās +eventView.entry.decryptionFailed.showEncrypted=Parādīt šifrēto datni +eventView.entry.decryptionFailed.copyEncrypted=Ievietot starpliktuvē šifrēto ceļu +eventView.entry.brokenDirFile.message=Bojāta mapes saite +eventView.entry.brokenDirFile.showEncrypted=Parādīt bojāto, šifrēto saiti +eventView.entry.brokenDirFile.copyEncrypted=Ievietot starpliktuvē bojātās saites ceļu +eventView.entry.brokenFileNode.message=Bojāts datņu sistēmas mezgls +eventView.entry.brokenFileNode.showEncrypted=Parādīt bojāto, šifrēto mezglu +eventView.entry.brokenFileNode.copyEncrypted=Ievietot starpliktuvē botjātā, šifrētā mezgla ceļu +eventView.entry.brokenFileNode.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu diff --git a/src/main/resources/i18n/strings_mk.properties b/src/main/resources/i18n/strings_mk.properties index 7ec6aa5f2..a2ea03831 100644 --- a/src/main/resources/i18n/strings_mk.properties +++ b/src/main/resources/i18n/strings_mk.properties @@ -167,3 +167,10 @@ vaultOptions.mount.mountPoint.directoryPickerButton=Избор… #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_mr.properties b/src/main/resources/i18n/strings_mr.properties index 16d2016cd..aa86df2bd 100644 --- a/src/main/resources/i18n/strings_mr.properties +++ b/src/main/resources/i18n/strings_mr.properties @@ -121,3 +121,10 @@ #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_nb.properties b/src/main/resources/i18n/strings_nb.properties index d80a58618..b13a55c7d 100644 --- a/src/main/resources/i18n/strings_nb.properties +++ b/src/main/resources/i18n/strings_nb.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Avvis ## Button generic.button.apply=Bruk generic.button.back=Tilbake @@ -281,7 +282,6 @@ preferences.title=Innstillinger ## General preferences.general=Generelt preferences.general.startHidden=Skjul vinduet når du starter Cryptomator -preferences.general.autoCloseVaults=Låsen åpner hvelv automatisk ved avslutning av programmet preferences.general.debugLogging=Aktiver loggføring av feilsøk preferences.general.debugDirectory=Vis loggfiler preferences.general.autoStart=Start Cryptomator ved systemstart @@ -413,7 +413,6 @@ main.vaultDetail.stats=Hvelvstatistikk main.vaultDetail.locateEncryptedFileBtn=Finn kryptert fil main.vaultDetail.locateEncryptedFileBtn.tooltip=Velg en fil fra hvelvet ditt for å finne den krypterte motparten main.vaultDetail.encryptedPathsCopied=Stier kopiert til utklippstavlen! -main.vaultDetail.filePickerTitle=Velg Fil I Hvelvet ### Missing main.vaultDetail.missing.info=Cryptomator kunne ikke finne et hvelv på denne søkestien. main.vaultDetail.missing.recheck=Kontroller igjen @@ -557,4 +556,10 @@ shareVault.hub.message=Hvordan dele et Hub-hvelv shareVault.hub.description=For å dele hvelvets innhold med et annet lagmedlem må du utføre to trinn: shareVault.hub.instruction.1=1. Del tilgang til den krypterte hvelvmappen via skylagring. shareVault.hub.instruction.2=2. Gi tilgang til lagmedlem i Cryptomator Hub. -shareVault.hub.openHub=Åpne Cryptomator hub \ No newline at end of file +shareVault.hub.openHub=Åpne Cryptomator hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_nl.properties b/src/main/resources/i18n/strings_nl.properties index 5a544e7d8..9875f5ef2 100644 --- a/src/main/resources/i18n/strings_nl.properties +++ b/src/main/resources/i18n/strings_nl.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Afwijzen ## Button generic.button.apply=Toepassen generic.button.back=Terug @@ -48,7 +49,7 @@ addvaultwizard.new.nameInstruction=Kies een naam voor de kluis addvaultwizard.new.namePrompt=Kluisnaam ### Location addvaultwizard.new.locationInstruction=Waar moet Cryptomator de versleutelde bestanden van je kluis opslaan? -addvaultwizard.new.locationLoading=Lokaal bestandssysteem controleren op standaard cloud opslag mappen… +addvaultwizard.new.locationLoading=Lokaal bestandssysteem controleren op standaard cloudopslagmappen… addvaultwizard.new.locationLabel=Opslaglocatie addvaultwizard.new.locationPrompt=… addvaultwizard.new.directoryPickerLabel=Andere locatie @@ -282,7 +283,7 @@ preferences.title=Voorkeuren ## General preferences.general=Algemeen preferences.general.startHidden=Verberg venster bij het opstarten van Cryptomator -preferences.general.autoCloseVaults=Open kluizen automatisch vergrendelen bij het afsluiten van de applicatie +preferences.general.autoCloseVaults=Vergrendel kluizen zonder te vragen bij het afsluiten van de toepassing preferences.general.debugLogging=Debug logging aanzetten preferences.general.debugDirectory=Logboekbestanden bekijken preferences.general.autoStart=Start Cryptomator als het systeem opstart @@ -394,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=Laat kluisinstellingen zien main.vaultlist.contextMenu.reveal=Toon Schijf main.vaultlist.addVaultBtn.menuItemNew=Nieuwe Kluis Aanmaken... main.vaultlist.addVaultBtn.menuItemExisting=Open Bestaande Kluis... +main.vaultlist.showEventsButton.tooltip=Afspraakweergave openen ##Notificaition main.notification.updateAvailable=Update beschikbaar. main.notification.support=Steun Cryptomator. @@ -422,7 +424,9 @@ main.vaultDetail.stats=Kluisstatistieken main.vaultDetail.locateEncryptedFileBtn=Zoek versleuteld bestand main.vaultDetail.locateEncryptedFileBtn.tooltip=Kies een bestand uit je kluis om de versleutelde tegenhanger te vinden main.vaultDetail.encryptedPathsCopied=Pad gekopieerd naar klembord! -main.vaultDetail.filePickerTitle=Selecteer bestand binnen de kluis +main.vaultDetail.locateEncrypted.filePickerTitle=Selecteer bestand binnen de kluis +main.vaultDetail.decryptName.buttonLabel=Bestandsnaam decoderen +main.vaultDetail.decryptName.tooltip=Kies een versleuteld kluisbestand om de naam te decoderen ### Missing main.vaultDetail.missing.info=Cryptomator kon op dit pad geen kluis vinden. main.vaultDetail.missing.recheck=Controleer nog eens @@ -576,4 +580,43 @@ shareVault.hub.message=Hoe een Hub kluis delen shareVault.hub.description=Om de inhoud van de kluis te delen met een ander teamlid, moet u twee stappen uitvoeren: shareVault.hub.instruction.1=1. Deel toegang van de versleutelde kluis map via de cloud opslag. shareVault.hub.instruction.2=2. Geef teamlid toegang in Cryptomator Hub. -shareVault.hub.openHub=Open Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Open Cryptomator Hub + +# Decrypt File Names +decryptNames.title=Bestandsnaam decoderen +decryptNames.filePicker.title=Selecteer versleuteld bestand +decryptNames.filePicker.extensionDescription=Cryptomator versleuteld bestand +decryptNames.copyTable.tooltip=Kopieer tabel +decryptNames.clearTable.tooltip=Wis tabel +decryptNames.copyHint=Kopieer cel inhoud met %s +decryptNames.dropZone.message=Sleep bestanden of klik om te selecteren +decryptNames.dropZone.error.vaultInternalFiles=Kluis interne bestanden zonder ontsleutelbare naam geselecteerd +decryptNames.dropZone.error.foreignFiles=Bestanden behoren niet tot de kluis "%s" +decryptNames.dropZone.error.noDirIdBackup=Map van de geselecteerde bestanden bevat geen dirId.c9r bestand +decryptNames.dropZone.error.generic=Kan bestandsnamen niet decoderen + + +# Event View +eventView.title=Activiteiten +eventView.filter.allVaults=Alle +eventView.clearListButton.tooltip=Wis lijst +## event list entries +eventView.entry.vaultLocked.description=Ontgrendel "%s" voor details +eventView.entry.conflictResolved.message=Opgelost conflict +eventView.entry.conflictResolved.showDecrypted=Toon gedecodeerd bestand +eventView.entry.conflictResolved.copyDecrypted=Kopieer gedecodeerd pad +eventView.entry.conflict.message=Conflictoplossing is mislukt +eventView.entry.conflict.showDecrypted=Toon gedecodeerd, origineel bestand +eventView.entry.conflict.copyDecrypted=Kopieer gedecodeerd, origineel pad +eventView.entry.conflict.showEncrypted=Conflicterend, versleuteld bestand weergeven +eventView.entry.conflict.copyEncrypted=Conflicterend, versleuteld pad kopiëren +eventView.entry.decryptionFailed.message=Decodering mislukt +eventView.entry.decryptionFailed.showEncrypted=Toon gedecodeerd bestand +eventView.entry.decryptionFailed.copyEncrypted=Kopieer gedecodeerd pad +eventView.entry.brokenDirFile.message=Verbroken directory link +eventView.entry.brokenDirFile.showEncrypted=Toon verbroken gecodeerde link +eventView.entry.brokenDirFile.copyEncrypted=Kopieer pad van verbroken link +eventView.entry.brokenFileNode.message=Kapot bestandssysteemknooppunt +eventView.entry.brokenFileNode.showEncrypted=Toon verbroken gecodeerde link +eventView.entry.brokenFileNode.copyEncrypted=Kopieer pad van verbroken, versleuteld knooppunt +eventView.entry.brokenFileNode.copyDecrypted=Kopieer gedecodeerd pad diff --git a/src/main/resources/i18n/strings_nn.properties b/src/main/resources/i18n/strings_nn.properties index f159b5ae8..79672070d 100644 --- a/src/main/resources/i18n/strings_nn.properties +++ b/src/main/resources/i18n/strings_nn.properties @@ -281,3 +281,10 @@ quit.lockAndQuitBtn=Lås og avslutt #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_no.properties b/src/main/resources/i18n/strings_no.properties index 16d2016cd..aa86df2bd 100644 --- a/src/main/resources/i18n/strings_no.properties +++ b/src/main/resources/i18n/strings_no.properties @@ -121,3 +121,10 @@ #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_pa.properties b/src/main/resources/i18n/strings_pa.properties index 9bb50b838..bf7dfa025 100644 --- a/src/main/resources/i18n/strings_pa.properties +++ b/src/main/resources/i18n/strings_pa.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=ਖ਼ਾਰਜ ## Button generic.button.apply=ਲਾਗੂ ਕਰੋ generic.button.back=ਪਿੱਛੇ @@ -63,6 +64,7 @@ addvaultwizard.new.validCharacters.dashes=ਹਾਈਫਨ (%s) ਜਾਂ ਹੇ addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=ਮਾਹਰ ਸੈਟਿੰਗਾਂ ਨੂੰ ਸਮਰੱਥ ਕਰੋ addvaultwizard.new.expertSettings.shorteningThreshold.invalid=ਮੁੱਲ 36 ਤੋਂ 220 ਵਿਚਾਲੇ ਚਾਹੀਦਾ ਹੈ (ਮੂਲ 220 ਹੈ) addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=ਹੋਰ ਸਿੱਖਣ ਲਈ ਦਸਤਾਵੇਜ਼ ਨੂੰ ਖੋਲ੍ਹੋ। +addvaultwizard.new.expertSettings.shorteningThreshold.title=ਇੰਕ੍ਰਿਪਟ ਕੀਤੀਆਂ ਫ਼ਾਈਲਾਂ ਦੇ ਨਾਵਾਂ ਦੀ ਵੱਧ ਤੋਂ ਵੱਧ ਲੰਬਾਈ addvaultwizard.new.expertSettings.shorteningThreshold.valid=ਵਾਜਬ ### Password addvaultwizard.new.createVaultBtn=ਵਾਲਟ ਬਣਾਓ @@ -208,14 +210,19 @@ health.check.runBatchBtn=ਚੁਣੀਆਂ ਚੋਣਾਂ ਨੂੰ ਚਲਾ ## Detail view health.check.detail.checkScheduled=ਜਾਂਚ ਨੂੰ ਸੈਡਿਊਲ ਕੀਤਾ ਗਿਆ ਹੈ। health.check.detail.checkRunning=ਜਾਂਚ ਇਸ ਵੇਲੇ ਚੱਲ ਰਹੀ ਹੈ… +health.check.detail.checkFinished=ਜਾਂਚ ਕਾਮਯਾਬੀ ਨਾਲ ਪੂਰੀ ਹੋਈ। health.check.detail.checkCancelled=ਜਾਂਚ ਨੂੰ ਰੱਦ ਕੀਤਾ ਗਿਆ ਹੈ। health.check.detail.listFilters.label=ਫਿਲਟਰ +health.check.detail.fixAllSpecificBtn=ਸਭ ਕਿਸਮਾਂ ਨੂੰ ਠੀਕ ਕਰੋ health.check.exportBtn=ਰਿਪੋਰਟ ਨੂੰ ਐਕਸਪੋਰਟ ਕਰੋ ## Result view health.result.severityFilter.good=ਵਧੀਆ health.result.severityFilter.info=ਜਾਣਕਾਰੀ health.result.severityFilter.warn=ਚੇਤਾਵਨੀ health.result.severityFilter.crit=ਗੰਭੀਰ +health.result.fixStateFilter.fixable=ਠੀਕ ਕਰਨ ਯੋਗ +health.result.fixStateFilter.notFixable=ਠੀਕ ਨਾ ਕਰਨ ਯੋਗ +health.result.fixStateFilter.fixing=ਠੀਕ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ… health.result.fixStateFilter.fixed=ਠੀਕ ਕੀਤਾ health.result.fixStateFilter.fixFailed=ਫੇਲ੍ਹ ਕਰਨਾ ਅਸਫ਼ਲ ## Fix Application @@ -349,7 +356,6 @@ main.vaultDetail.throughput.kbps=%.1f KiB/s main.vaultDetail.throughput.mbps=%.1f MiB/s main.vaultDetail.stats=ਵਾਲਟ ਅੰਕੜੇ main.vaultDetail.locateEncryptedFileBtn=ਇੰਕ੍ਰਿਪਟ ਕੀਤੀ ਫਾਇਲ ਨੂੰ ਲੱਭੋ -main.vaultDetail.filePickerTitle=ਵਾਲਟ ਵਿੱਚ ਫਾਇਲ ਨੂੰ ਚੁਣੋ ### Missing main.vaultDetail.missing.info=Cryptomator ਇਸ ਮਾਗਰ ਉੱਤੇ ਵਾਲਟ ਨਹੀਂ ਲੱਭਿਆ ਸਕੀ। main.vaultDetail.missing.recheck=ਮੁੜ-ਜਾਂਚੋ @@ -476,6 +482,9 @@ dokanySupportEnd.message=Dokany ਲਈ ਸਹਿਯੋਗ ਖ਼ਤਮ dokanySupportEnd.preferencesBtn=ਪਸੰਦੀਦਾ ਖੋਲ੍ਹੋ #Retry If Readonly +retryIfReadonly.title=ਵਾਲਟ ਪਹੁੰਚ ਲਈ ਪਾਬੰਦੀ ਹੈ +retryIfReadonly.message=ਵਾਲਟ ਡਾਇਰੈਕਟਰੀ ਲਈ ਕੋਈ ਲਿਖਣ ਪਹੁੰਚ ਨਹੀਂ ਹੈ +retryIfReadonly.retry=ਬਦਲੋ ਅਤੇ ਮੁੜ-ਕੋਸ਼ਿਸ਼ ਕਰੋ # Share Vault shareVault.title=ਵਾਲਟ ਨੂੰ ਸਾਂਝਾ ਕਰੋ @@ -492,4 +501,12 @@ shareVault.visitHub=Cryptomator Hub ਨੂੰ ਖੋਲ੍ਹੋ shareVault.hub.message=ਹੱਬ ਵਾਲਟ ਨੂੰ ਕਿਵੇਂ ਸਾਂਝਾ ਕਰ ਸਕਦੇ ਹਾਂ shareVault.hub.instruction.1=1. ਇੰਕ੍ਰਿਪਟ ਕੀਤੇ ਵਾਲਟ ਫੋਲਡਰ ਦੀ ਪਹੁੰਚ ਕਲਾਉਡ ਸਟੋਰੇਜ਼ ਰਾਹੀਂ ਸਾਂਝੀ ਕਰੋ। -shareVault.hub.openHub=Cryptomator Hub ਨੂੰ ਖੋਲ੍ਹੋ \ No newline at end of file +shareVault.hub.instruction.2=2. Cryptomator Hub ਵਿੱਚ ਟੀਮ ਮੈਂਬਰਾਂ ਨੂੰ ਪਹੁੰਚ ਦੀ ਮਨਜ਼ੂਰੀ ਦਿਓ। +shareVault.hub.openHub=Cryptomator Hub ਨੂੰ ਖੋਲ੍ਹੋ + +# Decrypt File Names + + +# Event View +eventView.filter.allVaults=ਸਭ +## event list entries diff --git a/src/main/resources/i18n/strings_pl.properties b/src/main/resources/i18n/strings_pl.properties index a3dc2c7b2..815975fb1 100644 --- a/src/main/resources/i18n/strings_pl.properties +++ b/src/main/resources/i18n/strings_pl.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Odrzuć ## Button generic.button.apply=Zastosuj generic.button.back=Wstecz @@ -176,6 +177,7 @@ hub.registerFailed.description.generic=Wystąpił błąd w procesie rejestracji. hub.registerFailed.description.deviceAlreadyExists=To urządzenie jest już zarejestrowane dla innego użytkownika. Spróbuj zmienić konto użytkownika lub użyć innego urządzenia. ### Unauthorized hub.unauthorized.message=Brak dostępu +hub.unauthorized.description=Nie masz uprawnień do otwierania tego sejfu. Skontaktuj się z właścicielem sejfu i poproś o dostęp. ### Requires Account Initialization hub.requireAccountInit.message=Wymagane działanie hub.requireAccountInit.description.0=Aby kontynuować, wykonaj wymagane kroki w Twoim @@ -281,7 +283,7 @@ preferences.title=Ustawienia ## General preferences.general=Ogólne preferences.general.startHidden=Ukryj okno podczas uruchamiania programu Cryptomator -preferences.general.autoCloseVaults=Zablokuj automatycznie otwarte sejfy podczas zamykania aplikacji +preferences.general.autoCloseVaults=Zablokuj sejfy bez pytania, kiedy opuszczasz aplikację preferences.general.debugLogging=Włącz logowanie w trybie debug preferences.general.debugDirectory=Pokaż pliki logowania preferences.general.autoStart=Uruchom Cryptomator po uruchomieniu systemu @@ -421,7 +423,6 @@ main.vaultDetail.stats=Statystyki sejfu main.vaultDetail.locateEncryptedFileBtn=Zlokalizuj zaszyfrowany plik main.vaultDetail.locateEncryptedFileBtn.tooltip=Wybierz plik z sejfu, aby zlokalizować jego zaszyfrowany odpowiednik main.vaultDetail.encryptedPathsCopied=Ścieżki skopiowane do schowka! -main.vaultDetail.filePickerTitle=Wybierz plik wewnątrz sejfu ### Missing main.vaultDetail.missing.info=Cryptomator nie mógł znaleźć sejfu w tej lokalizacji. main.vaultDetail.missing.recheck=Ponów próbę @@ -552,6 +553,10 @@ dokanySupportEnd.description=Typ udziału Dokany nie jest już wspierany przez C dokanySupportEnd.preferencesBtn=Otwórz ustawienia #Retry If Readonly +retryIfReadonly.title=Ograniczony dostęp do sejfu +retryIfReadonly.message=Brak dostępu do zapisu w katalogu sejfu +retryIfReadonly.description=Cryptomator nie może zapisywać do katalogu sejfu. Możesz zmienić sejf w tryb tylko do odczytu i spróbować ponownie. Tę opcję można wyłączyć w opcjach sejfu. +retryIfReadonly.retry=Zmień i spróbuj ponownie # Share Vault shareVault.title=Udostępnij sejf @@ -571,4 +576,10 @@ shareVault.hub.message=Jak udostępnić sejf Hub shareVault.hub.description=Aby udostępnić zawartość sejfu innemu członkowi zespołu, musisz wykonać dwa kroki: shareVault.hub.instruction.1=1. Udziel dostępu do zaszyfrowanego sejfu przez miejsce w chmurze. shareVault.hub.instruction.2=2. Udziel dostępu członkowi zespołu w Cryptomator Hub. -shareVault.hub.openHub=Otwórz Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Otwórz Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_pt.properties b/src/main/resources/i18n/strings_pt.properties index bd4b5c500..e123071b0 100644 --- a/src/main/resources/i18n/strings_pt.properties +++ b/src/main/resources/i18n/strings_pt.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Ignorar ## Button generic.button.apply=Aplicar generic.button.back=Anterior @@ -282,7 +283,7 @@ preferences.title=Preferências ## General preferences.general=Geral preferences.general.startHidden=Ocultar janela ao iniciar o Cryptomator -preferences.general.autoCloseVaults=Bloquear cofres abertos automaticamente ao sair da aplicação +preferences.general.autoCloseVaults=Bloquear cofres sem perguntar ao fechar a aplicação preferences.general.debugLogging=Ativar o registo de erros preferences.general.debugDirectory=Mostrar ficheiros de registo preferences.general.autoStart=Iniciar o Cryptomator no início do sistema @@ -394,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=Mostrar opções do Cofre main.vaultlist.contextMenu.reveal=Revelar unidade main.vaultlist.addVaultBtn.menuItemNew=Criar novo cofre... main.vaultlist.addVaultBtn.menuItemExisting=Abrir cofre existente... +main.vaultlist.showEventsButton.tooltip=Abrir visualização do evento ##Notificaition main.notification.updateAvailable=A atualização está disponível. main.notification.support=Apoie o Cryptomator. @@ -422,7 +424,9 @@ main.vaultDetail.stats=Estatísticas do Cofre main.vaultDetail.locateEncryptedFileBtn=Localizar Ficheiro Encriptado main.vaultDetail.locateEncryptedFileBtn.tooltip=Escolha um ficheiro do seu cofre para localizar a sua contraparte encriptada main.vaultDetail.encryptedPathsCopied=Caminhos copiados para a área de transferência! -main.vaultDetail.filePickerTitle=Selecione o ficheiro no cofre +main.vaultDetail.locateEncrypted.filePickerTitle=Selecionar ficheiro dentro do cofre +main.vaultDetail.decryptName.buttonLabel=Desencriptar nome do ficheiro +main.vaultDetail.decryptName.tooltip=Escolha um ficheiro de cofre encriptado para desencriptar o seu nome ### Missing main.vaultDetail.missing.info=O Cryptomator não conseguiu encontrar um cofre neste diretório. main.vaultDetail.missing.recheck=Verificar novamente @@ -576,4 +580,43 @@ shareVault.hub.message=Como partilhar um cofre do Hub shareVault.hub.description=Para partilhar o conteúdo do cofre com outro membro da equipa, precisa executar duas etapas: shareVault.hub.instruction.1=1. Partilhe o acesso à pasta encriptada do cofre via armazenamento na nuvem. shareVault.hub.instruction.2=2. Conceder acesso ao membro da equipe no Hub Cryptomator. -shareVault.hub.openHub=Abrir Hub do Cryptomator \ No newline at end of file +shareVault.hub.openHub=Abrir Hub do Cryptomator + +# Decrypt File Names +decryptNames.title=Desencriptar nomes de ficheiros +decryptNames.filePicker.title=Selecione o ficheiro encriptado +decryptNames.filePicker.extensionDescription=Ficheiro encriptado do Cryptomator +decryptNames.copyTable.tooltip=Copiar tabela +decryptNames.clearTable.tooltip=Limpar tabela +decryptNames.copyHint=Copiar conteúdo da célula com %s +decryptNames.dropZone.message=Solte os ficheiros ou clique para selecionar +decryptNames.dropZone.error.vaultInternalFiles=Ficheiros internos do cofre sem nome decifrável selecionado +decryptNames.dropZone.error.foreignFiles=Ficheiros não pertencem ao cofre "%s" +decryptNames.dropZone.error.noDirIdBackup=O diretório dos ficheiros selecionados não contém o ficheiro dirId.c9r +decryptNames.dropZone.error.generic=Falha ao desencriptar nomes de ficheiros + + +# Event View +eventView.title=Eventos +eventView.filter.allVaults=Todos +eventView.clearListButton.tooltip=Limpar lista +## event list entries +eventView.entry.vaultLocked.description=Desbloquear "%s" para detalhes +eventView.entry.conflictResolved.message=Conflito resolvido +eventView.entry.conflictResolved.showDecrypted=Mostrar ficheiro desencriptado +eventView.entry.conflictResolved.copyDecrypted=Copiar caminho desencriptado +eventView.entry.conflict.message=Resolução de conflito falhou +eventView.entry.conflict.showDecrypted=Mostrar ficheiro original desencriptado +eventView.entry.conflict.copyDecrypted=Copie caminho original desencriptado +eventView.entry.conflict.showEncrypted=Mostrar ficheiro encriptado conflitante +eventView.entry.conflict.copyEncrypted=Copiar caminho encriptado conflituante +eventView.entry.decryptionFailed.message=Falha na desencriptação +eventView.entry.decryptionFailed.showEncrypted=Mostrar ficheiro encriptado +eventView.entry.decryptionFailed.copyEncrypted=Copiar caminho de encriptação +eventView.entry.brokenDirFile.message=Link de diretório quebrado +eventView.entry.brokenDirFile.showEncrypted=Mostrar link quebrado e encriptado +eventView.entry.brokenDirFile.copyEncrypted=Copiar caminho do link quebrado +eventView.entry.brokenFileNode.message=Nó do sistema de ficheiros avariado +eventView.entry.brokenFileNode.showEncrypted=Mostrar nó encriptado quebrado +eventView.entry.brokenFileNode.copyEncrypted=Copiar o caminho do nó encriptado e danificado +eventView.entry.brokenFileNode.copyDecrypted=Copiar caminho desencriptado diff --git a/src/main/resources/i18n/strings_pt_BR.properties b/src/main/resources/i18n/strings_pt_BR.properties index 9dc86b406..58f31d575 100644 --- a/src/main/resources/i18n/strings_pt_BR.properties +++ b/src/main/resources/i18n/strings_pt_BR.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Ignorar ## Button generic.button.apply=Aplicar generic.button.back=Voltar @@ -282,7 +283,7 @@ preferences.title=Preferências ## General preferences.general=Geral preferences.general.startHidden=Ocultar janela ao iniciar o Cryptomator -preferences.general.autoCloseVaults=Bloquear cofres abertos automaticamente ao sair do aplicativo +preferences.general.autoCloseVaults=Bloquear cofres sem perguntar ao sair do aplicativo preferences.general.debugLogging=Ativar log de debug preferences.general.debugDirectory=Mostrar arquivos de log preferences.general.autoStart=Iniciar o Cryptomator ao inicializar o sistema @@ -394,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=Exibir Opções de Cofre main.vaultlist.contextMenu.reveal=Revelar Volume main.vaultlist.addVaultBtn.menuItemNew=Novo Cofre... main.vaultlist.addVaultBtn.menuItemExisting=Abrir Cofre Existente... +main.vaultlist.showEventsButton.tooltip=Abrir visualização de evento ##Notificaition main.notification.updateAvailable=Atualização disponível. main.notification.support=Apoie o Cryptomator. @@ -422,7 +424,9 @@ main.vaultDetail.stats=Estatísticas do Cofre main.vaultDetail.locateEncryptedFileBtn=Localizar Arquivo Criptografado main.vaultDetail.locateEncryptedFileBtn.tooltip=Escolha um arquivo do seu cofre para localizar sua versão criptografada main.vaultDetail.encryptedPathsCopied=Caminhos copiados para a Área de Transferência! -main.vaultDetail.filePickerTitle=Selecione o Arquivo No Cofre +main.vaultDetail.locateEncrypted.filePickerTitle=Selecionar Arquivo no Cofre +main.vaultDetail.decryptName.buttonLabel=Descriptografar Nome do Arquivo +main.vaultDetail.decryptName.tooltip=Escolha um arquivo criptografado do cofre para descriptografar seu nome ### Missing main.vaultDetail.missing.info=O Cryptomator não encontrou um cofre neste caminho. main.vaultDetail.missing.recheck=Verificar novamente @@ -576,4 +580,43 @@ shareVault.hub.message=Como compartilhar um cofre do Hub shareVault.hub.description=Para compartilhar o conteúdo do cofre com outro membro da equipe, você precisa executar duas etapas: shareVault.hub.instruction.1=1. Compartilhe o acesso da pasta do cofre criptografado via armazenamento na nuvem. shareVault.hub.instruction.2=2. Conceda acesso ao membro da equipe no Cryptomator Hub. -shareVault.hub.openHub=Abrir o Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Abrir o Cryptomator Hub + +# Decrypt File Names +decryptNames.title=Descriptografar Nomes de Arquivos +decryptNames.filePicker.title=Selecione o arquivo criptografado +decryptNames.filePicker.extensionDescription=Arquivo criptografado do Cryptomator +decryptNames.copyTable.tooltip=Copiar tabela +decryptNames.clearTable.tooltip=Limpar tabela +decryptNames.copyHint=Copiar conteúdo da célula com %s +decryptNames.dropZone.message=Arraste e solte arquivos ou clique para selecionar +decryptNames.dropZone.error.vaultInternalFiles=Selecionados arquivos internos do cofre sem nome descriptografável +decryptNames.dropZone.error.foreignFiles=Arquivos não pertencem ao cofre "%s" +decryptNames.dropZone.error.noDirIdBackup=Diretório dos arquivos selecionados não contém o arquivo dirId.c9r +decryptNames.dropZone.error.generic=Falha ao descriptografar nomes de arquivos + + +# Event View +eventView.title=Eventos +eventView.filter.allVaults=Todos +eventView.clearListButton.tooltip=Limpar lista +## event list entries +eventView.entry.vaultLocked.description=Desbloquear "%s" para detalhes +eventView.entry.conflictResolved.message=Conflito resolvido +eventView.entry.conflictResolved.showDecrypted=Mostrar arquivo descriptografado +eventView.entry.conflictResolved.copyDecrypted=Copiar caminho descriptografado +eventView.entry.conflict.message=Resolução de conflitos falhou +eventView.entry.conflict.showDecrypted=Mostrar arquivo original descriptografado +eventView.entry.conflict.copyDecrypted=Copiar caminho original descriptografado +eventView.entry.conflict.showEncrypted=Mostrar arquivo criptografado conflitante +eventView.entry.conflict.copyEncrypted=Copiar caminho criptografado conflitante +eventView.entry.decryptionFailed.message=Decriptação falhou +eventView.entry.decryptionFailed.showEncrypted=Mostrar arquivo criptografado +eventView.entry.decryptionFailed.copyEncrypted=Copiar caminho encriptado +eventView.entry.brokenDirFile.message=Link do diretório quebrado +eventView.entry.brokenDirFile.showEncrypted=Mostrar link quebrado e criptografado +eventView.entry.brokenDirFile.copyEncrypted=Copiar caminho do link quebrado +eventView.entry.brokenFileNode.message=Nó de sistema de arquivos danificado +eventView.entry.brokenFileNode.showEncrypted=Mostrar nó criptografado com falha +eventView.entry.brokenFileNode.copyEncrypted=Copiar caminho do nó quebrado criptografado +eventView.entry.brokenFileNode.copyDecrypted=Copiar caminho descriptografado diff --git a/src/main/resources/i18n/strings_ro.properties b/src/main/resources/i18n/strings_ro.properties index da93916db..e5812a6ec 100644 --- a/src/main/resources/i18n/strings_ro.properties +++ b/src/main/resources/i18n/strings_ro.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Renunță ## Button generic.button.apply=Aplică generic.button.back=Înapoi @@ -281,7 +282,6 @@ preferences.title=Preferințe ## General preferences.general=Setări Generale preferences.general.startHidden=Ascunde fereastra la pornirea Cryptomator -preferences.general.autoCloseVaults=Încuie seifurile deschise la ieșirea din aplicație automat preferences.general.debugLogging=Activează jurnalul de depanare preferences.general.debugDirectory=Dezvăluie fişierele jurnal preferences.general.autoStart=Lansați Cryptomator la pornirea sistemului @@ -419,7 +419,6 @@ main.vaultDetail.stats=Statistici de seif main.vaultDetail.locateEncryptedFileBtn=Localizează fișier criptat main.vaultDetail.locateEncryptedFileBtn.tooltip=Alege un fișier din seiful tău pentru a-i localiza echivalentul criptat main.vaultDetail.encryptedPathsCopied=Căile au fost copiate în Clipboard -main.vaultDetail.filePickerTitle=Alege fișier din seifului ### Missing main.vaultDetail.missing.info=Cryptomator nu a putut găsi un seif pe această cale. main.vaultDetail.missing.recheck=Verifică din nou @@ -568,4 +567,10 @@ shareVault.hub.message=Cum să partajezi un seif Hub shareVault.hub.description=Pentru a partaja conținutul de seif cu un alt membru al echipei, trebuie să efectuați doi pași: shareVault.hub.instruction.1=1. Partajarea accesului la folderul criptat din seif prin stocarea in nor. shareVault.hub.instruction.2=2. Acordă acces membrului echipei din hub-ul Cryptomator. -shareVault.hub.openHub=Deschide Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Deschide Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_ru.properties b/src/main/resources/i18n/strings_ru.properties index 381e5c737..c0f3da76a 100644 --- a/src/main/resources/i18n/strings_ru.properties +++ b/src/main/resources/i18n/strings_ru.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Отклонить ## Button generic.button.apply=Применить generic.button.back=Назад @@ -282,7 +283,7 @@ preferences.title=Настройки ## General preferences.general=Общие preferences.general.startHidden=Скрывать окно при запуске Cryptomator -preferences.general.autoCloseVaults=Автоблокировка открытых хранилищ при выходе из приложения +preferences.general.autoCloseVaults=Блокировать хранилища без запроса при выходе из приложения preferences.general.debugLogging=Вести журнал отладки preferences.general.debugDirectory=Показать файлы журнала preferences.general.autoStart=Запускать Cryptomator при старте системы @@ -394,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=Параметры хранилища main.vaultlist.contextMenu.reveal=Показать диск main.vaultlist.addVaultBtn.menuItemNew=Создать хранилище... main.vaultlist.addVaultBtn.menuItemExisting=Открыть имеющееся хранилище... +main.vaultlist.showEventsButton.tooltip=Открыть просмотр события ##Notificaition main.notification.updateAvailable=Есть обновление. main.notification.support=Поддержите Cryptomator. @@ -422,7 +424,9 @@ main.vaultDetail.stats=Статистика хранилища main.vaultDetail.locateEncryptedFileBtn=Найти зашифрованный файл main.vaultDetail.locateEncryptedFileBtn.tooltip=Выберите файл из своего хранилища, чтобы найти его зашифрованный дубликат main.vaultDetail.encryptedPathsCopied=Пути скопированы в буфер обмена. -main.vaultDetail.filePickerTitle=Выберите файл внутри хранилища +main.vaultDetail.locateEncrypted.filePickerTitle=Выберите файл внутри хранилища +main.vaultDetail.decryptName.buttonLabel=Расшифровать имя файла +main.vaultDetail.decryptName.tooltip=Выберите зашифрованный файл хранилища для расшифровки его имени ### Missing main.vaultDetail.missing.info=Cryptomator не смог найти хранилище по этому пути. main.vaultDetail.missing.recheck=Перепроверить @@ -576,4 +580,43 @@ shareVault.hub.message=Как поделиться хранилищем в ха shareVault.hub.description=Чтобы поделиться содержимым хранилища с другим членом команды, выполните два шага: shareVault.hub.instruction.1=1. Делитесь доступом к зашифрованной папке хранилища через облако. shareVault.hub.instruction.2=2. Предоставьте доступ члену команды в хабе Cryptomator. -shareVault.hub.openHub=Открыть хаб Cryptomator \ No newline at end of file +shareVault.hub.openHub=Открыть хаб Cryptomator + +# Decrypt File Names +decryptNames.title=Расшифровать имена файлов +decryptNames.filePicker.title=Выберите зашифрованный файл +decryptNames.filePicker.extensionDescription=Файл, зашифрованный Cryptomator +decryptNames.copyTable.tooltip=Скопировать таблицу +decryptNames.clearTable.tooltip=Очистить таблицу +decryptNames.copyHint=Скопировать содержимое ячейки с %s +decryptNames.dropZone.message=Перетащите файлы или нажмите для выбора +decryptNames.dropZone.error.vaultInternalFiles=Выбраны внутренние файлы хранилища с нерасшифрованными именами +decryptNames.dropZone.error.foreignFiles=Файлы не принадлежат хранилищу "%s" +decryptNames.dropZone.error.noDirIdBackup=Папка выбранных файлов не содержит файла dirId.c9r +decryptNames.dropZone.error.generic=Не удалось расшифровать имена файлов + + +# Event View +eventView.title=События +eventView.filter.allVaults=Все +eventView.clearListButton.tooltip=Очистить список +## event list entries +eventView.entry.vaultLocked.description=Разблокируйте "%s" для деталей +eventView.entry.conflictResolved.message=Решённый конфликт +eventView.entry.conflictResolved.showDecrypted=Показать расшифрованный файл +eventView.entry.conflictResolved.copyDecrypted=Скопировать расшифрованный путь +eventView.entry.conflict.message=Не удалось решить конфликты +eventView.entry.conflict.showDecrypted=Показать расшифрованный исходный файл +eventView.entry.conflict.copyDecrypted=Скопировать расшифрованный исходный путь +eventView.entry.conflict.showEncrypted=Показать конфликтующий зашифрованный файл +eventView.entry.conflict.copyEncrypted=Скопировать конфликтующий зашифрованный путь +eventView.entry.decryptionFailed.message=Ошибка дешифрования +eventView.entry.decryptionFailed.showEncrypted=Показать зашифрованный файл +eventView.entry.decryptionFailed.copyEncrypted=Скопировать зашифрованный путь +eventView.entry.brokenDirFile.message=Повреждённая ссылка на папку +eventView.entry.brokenDirFile.showEncrypted=Показать повреждённую зашифрованную ссылку +eventView.entry.brokenDirFile.copyEncrypted=Скопировать путь повреждённой ссылки +eventView.entry.brokenFileNode.message=Повреждённый узел файловой системы +eventView.entry.brokenFileNode.showEncrypted=Показать повреждённый зашифрованный узел +eventView.entry.brokenFileNode.copyEncrypted=Скопировать путь повреждённого зашифрованного узла +eventView.entry.brokenFileNode.copyDecrypted=Скопировать расшифрованный путь diff --git a/src/main/resources/i18n/strings_si.properties b/src/main/resources/i18n/strings_si.properties index 535e0dca0..e639f1f7f 100644 --- a/src/main/resources/i18n/strings_si.properties +++ b/src/main/resources/i18n/strings_si.properties @@ -138,3 +138,10 @@ hub.registerSuccess.unlockBtn=අගුළුහරින්න #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_sk.properties b/src/main/resources/i18n/strings_sk.properties index 2dbc737d6..b1b985b7a 100644 --- a/src/main/resources/i18n/strings_sk.properties +++ b/src/main/resources/i18n/strings_sk.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Zamietnuť ## Button generic.button.apply=Použiť generic.button.back=Späť @@ -48,6 +49,7 @@ addvaultwizard.new.nameInstruction=Zvoľte názov pre trezor addvaultwizard.new.namePrompt=Názov trezoru ### Location addvaultwizard.new.locationInstruction=Kde by mal Cryptomator uchovávať šifrované súbory vášho trezoru? +addvaultwizard.new.locationLoading=Kontroluje sa lokálny súborový systém pre predvolené adresáre cloudového úložiska… addvaultwizard.new.locationLabel=Umiestnenie úložiska addvaultwizard.new.locationPrompt=… addvaultwizard.new.directoryPickerLabel=Vlastné umiestnenie @@ -136,7 +138,7 @@ unlock.success.revealBtn=Odkryť disk unlock.error.customPath.message=Nie je možné namapovať trezor na uživateĺskej ceste unlock.error.customPath.description.notSupported=Ak chcete naďalej používať vlastnú cestu, prejdite do nastavení a vyberte typ zväzku, ktorý ju podporuje. V opačnom prípade prejdite na možnosti trezoru a vyberte podporovaný bod pripojenia. unlock.error.customPath.description.notExists=Vlastná cesta pripojenia neexistuje. Buď ju vytvorte v miestnom súborovom systéme, alebo ju zmeňte v možnostiach trezora. -unlock.error.customPath.description.inUse=Písmeno disku alebo vlastná cesta pripojenia "%s" sa už používa. +unlock.error.customPath.description.inUse=Písmeno disku alebo zvolená mapovaná cesta "%s" sa aktuálne používa. unlock.error.customPath.description.hideawayNotDir=Dočasne, skrytý súbor "%3$s" použitý pre odomknutie nemôže byť odstránený. Prosím skontrolujte súbor a následne zmažte manuálne. unlock.error.customPath.description.couldNotBeCleaned=Váš trezor sa nepodarilo pripojiť na cestu "%s". Skúste to prosím znova alebo vyberte inú cestu. unlock.error.customPath.description.notEmptyDir=Vlastná cesta pripojenia "%s" nie je prázdny priečinok. Vyberte prázdny priečinok a skúste to znova. @@ -168,6 +170,7 @@ hub.registerSuccess.unlockBtn=Odomknúť hub.registerFailed.message=Registrácia zariadenia zlyhala ### Unauthorized hub.unauthorized.message=Prístup zamietnutý +hub.unauthorized.description=Nie ste autorizovaný na otvorenie tohto trezora. Kontaktujte vlastníka trezora pre vyžiadanie prístupu. ### Requires Account Initialization hub.requireAccountInit.message=Vyžadovaná akcia hub.requireAccountInit.description.0=Pre pokračovanie vyplňte potrebné kroky vo vašom @@ -273,7 +276,7 @@ preferences.title=Predvoľby ## General preferences.general=Hlavné preferences.general.startHidden=Skryť okno počas štartu Cryptomator-a -preferences.general.autoCloseVaults=Uzamknúť otvorené trezory pri ukončovaní aplikácie +preferences.general.autoCloseVaults=Uzamknúť trezory bez výzvy keď ukončujete aplikáciu preferences.general.debugLogging=Povoliť logovanie chýb preferences.general.debugDirectory=Ukázať súbory logov preferences.general.autoStart=Spustiť Cryptomator pri štarte systému @@ -385,6 +388,7 @@ main.vaultlist.contextMenu.vaultoptions=Ukáž možnosti trezora main.vaultlist.contextMenu.reveal=Odkry disk main.vaultlist.addVaultBtn.menuItemNew=Vytvoriť Nový trezor… main.vaultlist.addVaultBtn.menuItemExisting=Otvoriť Existujúci trezor... +main.vaultlist.showEventsButton.tooltip=Otvoriť zobrazenie udalosti ##Notificaition main.notification.updateAvailable=Aktualizácia je k dispozícii. main.notification.support=O aplikácii Cryptomator. @@ -413,7 +417,9 @@ main.vaultDetail.stats=Štatistiky trezora main.vaultDetail.locateEncryptedFileBtn=Lokalizujte zašifrovaný súbor main.vaultDetail.locateEncryptedFileBtn.tooltip=Zvoľte súbor z Vášho trezora pre lokalizáciu šifrovaného náprotivku main.vaultDetail.encryptedPathsCopied=Cesty skopírované do clipboard-u! -main.vaultDetail.filePickerTitle=Zvoľte súbor v trezore +main.vaultDetail.locateEncrypted.filePickerTitle=Zvoľte súbor v trezore +main.vaultDetail.decryptName.buttonLabel=Dešifrovať názov súboru +main.vaultDetail.decryptName.tooltip=Vyberte zašifrovaný súbor trezoru na dešifrovanie jeho názvu ### Missing main.vaultDetail.missing.info=Cryptomator nevie nájsť trezor na tejto ceste. main.vaultDetail.missing.recheck=Prekontrolovať @@ -543,8 +549,52 @@ dokanySupportEnd.description=Typ média Dokany už Cryptomator viac nepodporuje. dokanySupportEnd.preferencesBtn=Otvoriť predvoľby #Retry If Readonly +retryIfReadonly.title=Obmedziť prístup k trezoru +retryIfReadonly.message=Bez možnosti zápisu do adresára trezora +retryIfReadonly.description=Cryptomator nemôže zapisovať do adresára trezora. Môžte zmeniť trezor len na čítanie a vyskúšať znovu. Táto voľba môže byť zakázaná v nastaveniach trezora. +retryIfReadonly.retry=Zmeniť a skúsiť znova # Share Vault shareVault.title=Zdielať trezor shareVault.hubAd.authentication=* Silná autentifikácia shareVault.hubAd.encryption=* Šifrovanie end-to-end + + +# Decrypt File Names +decryptNames.title=Dešifrovať názvy súborov +decryptNames.filePicker.title=Vyberte zašifrovaný súbor +decryptNames.filePicker.extensionDescription=Cryptomator zašifrovaný súbor +decryptNames.copyTable.tooltip=Kopírovať tabuľku +decryptNames.clearTable.tooltip=Vymazať tabuľku +decryptNames.copyHint=Kopírovať obsah bunky s %s +decryptNames.dropZone.message=Presuňte súbory alebo ich kliknutím vyberte +decryptNames.dropZone.error.vaultInternalFiles=Interné súbory trezora bez vybratého názvu, ktorý je možné dešifrovať +decryptNames.dropZone.error.foreignFiles=Súbory nepatria do trezoru "%s" +decryptNames.dropZone.error.noDirIdBackup=Adresár vybraných súborov neobsahuje súbor dirId.c9r +decryptNames.dropZone.error.generic=Nepodarilo sa dešifrovať názvy súborov + + +# Event View +eventView.title=Udalosti +eventView.filter.allVaults=Všetko +eventView.clearListButton.tooltip=Vymazať zoznam +## event list entries +eventView.entry.vaultLocked.description=Podrobnosti získate odomknutím „%s“ +eventView.entry.conflictResolved.message=Vyriešený konflikt +eventView.entry.conflictResolved.showDecrypted=Zobraziť dešifrovaný súbor +eventView.entry.conflictResolved.copyDecrypted=Skopírujte dešifrovanú cestu +eventView.entry.conflict.message=Riešenie konfliktu zlyhalo +eventView.entry.conflict.showDecrypted=Zobraziť dešifrovaný pôvodný súbor +eventView.entry.conflict.copyDecrypted=Kopírovať dešifrovanú, pôvodnú cestu +eventView.entry.conflict.showEncrypted=Zobraziť konfliktný zašifrovaný súbor +eventView.entry.conflict.copyEncrypted=Kopírovať konfliktnú zašifrovanú cestu +eventView.entry.decryptionFailed.message=Dešifrovanie zlyhalo +eventView.entry.decryptionFailed.showEncrypted=Zobraziť zašifrovaný súbor +eventView.entry.decryptionFailed.copyEncrypted=Skopírujte zašifrovanú cestu +eventView.entry.brokenDirFile.message=Nefunkčný odkaz na adresár +eventView.entry.brokenDirFile.showEncrypted=Zobraziť nefunkčný, šifrovaný odkaz +eventView.entry.brokenDirFile.copyEncrypted=Kopírovať cestu nefunkčného odkazu +eventView.entry.brokenFileNode.message=Poškodený uzol súborového systému +eventView.entry.brokenFileNode.showEncrypted=Zobraziť nefunkčný, zašifrovaný uzol +eventView.entry.brokenFileNode.copyEncrypted=Kopírovať cestu nefunkčného, ​​šifrovaného uzla +eventView.entry.brokenFileNode.copyDecrypted=Skopírujte dešifrovanú cestu diff --git a/src/main/resources/i18n/strings_sl.properties b/src/main/resources/i18n/strings_sl.properties index 21f49fc5b..bc19194d4 100644 --- a/src/main/resources/i18n/strings_sl.properties +++ b/src/main/resources/i18n/strings_sl.properties @@ -181,3 +181,10 @@ recoveryKey.recover.invalidKey=Obnovitveni ključ ni pravilen #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_sr.properties b/src/main/resources/i18n/strings_sr.properties index 8a5c9ef99..72947efeb 100644 --- a/src/main/resources/i18n/strings_sr.properties +++ b/src/main/resources/i18n/strings_sr.properties @@ -325,3 +325,10 @@ quit.lockAndQuitBtn=Zaključaj i Izađi #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_sr_Latn.properties b/src/main/resources/i18n/strings_sr_Latn.properties index c05c42e36..a4e32a3a1 100644 --- a/src/main/resources/i18n/strings_sr_Latn.properties +++ b/src/main/resources/i18n/strings_sr_Latn.properties @@ -244,3 +244,10 @@ vaultOptions.masterkey.changePasswordBtn=Promena lozinke #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_sv.properties b/src/main/resources/i18n/strings_sv.properties index 79735b489..67fc2fb7f 100644 --- a/src/main/resources/i18n/strings_sv.properties +++ b/src/main/resources/i18n/strings_sv.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Avfärda ## Button generic.button.apply=Verkställ generic.button.back=Bakåt @@ -282,7 +283,6 @@ preferences.title=Inställningar ## General preferences.general=Allmänt preferences.general.startHidden=Dölj fönster när Cryptomator startar -preferences.general.autoCloseVaults=Lås öppna valv automatiskt när du avslutar programmet preferences.general.debugLogging=Aktivera loggning för felsökning preferences.general.debugDirectory=Visa loggfiler preferences.general.autoStart=Starta Cryptomator vid systemstart @@ -394,6 +394,7 @@ main.vaultlist.contextMenu.vaultoptions=Visa inställningar för valv main.vaultlist.contextMenu.reveal=Visa enhet main.vaultlist.addVaultBtn.menuItemNew=Skapa nytt valv... main.vaultlist.addVaultBtn.menuItemExisting=Öppna befintligt valv... +main.vaultlist.showEventsButton.tooltip=Öppna händelsevy ##Notificaition main.notification.updateAvailable=Uppdatering tillgänglig. main.notification.support=Stöd Cryptomator. @@ -422,7 +423,9 @@ main.vaultDetail.stats=Valv Statistik main.vaultDetail.locateEncryptedFileBtn=Leta upp krypterad fil main.vaultDetail.locateEncryptedFileBtn.tooltip=Välj en fil från ditt valv för att hitta dess krypterade motsvarighet main.vaultDetail.encryptedPathsCopied=Sökvägar kopierade till klippbordet! -main.vaultDetail.filePickerTitle=Välj fil inuti valvet +main.vaultDetail.locateEncrypted.filePickerTitle=Välj fil inuti valvet +main.vaultDetail.decryptName.buttonLabel=Dekryptera filnamn +main.vaultDetail.decryptName.tooltip=Välj en krypterad valvfil för att dekryptera dess namn ### Missing main.vaultDetail.missing.info=Cryptomator kunde inte hitta någt valv i denna sökväg. main.vaultDetail.missing.recheck=Kontrollera igen @@ -576,4 +579,43 @@ shareVault.hub.message=Hur man delar ett navvalv shareVault.hub.description=För att kunna dela valvinnehållet med en annan gruppmedlem måste du utföra två steg: shareVault.hub.instruction.1=1. Dela åtkomst till den krypterade valvmappen via molnlagring. shareVault.hub.instruction.2=2. Ge åtkomst till gruppmedlemmen i Cryptomatornavet. -shareVault.hub.openHub=Öppna kryptomatornav \ No newline at end of file +shareVault.hub.openHub=Öppna kryptomatornav + +# Decrypt File Names +decryptNames.title=Dekryptera filnamn +decryptNames.filePicker.title=Välj krypterad fil +decryptNames.filePicker.extensionDescription=Cryptomator krypterad fil +decryptNames.copyTable.tooltip=Kopiera tabell +decryptNames.clearTable.tooltip=Rensa tabell +decryptNames.copyHint=Kopiera cellinnehåll med %s +decryptNames.dropZone.message=Släpp filer eller klicka för att välja +decryptNames.dropZone.error.vaultInternalFiles=Interna valvfiler utan dekrypterbart namn valdes +decryptNames.dropZone.error.foreignFiles=Filer tillhör inte valvet "%s" +decryptNames.dropZone.error.noDirIdBackup=Katalog med valda filer innehåller inte dirId.c9r fil +decryptNames.dropZone.error.generic=Det gick inte att dekryptera filnamn + + +# Event View +eventView.title=Händelser +eventView.filter.allVaults=Samtliga +eventView.clearListButton.tooltip=Rensa listan +## event list entries +eventView.entry.vaultLocked.description=Lås upp "%s" för detaljer +eventView.entry.conflictResolved.message=Löst konflikt +eventView.entry.conflictResolved.showDecrypted=Visa dekrypterad fil +eventView.entry.conflictResolved.copyDecrypted=Kopiera dekrypterad sökväg +eventView.entry.conflict.message=Konfliktlösning misslyckades +eventView.entry.conflict.showDecrypted=Visa dekrypterad, originalfil +eventView.entry.conflict.copyDecrypted=Kopiera dekrypterad, ursprunglig sökväg +eventView.entry.conflict.showEncrypted=Visa krypterad fil som inte kunde synkroniseras +eventView.entry.conflict.copyEncrypted=Kopiera krypterad sökväg som inte kunde synkroniseras +eventView.entry.decryptionFailed.message=Dekryptering misslyckades +eventView.entry.decryptionFailed.showEncrypted=Visa krypterad fil +eventView.entry.decryptionFailed.copyEncrypted=Kopiera krypterad sökväg +eventView.entry.brokenDirFile.message=Trasig kataloglänk +eventView.entry.brokenDirFile.showEncrypted=Visa trasig, krypterad länk +eventView.entry.brokenDirFile.copyEncrypted=Kopiera sökväg för trasig länk +eventView.entry.brokenFileNode.message=Trasig filsystemsnod +eventView.entry.brokenFileNode.showEncrypted=Visa trasig krypterad nod +eventView.entry.brokenFileNode.copyEncrypted=Kopiera sökväg för trasig, krypterad nod +eventView.entry.brokenFileNode.copyDecrypted=Kopiera dekrypterad sökväg diff --git a/src/main/resources/i18n/strings_sw.properties b/src/main/resources/i18n/strings_sw.properties index 84f640b03..508d4c141 100644 --- a/src/main/resources/i18n/strings_sw.properties +++ b/src/main/resources/i18n/strings_sw.properties @@ -244,7 +244,6 @@ preferences.title=Mapendeleo ## General preferences.general=Jumla preferences.general.startHidden=Ficha dirisha wakati wa kuanza Cryptomator -preferences.general.autoCloseVaults=Funga kuba kiotomatiki wakati wa kuacha programu preferences.general.debugLogging=Wezesha utatuzi wa ufunguaji preferences.general.debugDirectory=Fichua faili za logi preferences.general.autoStart=Zindua Cryptomator kwenye kuanza kwa mfumo @@ -361,7 +360,6 @@ main.vaultDetail.stats=Takwimu za Kuba main.vaultDetail.locateEncryptedFileBtn=Tafuta Faili Iliyosimbwa kwa Njia Fiche main.vaultDetail.locateEncryptedFileBtn.tooltip=Chagua faili kutoka kwa chumba chako ili kupata mwenza wake uliosimbwa kwa njia fiche main.vaultDetail.encryptedPathsCopied=Njia Zimenakiliwa kwenye Ubao wa kunakili! -main.vaultDetail.filePickerTitle=Chagua Faili Ndani ya Kuba ### Missing main.vaultDetail.missing.info=Cryptomator haikuweza kupata kuba katika njia hii. main.vaultDetail.missing.recheck=Kagua upya @@ -471,3 +469,10 @@ dokanySupportEnd.preferencesBtn=Fungua Mapendeleo #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_ta.properties b/src/main/resources/i18n/strings_ta.properties index d8359bdb0..86df70c40 100644 --- a/src/main/resources/i18n/strings_ta.properties +++ b/src/main/resources/i18n/strings_ta.properties @@ -308,3 +308,10 @@ quit.forced.forceAndQuitBtn=கட்டாயப்படுத்தி வெ #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_te.properties b/src/main/resources/i18n/strings_te.properties index 4555a9cda..046c8ed75 100644 --- a/src/main/resources/i18n/strings_te.properties +++ b/src/main/resources/i18n/strings_te.properties @@ -125,3 +125,10 @@ preferences.interface.theme.light=కాంతి #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_th.properties b/src/main/resources/i18n/strings_th.properties index 2f57abf4e..22f981768 100644 --- a/src/main/resources/i18n/strings_th.properties +++ b/src/main/resources/i18n/strings_th.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=ไม่สนใจ ## Button generic.button.apply=นำไปใช้ generic.button.back=ย้อนกลับ @@ -252,7 +253,6 @@ preferences.title=การตั้งค่า ## General preferences.general=ทั่วไป preferences.general.startHidden=ซ่อนหน้าต่างเมื่อเริ่ม Cryptomator -preferences.general.autoCloseVaults=ล็อก vault ที่เปิดอยู่โดยอัตโนมัติเมื่อออกจากแอปพลิเคชัน preferences.general.debugLogging=เปิดใช้งานการบันทึกข้อมูลดีบัก preferences.general.debugDirectory=เปิดไฟล์บันทึก preferences.general.autoStart=เริ่มต้น Cryptomator เมื่อเริ่มระบบ @@ -355,7 +355,6 @@ main.vaultDetail.throughput.idle=ไม่ทำงาน main.vaultDetail.throughput.kbps=%.1f KiB/วินาที main.vaultDetail.throughput.mbps=%.1f MiB/วินาที main.vaultDetail.stats=สถิติของ Vault -main.vaultDetail.filePickerTitle=เลือกไฟล์ภายใน Vault ### Missing main.vaultDetail.missing.recheck=ตรวจสอบอีกครั้ง main.vaultDetail.missing.remove=ลบออกจากรายชื่อ Vault… @@ -450,3 +449,10 @@ dokanySupportEnd.preferencesBtn=การตั้งค่า shareVault.title=แชร์ Vault shareVault.hubAd.authentication=• การพิสูจน์ตัวตนที่เข้มงวด shareVault.hubAd.encryption=• เข้ารหัสจากต้นทางถึงปลายทาง + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_tr.properties b/src/main/resources/i18n/strings_tr.properties index 4f7ed1ab4..e77dfd64c 100644 --- a/src/main/resources/i18n/strings_tr.properties +++ b/src/main/resources/i18n/strings_tr.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=Yoksay ## Button generic.button.apply=Uygula generic.button.back=Geri @@ -282,7 +283,7 @@ preferences.title=Tercihler ## General preferences.general=Genel preferences.general.startHidden=Cryptomator'ı başlatırken pencereyi gizle -preferences.general.autoCloseVaults=Uygulamadan çıkarken açık kasaları otomatik olarak kilitle +preferences.general.autoCloseVaults=Bütün kasaları uygulamadan çıkarken sormadan kitle preferences.general.debugLogging=Hata ayıklama günlüğünü etkinleştir preferences.general.debugDirectory=Kayıt dosyalarını göster preferences.general.autoStart=Cryptomator'u sistem başlangıcında çalıştır @@ -422,7 +423,6 @@ main.vaultDetail.stats=Kasa İstatistikleri main.vaultDetail.locateEncryptedFileBtn=Şifrelenmiş Dosyayı Bul main.vaultDetail.locateEncryptedFileBtn.tooltip=Şifrelenmiş halini bulmak için kasanızdan bir dosya seçin main.vaultDetail.encryptedPathsCopied=Yollar Panoya Kopyalandı! -main.vaultDetail.filePickerTitle=Kasa İçinde Dosya Seç ### Missing main.vaultDetail.missing.info=Cryptomator bu yolda bir kasa bulamadı. main.vaultDetail.missing.recheck=Yeniden denetle @@ -576,4 +576,11 @@ shareVault.hub.message=Hub kasası nasıl paylaşılır shareVault.hub.description=Kasa içeriğini başka bir ekip üyesiyle paylaşmak için iki adım gerçekleştirmeniz gerekir: shareVault.hub.instruction.1=1. Şifrelenmiş kasa klasörünün erişimini bulut depolama üzerinden paylaşın. shareVault.hub.instruction.2=2. Cryptomator Hub'da ekip üyesine erişim izni verin. -shareVault.hub.openHub=Cryptomator Hub'ı aç \ No newline at end of file +shareVault.hub.openHub=Cryptomator Hub'ı aç + +# Decrypt File Names + + +# Event View +eventView.filter.allVaults=Tümü +## event list entries diff --git a/src/main/resources/i18n/strings_ug.properties b/src/main/resources/i18n/strings_ug.properties index 1b6ad1c69..427c30f62 100644 --- a/src/main/resources/i18n/strings_ug.properties +++ b/src/main/resources/i18n/strings_ug.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=رەت قىلش ## Button generic.button.apply=قوللىنىش generic.button.back=قايت @@ -280,7 +281,6 @@ preferences.title=تەڭشەكلەر ## General preferences.general=ئومۇمىي preferences.general.startHidden=Cryptomator قوزغالغاندا كۆزنەكنى يوشۇر -preferences.general.autoCloseVaults=پروگرامما ئاخىرلاشقاندا ئوچۇق ئامبارلارنى ئاپتوماتىك قۇلۇپلا preferences.general.debugLogging=خاتالىق تاپقۇچ خاتىرىسىنى قوزغات preferences.general.debugDirectory=خاتىرە ھۆججەتلىرىنى كۆرسەت preferences.general.autoStart=سىستېما قوزغالغاندا Cryptomator نى قوزغات @@ -409,7 +409,6 @@ main.vaultDetail.stats=ئامبار ستاتىستىكىسى main.vaultDetail.locateEncryptedFileBtn=شىفىرلانغان ھۆججەتنى تاپ main.vaultDetail.locateEncryptedFileBtn.tooltip=شىفىرلانغان نۇسخىسىنى تېپىش ئۈچۈن ئامبارىڭىزدىن بىر ھۆججەتنى تاللاڭ main.vaultDetail.encryptedPathsCopied=يولى چاپلاش تاختىسىغا كۆچۈرۈلدى! -main.vaultDetail.filePickerTitle=ئامبار ئىچىدىكى ھۆججەتنى تاللاڭ ### Missing main.vaultDetail.missing.info=Cryptomator بۇ يولدا قويۇلما تېپىلمىدى. main.vaultDetail.missing.recheck=قايتا تەكشۈرۈش @@ -559,4 +558,10 @@ shareVault.hub.message=ئامبارنى قانداق ھەمبەھىرلەيمى shareVault.hub.description=ئامبار مەزمۇنىنى باشقا گۇرۇپپىنىڭ ئەزاسىغا ھەمبەھىرلەش ئۈچۈن تۆۋەندىكى ئىككى قەدەم بويچە ھەمبەھىرلەش كېرەك: shareVault.hub.instruction.1=1. شىفىرلانغان ئامبار مۇندەرىجىسىنى بۇلۇت ساقلاش ئارقىلىق ھەمبەھىرلەڭ. shareVault.hub.instruction.2=2. Cryptomator Hub ئۇچۇرى ئارقىلىق گۇرۇپپا ئەزاسىغا ھەمبەھىرلەڭ. -shareVault.hub.openHub=Cryptomator Hub ئۇچۇرىنى ئېچىڭ \ No newline at end of file +shareVault.hub.openHub=Cryptomator Hub ئۇچۇرىنى ئېچىڭ + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_uk.properties b/src/main/resources/i18n/strings_uk.properties index f23de9ca0..a3bd1290b 100644 --- a/src/main/resources/i18n/strings_uk.properties +++ b/src/main/resources/i18n/strings_uk.properties @@ -2,6 +2,7 @@ additionalStyleSheets= # Generics +generic.action.dismiss=Відхилити ## Button generic.button.apply=Застосувати generic.button.back=Назад @@ -177,6 +178,7 @@ hub.registerFailed.description.generic=Помилка виникла у проц hub.registerFailed.description.deviceAlreadyExists=Цей пристрій вже зареєстровано для іншого користувача. Спробуйте змінити обліковий запис користувача або скористайтеся іншим пристроєм. ### Unauthorized hub.unauthorized.message=У доступі відмовлено +hub.unauthorized.description=Ви не маєте права для відкриття цього сховища. Зверніться до власника сховища, щоб запитати доступ. ### Requires Account Initialization hub.requireAccountInit.message=Необхідна дія hub.requireAccountInit.description.0=Для продовження, будь ласка, завершіть виконання обов'язкових кроків @@ -282,7 +284,6 @@ preferences.title=Налаштування ## General preferences.general=Загальні preferences.general.startHidden=Приховати вікно під час запуску Cryptomator -preferences.general.autoCloseVaults=Заблокувати відкриті сховища автоматично в разі закриття додатка preferences.general.debugLogging=Увімкнути ведення журналу налагодження preferences.general.debugDirectory=Показати файли журналу preferences.general.autoStart=Запускати Cryptomator під час запуску системи @@ -422,7 +423,6 @@ main.vaultDetail.stats=Статистика сховища main.vaultDetail.locateEncryptedFileBtn=Знайти зашифрований файл main.vaultDetail.locateEncryptedFileBtn.tooltip=Виберіть файл із вашого сховища, щоб знайти розташування його зашифрованого відповідника main.vaultDetail.encryptedPathsCopied=Шляхи скопійовано в буфер обміну! -main.vaultDetail.filePickerTitle=Виберіть файл у сховищі ### Missing main.vaultDetail.missing.info=Cryptomator не зміг знайти сховище за цим шляхом. main.vaultDetail.missing.recheck=Перевірити знову @@ -553,6 +553,10 @@ dokanySupportEnd.description=Тип сховища Dokany більше не пі dokanySupportEnd.preferencesBtn=Відкрити налаштування #Retry If Readonly +retryIfReadonly.title=Обмежений доступ до сховища +retryIfReadonly.message=Немає прав на запис до папки сховища +retryIfReadonly.description=Cryptomator не може записувати в папку сховища. Ви можете змінити режим доступу до сховища на "лише для читання" і повторити спробу. Цей параметр можна вимкнути у налаштуваннях сховища. +retryIfReadonly.retry=Змінити і повторити спробу # Share Vault shareVault.title=Поділитися сховищем @@ -572,4 +576,10 @@ shareVault.hub.message=Як поділитись сховищем у хабі (H shareVault.hub.description=Щоб поділитися вмістом сховища з іншим членом команди, ви повинні виконати два кроки: shareVault.hub.instruction.1=1. Поділіться доступом до зашифрованої папки сховища через хмарне сховище. shareVault.hub.instruction.2=2. Надайте доступ учаснику команди у Cryptomator Hub. -shareVault.hub.openHub=Відкрити Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=Відкрити Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_ur.properties b/src/main/resources/i18n/strings_ur.properties index 16d2016cd..aa86df2bd 100644 --- a/src/main/resources/i18n/strings_ur.properties +++ b/src/main/resources/i18n/strings_ur.properties @@ -121,3 +121,10 @@ #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_vi.properties b/src/main/resources/i18n/strings_vi.properties index 77fab0701..858d0f527 100644 --- a/src/main/resources/i18n/strings_vi.properties +++ b/src/main/resources/i18n/strings_vi.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss="Bỏ qua" ## Button generic.button.apply=Áp dụng generic.button.back=Quay lại @@ -264,7 +265,6 @@ preferences.title=Tùy chỉnh ## General preferences.general=Chung preferences.general.startHidden=Ẩn cửa sổ khi khởi động Cryptomator -preferences.general.autoCloseVaults=Tự động khóa các vault còn đang mở khi thoát ứng dụng preferences.general.debugLogging=Bật nhật kí gỡ lỗi preferences.general.debugDirectory=Mở tệp nhật ký preferences.general.autoStart=Khởi chạy Cryptomator khi khởi động hệ thống @@ -388,7 +388,6 @@ main.vaultDetail.stats=Thống kê Vault main.vaultDetail.locateEncryptedFileBtn=Định vị Tệp Đã mã hoá main.vaultDetail.locateEncryptedFileBtn.tooltip=Chọn một tệp từ vaule của bạn để xác định vị trí đối tác được mã hoá của nó main.vaultDetail.encryptedPathsCopied=Đường dẫn Đã sao chép vào Bảng tạm! -main.vaultDetail.filePickerTitle=Chọn Tệp Bên trong Vault ### Missing main.vaultDetail.missing.info=Cryptomator không thể tìm thấy vault tại đường dẫn này. main.vaultDetail.missing.recheck=Kiểm tra lại @@ -520,3 +519,10 @@ shareVault.hubAd.description=Cách an toàn để làm việc theo nhóm shareVault.hubAd.keyManagement=• Quản lý khóa Zero-knowledge shareVault.hubAd.encryption=• Mã hóa đầu cuối shareVault.visitHub=Truy cập Cryptomator Hub + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_zh.properties b/src/main/resources/i18n/strings_zh.properties index c5c798f2b..3e97a80de 100644 --- a/src/main/resources/i18n/strings_zh.properties +++ b/src/main/resources/i18n/strings_zh.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=放弃 ## Button generic.button.apply=应用 generic.button.back=后退 @@ -48,7 +49,7 @@ addvaultwizard.new.nameInstruction=为保险库创建一个名称 addvaultwizard.new.namePrompt=保险库名称 ### Location addvaultwizard.new.locationInstruction=Cryptomator 应该在哪里存储您保险库的加密文件? -addvaultwizard.new.locationLoading=正在检查本地文件系统默认云存储目录… +addvaultwizard.new.locationLoading=检查本地文件系统以查找默认的云存储目录… addvaultwizard.new.locationLabel=存储位置 addvaultwizard.new.locationPrompt=… addvaultwizard.new.directoryPickerLabel=自定义位置 @@ -282,7 +283,7 @@ preferences.title=首选项 ## General preferences.general=常规​​​​​ preferences.general.startHidden=最小化启动 Cryptomator 到系统托盘 -preferences.general.autoCloseVaults=退出程序时自动锁定打开的保险库 +preferences.general.autoCloseVaults=退出程序时直接锁定保险库而无需询问 preferences.general.debugLogging=启用调试日志 preferences.general.debugDirectory=显示日志文件 preferences.general.autoStart=开机自动启动 @@ -422,7 +423,6 @@ main.vaultDetail.stats=保险库统计 main.vaultDetail.locateEncryptedFileBtn=找出加密后文件 main.vaultDetail.locateEncryptedFileBtn.tooltip=选择保险库中的一个文件来找出已加密文件的位置 main.vaultDetail.encryptedPathsCopied=路径已复制到剪贴板 -main.vaultDetail.filePickerTitle=选择保险库中的文件 ### Missing main.vaultDetail.missing.info=Cryptomator在此路径找不到保险库 main.vaultDetail.missing.recheck=重新检查 @@ -553,6 +553,10 @@ dokanySupportEnd.description=Cryptomator 不再支持 Dokany 卷类型。您的 dokanySupportEnd.preferencesBtn=打开首选项 #Retry If Readonly +retryIfReadonly.title=保险库访问受限 +retryIfReadonly.message=无权写入保险库目录 +retryIfReadonly.description=Cryptomator 无法写入保险库目录。您可以更改保险库的只读选项,然后重试。此选项可在保险库选项中禁用 +retryIfReadonly.retry=更改并重试 # Share Vault shareVault.title=共享保险库 @@ -572,4 +576,10 @@ shareVault.hub.message=如何共享 Hub 保险库 shareVault.hub.description=为了与其他团队成员共享保险库内容,您必须执行以下两步: shareVault.hub.instruction.1=1:通过云存储共享加密保险库文件夹的访问权限。 shareVault.hub.instruction.2=2:在 Cryptomator Hub 内授予团队成员访问权限。 -shareVault.hub.openHub=打开 Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=打开 Cryptomator Hub + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_zh_HK.properties b/src/main/resources/i18n/strings_zh_HK.properties index 78dc9c25f..7ee362b37 100644 --- a/src/main/resources/i18n/strings_zh_HK.properties +++ b/src/main/resources/i18n/strings_zh_HK.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=忽略 ## Button generic.button.apply=套用 generic.button.back=返回 @@ -281,7 +282,6 @@ preferences.title=偏好設定 ## General preferences.general=一般 preferences.general.startHidden=啟動 Cryptomator 時隱藏視窗 -preferences.general.autoCloseVaults=關閉應用時,自動鎖定打開的加密庫 preferences.general.debugLogging=啟用除錯日誌 preferences.general.debugDirectory=展示日誌檔案 preferences.general.autoStart=系統啟動時同時啟動 Cryptomator @@ -421,7 +421,6 @@ main.vaultDetail.stats=加密庫統計數據 main.vaultDetail.locateEncryptedFileBtn=顯示加密檔案路徑 main.vaultDetail.locateEncryptedFileBtn.tooltip=選擇要顯示對應加密檔案路徑的加密檔案庫檔案 main.vaultDetail.encryptedPathsCopied=路徑已複製到剪貼簿 -main.vaultDetail.filePickerTitle=從加密檔案庫中選擇檔案 ### Missing main.vaultDetail.missing.info=Cryptomator 無法在指定位置找到加密庫。 main.vaultDetail.missing.recheck=重新檢查 @@ -546,3 +545,10 @@ dokanySupportEnd.preferencesBtn=開啟偏好設定 #Retry If Readonly # Share Vault + + +# Decrypt File Names + + +# Event View +## event list entries diff --git a/src/main/resources/i18n/strings_zh_TW.properties b/src/main/resources/i18n/strings_zh_TW.properties index 582bfdf90..075e43fdd 100644 --- a/src/main/resources/i18n/strings_zh_TW.properties +++ b/src/main/resources/i18n/strings_zh_TW.properties @@ -1,6 +1,7 @@ # Locale Specific CSS files such as CJK, RTL,... # Generics +generic.action.dismiss=忽略 ## Button generic.button.apply=套用 generic.button.back=上一頁 @@ -93,7 +94,7 @@ addvault.new.readme.accessLocation.2=這是您加密檔案庫的存取位置。 addvault.new.readme.accessLocation.3=所有被加進這個磁區的檔案都將被 Cryptomator 加密。你可以把它當做磁碟或資料夾使用。這裡式顯示出解密後內容,您的檔案總是以被加密的狀態儲存在磁碟中。 addvault.new.readme.accessLocation.4=您可以放心移除這個檔案。 ## Existing -addvaultwizard.existing.title=添加現有的加密檔案庫 +addvaultwizard.existing.title=開啟現有加密檔案庫 addvaultwizard.existing.instruction=請選擇現有加密檔案庫中名為「vault.cryptomator」的檔案。如果只有一個名為「masterkey.cryptomator」的檔案,則選擇該檔案。 addvaultwizard.existing.chooseBtn=選取… addvaultwizard.existing.filePickerTitle=選取加密檔案庫的檔案 @@ -126,7 +127,7 @@ unlock.unlockBtn=解鎖 ## Select unlock.chooseMasterkey.message=未找到主金鑰文件 unlock.chooseMasterkey.description=無法在其預期位置找到加密檔案庫「%s」的主密鑰檔案。請手動選擇密鑰文件。 -unlock.chooseMasterkey.filePickerTitle=选择主金鑰文件 +unlock.chooseMasterkey.filePickerTitle=選擇主金鑰檔案 unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator 主密鑰 ## Success unlock.success.message=解鎖成功 @@ -176,6 +177,7 @@ hub.registerFailed.description.generic=註冊過程發生錯誤。更多細節 hub.registerFailed.description.deviceAlreadyExists=其他使用者已在此裝置上註冊。請切換至該使用者帳戶或使用其他裝置進行註冊。 ### Unauthorized hub.unauthorized.message=拒絕存取 +hub.unauthorized.description=您沒有被授權開啟這個加密檔案庫。請聯絡檔案庫擁有者取得權限。 ### Requires Account Initialization hub.requireAccountInit.message=需進一步操作 hub.requireAccountInit.description.0=請完成您的 @@ -281,7 +283,7 @@ preferences.title=偏好 ## General preferences.general=一般 preferences.general.startHidden=啟動 Cryptomator 時隱藏視窗 -preferences.general.autoCloseVaults=結束程式自動鎖定啟用中的檔案庫 +preferences.general.autoCloseVaults=當離開應用程式的時候直接鎖定檔案庫而不詢問 preferences.general.debugLogging=啟用除錯日誌 preferences.general.debugDirectory=顯示日誌檔 preferences.general.autoStart=系統啟動時同時啟動 Cryptomator @@ -393,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=顯示加密檔案庫選項 main.vaultlist.contextMenu.reveal=顯示磁碟 main.vaultlist.addVaultBtn.menuItemNew=新建加密檔案庫... main.vaultlist.addVaultBtn.menuItemExisting=開啟現有的加密檔案庫... +main.vaultlist.showEventsButton.tooltip=打開事件檢視 ##Notificaition main.notification.updateAvailable=有可用更新 main.notification.support=贊助 Cryptomator. @@ -421,7 +424,9 @@ main.vaultDetail.stats=加密檔案庫統計 main.vaultDetail.locateEncryptedFileBtn=顯示加密檔案路徑 main.vaultDetail.locateEncryptedFileBtn.tooltip=選擇要顯示對應加密檔案路徑的加密檔案庫檔案 main.vaultDetail.encryptedPathsCopied=路徑已複製到剪貼簿 -main.vaultDetail.filePickerTitle=從加密檔案庫中選擇檔案 +main.vaultDetail.locateEncrypted.filePickerTitle=從加密檔案庫中選擇檔案 +main.vaultDetail.decryptName.buttonLabel=解密檔案名稱 +main.vaultDetail.decryptName.tooltip=選擇加密的檔案庫檔案以解密其名稱 ### Missing main.vaultDetail.missing.info=Cryptomator 無法在指定位置找到加密檔案庫。 main.vaultDetail.missing.recheck=重新檢查 @@ -552,6 +557,10 @@ dokanySupportEnd.description=Cryptomator 不再支援 Dokany 檔案系統。已 dokanySupportEnd.preferencesBtn=開啟偏好設定 #Retry If Readonly +retryIfReadonly.title=加密檔案庫的存取受限 +retryIfReadonly.message=沒有加密檔案庫路徑的寫入權限 +retryIfReadonly.description=Cryptomator 無法寫入到加密檔案庫路徑。您可以將加密檔案庫改成唯讀並重試。此選項可以在加密檔案庫選項中關閉。 +retryIfReadonly.retry=修改與重試 # Share Vault shareVault.title=與其他人共用加密檔案庫 @@ -571,4 +580,24 @@ shareVault.hub.message=如何與其他人共用加密檔案庫 shareVault.hub.description=要與您的團隊成員共用加密檔案庫,您需要完成以下兩個步驟: shareVault.hub.instruction.1=1. 調整您雲端儲存空間中加密檔案庫資料夾的存取權限。 shareVault.hub.instruction.2=2. 在Cryptomator Hub中允許團隊成員對加密檔案庫進行存取。 -shareVault.hub.openHub=打開 Cryptomator Hub \ No newline at end of file +shareVault.hub.openHub=打開 Cryptomator Hub + +# Decrypt File Names +decryptNames.filePicker.title=選擇已加密的檔案 +decryptNames.filePicker.extensionDescription=Cryptomator 加密檔案 +decryptNames.copyTable.tooltip=複製表格 +decryptNames.clearTable.tooltip=清除表格 +decryptNames.dropZone.error.foreignFiles=檔案不屬於加密檔案庫「%s」 +decryptNames.dropZone.error.generic=解密檔案名稱失敗 + + +# Event View +eventView.title=事件 +eventView.filter.allVaults=全部 +## event list entries +eventView.entry.decryptionFailed.message=解密失敗 +eventView.entry.decryptionFailed.showEncrypted=顯示加密的檔案 +eventView.entry.decryptionFailed.copyEncrypted=複製加密路徑 +eventView.entry.brokenDirFile.message=損壞的目錄連結 +eventView.entry.brokenDirFile.showEncrypted=顯示損壞的加密路徑 +eventView.entry.brokenDirFile.copyEncrypted=複製損壞的路徑連結 diff --git a/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java b/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java index abf803e1e..438574b78 100644 --- a/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java +++ b/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java @@ -1,6 +1,7 @@ package org.cryptomator.common.keychain; +import org.cryptomator.JavaFXUtil; import org.cryptomator.integrations.keychain.KeychainAccessException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; @@ -13,11 +14,16 @@ import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import java.time.Duration; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -public class KeychainManagerTest { +class KeychainManagerTest { + + @BeforeAll + public static void startup() throws InterruptedException { + var isRunning = JavaFXUtil.startPlatform(); + Assumptions.assumeTrue(isRunning); + } @Test public void testStoreAndLoad() throws KeychainAccessException { @@ -27,15 +33,7 @@ public class KeychainManagerTest { } @Nested - public static class WhenObservingProperties { - - @BeforeAll - public static void startup() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - Platform.startup(latch::countDown); - var javafxStarted = latch.await(5, TimeUnit.SECONDS); - Assumptions.assumeTrue(javafxStarted); - } + class WhenObservingProperties { @Test public void testPropertyChangesWhenStoringPassword() throws KeychainAccessException, InterruptedException { @@ -43,7 +41,7 @@ public class KeychainManagerTest { ReadOnlyBooleanProperty property = keychainManager.getPassphraseStoredProperty("test"); Assertions.assertFalse(property.get()); - keychainManager.storePassphrase("test", null,"bar"); + keychainManager.storePassphrase("test", null, "bar"); AtomicBoolean result = new AtomicBoolean(false); CountDownLatch latch = new CountDownLatch(1); diff --git a/src/test/java/org/cryptomator/ui/addvaultwizard/ReadMeGeneratorTest.java b/src/test/java/org/cryptomator/ui/addvaultwizard/ReadMeGeneratorTest.java index 97f02aee1..10451daa2 100644 --- a/src/test/java/org/cryptomator/ui/addvaultwizard/ReadMeGeneratorTest.java +++ b/src/test/java/org/cryptomator/ui/addvaultwizard/ReadMeGeneratorTest.java @@ -15,8 +15,8 @@ public class ReadMeGeneratorTest { @ParameterizedTest @CsvSource({ // "test,test", // - "t\u00E4st,t\\u228st", // - "t\uD83D\uDE09st,t\\u55357\\u56841st", // + "t\u00E4st,t\\'E4st", // + "t\uD83D\uDE09st,t\\uc1\\u55357\\uc1\\u56841st", // }) public void testEscapeNonAsciiChars(String input, String expectedResult) { ReadmeGenerator readmeGenerator = new ReadmeGenerator(null); @@ -40,7 +40,7 @@ public class ReadMeGeneratorTest { MatcherAssert.assertThat(result, CoreMatchers.startsWith("{\\rtf1\\fbidis\\ansi\\uc0\\fs32")); MatcherAssert.assertThat(result, CoreMatchers.containsString("{\\sa80 Dear User,}\\par")); MatcherAssert.assertThat(result, CoreMatchers.containsString("{\\sa80 \\b please don't touch the \"d\" directory.}\\par ")); - MatcherAssert.assertThat(result, CoreMatchers.containsString("{\\sa80 Thank you for your cooperation \\u55357\\u56841}\\par")); + MatcherAssert.assertThat(result, CoreMatchers.containsString("{\\sa80 Thank you for your cooperation \\uc1\\u55357\\uc1\\u56841}\\par")); MatcherAssert.assertThat(result, CoreMatchers.endsWith("}")); } diff --git a/src/test/java/org/cryptomator/ui/controls/SecurePasswordFieldTest.java b/src/test/java/org/cryptomator/ui/controls/SecurePasswordFieldTest.java index bfe31816e..da9d65117 100644 --- a/src/test/java/org/cryptomator/ui/controls/SecurePasswordFieldTest.java +++ b/src/test/java/org/cryptomator/ui/controls/SecurePasswordFieldTest.java @@ -1,6 +1,6 @@ package org.cryptomator.ui.controls; -import org.junit.jupiter.api.AfterAll; +import org.cryptomator.JavaFXUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; @@ -8,20 +8,14 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import javafx.application.Platform; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - public class SecurePasswordFieldTest { private SecurePasswordField pwField = new SecurePasswordField(); @BeforeAll public static void initJavaFx() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - Platform.startup(latch::countDown); - var javafxStarted = latch.await(5, TimeUnit.SECONDS); - Assumptions.assumeTrue(javafxStarted); + var isRunning = JavaFXUtil.startPlatform(); + Assumptions.assumeTrue(isRunning); } @Nested