Merge branch 'develop' into feature/mount-provider

# Conflicts:
#	.github/workflows/appimage.yml
#	.github/workflows/mac-dmg.yml
#	.github/workflows/win-exe.yml
#	pom.xml
#	src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
#	src/main/java/org/cryptomator/ui/traymenu/AwtTrayMenuController.java
This commit is contained in:
Armin Schrenk
2022-12-07 17:01:56 +01:00
88 changed files with 1601 additions and 370 deletions

View File

@@ -13,40 +13,35 @@ env:
JAVA_VERSION: 19
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ github.event.inputs.version }}
build:
name: Build AppImage
runs-on: ubuntu-latest
needs: [get-version]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
java-package: 'jdk+fx'
cache: 'maven'
- id: versions
name: Apply version information
- name: Ensure major jfx version in pom equals in jdk
shell: pwsh
run: |
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR}
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ github.event.inputs.version }}"
mvn versions:set -DnewVersion=${SEM_VER_STR}
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
REVCOUNT=`git rev-list --count HEAD`
echo "::set-output name=semVerStr::${SEM_VER_STR}"
echo "::set-output name=semVerNum::${SEM_VER_NUM}"
echo "::set-output name=revNum::${REVCOUNT}"
- name: Validate Version
uses: skymatic/semver-validation-action@v1
with:
version: ${{ steps.versions.outputs.semVerStr }}
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
exit 1
}
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B clean package -Pdependency-check,linux -DskipTests
- name: Patch target dir
@@ -60,7 +55,7 @@ jobs:
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--strip-native-commands
--no-header-files
--no-man-pages
@@ -69,8 +64,8 @@ jobs:
- name: Prepare additional launcher
run: envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
env:
SEMVER_STR: ${{ steps.versions.outputs.semVerStr }}
REVISION_NUM: ${{ steps.versions.outputs.revNum }}
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
REVISION_NUM: ${{ needs.get-version.outputs.revNum }}
- name: Run jpackage
run: >
${JAVA_HOME}/bin/jpackage
@@ -84,12 +79,12 @@ jobs:
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2022 Skymatic GmbH"
--app-version "${{ steps.versions.outputs.semVerNum }}.${{ steps.versions.outputs.revNum }}"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\""
--java-options "-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\""
@@ -98,7 +93,7 @@ jobs:
--java-options "-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=false"
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ steps.versions.outputs.revNum }}\""
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
--resource-dir dist/linux/resources
- name: Patch Cryptomator.AppDir
@@ -136,7 +131,7 @@ jobs:
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Build AppImage
run: >
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ steps.versions.outputs.semVerStr }}-x86_64.AppImage
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-x86_64.AppImage
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-x86_64.AppImage.zsync'
--sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
- name: Create detached GPG signatures

View File

@@ -13,44 +13,9 @@ defaults:
shell: bash
jobs:
release-check-precondition:
name: Validate pushed commit to release/hotfix branch or pushed tag
runs-on: ubuntu-latest
if: "(startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/heads/hotfix/') || startsWith(github.ref, 'refs/heads/release/'))
&& !(contains(github.event.head_commit.message, '[ci skip]') || contains(github.event.head_commit.message, '[skip ci]'))"
steps:
- uses: actions/checkout@v2
- id: validate-pom-version
name: Validate POM version
run: |
if [[ $GITHUB_REF =~ refs/heads/(hotfix|release)/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
elif [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
else
echo "Failed to parse version"
exit 1
fi
if [[ ${SEM_VER_STR} == `mvn help:evaluate -Dexpression=project.version -q -DforceStdout` ]]; then
echo "::set-output name=semVerStr::${SEM_VER_STR}"
else
echo "Version not set in POM"
exit 1
fi
- name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file
run: |
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\"/>" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
exit 1
fi
test:
name: Compile and Test
needs: release-check-precondition
runs-on: ubuntu-latest
if: "always()
&& (needs.release-check-precondition.result=='success' || needs.release-check-precondition.result=='skipped')
&& !(contains(github.event.head_commit.message, '[ci skip]') || contains(github.event.head_commit.message, '[skip ci]'))"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
@@ -77,15 +42,6 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Sign source tarball with key 615D449FE6E6A235
if: startsWith(github.ref, 'refs/tags/')
run: |
git archive --prefix="cryptomator-${{ github.ref_name }}/" -o "cryptomator-${{ github.ref_name }}.tar.gz" ${{ github.ref }}
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
env:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Draft a release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
@@ -94,9 +50,6 @@ jobs:
discussion_category_name: releases
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
generate_release_notes: true
files: |
cryptomator-*.tar.gz.asc
fail_on_unmatched_files: true
body: |-
:construction: Work in Progress

View File

@@ -1,21 +1,27 @@
name: Build Debian Package
on:
release:
types: [published]
workflow_dispatch:
inputs:
ref:
description: 'GitHub Ref (e.g. refs/tags/1.6.16)'
required: true
semver:
description: 'SemVer String (e.g. 1.7.0-beta1)'
required: true
ppaver:
description: 'Base PPA Version String (e.g. 1.6.16+1.7.0~beta1) without -0ppa1'
required: true
dput:
description: 'Upload to PPA'
required: true
default: false
type: boolean
version:
description: 'Version'
required: false
env:
JAVA_VERSION: 19
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/19/openjfx-19_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/19/openjfx-19_linux-aarch64_bin-jmods.zip'
jobs:
build:
@@ -24,50 +30,68 @@ jobs:
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.events.inputs.ref }}
- id: versions
name: Get version information
run: |
SEM_VER_STR="${{ github.events.inputs.semver }}"
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
REVCOUNT=`git rev-list --count HEAD`
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
- name: Install build tools
run: |
sudo add-apt-repository ppa:coffeelibs/openjdk
sudo apt-get update
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-19
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-19 libgtk2.0-0
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- id: versions
name: Apply version information
run: |
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR}
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ github.event.inputs.version }}"
mvn versions:set -DnewVersion=${SEM_VER_STR}
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
REVCOUNT=`git rev-list --count HEAD`
echo "::set-output name=semVerStr::${SEM_VER_STR}"
echo "::set-output name=semVerNum::${SEM_VER_NUM}"
echo "::set-output name=revNum::${REVCOUNT}"
echo "::set-output name=ppaVerStr::${SEM_VER_STR/-/\~}-${REVCOUNT}"
- name: Validate Version
uses: skymatic/semver-validation-action@v1
with:
version: ${{ steps.versions.outputs.semVerStr }}
- name: Run maven
run: mvn -B clean package -Pdependency-check,linux -DskipTests
- name: Create orig.tar.gz with common/ libs/ mods/
- name: Download OpenJFX jmods
id: download-jmods
run: |
curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
mkdir -p jmods/amd64
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64
curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
mkdir -p jmods/aarch64
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64
- name: Ensure major jfx version in pom and in jmods is the same
run: |
JMOD_VERSION_AMD64=$(jmod describe ${JAVA_HOME}/jmods/amd64/javafx.base.jmod | head -1)
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@}
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*}
JMOD_VERSION_AARCH64=$(jmod describe ${JAVA_HOME}/jmods/aarch64/javafx.base.jmod | head -1)
JMOD_VERSION_AARCH64=${JMOD_VERSION_AARCH64#*@}
JMOD_VERSION_AARCH64=${JMOD_VERSION_AARCH64%%.*}
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION_AMD64})"
exit 1
fi
if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AARCH64 ]; then
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != aarch64 jmod version (${JMOD_VERSION_AARCH64})"
exit 1
fi
- name: Create orig.tar.gz with common/ libs/ mods/ jmods/
run: |
mkdir pkgdir
cp -r target/libs pkgdir
cp -r target/mods pkgdir
cp -r jmods pkgdir
cp -r dist/linux/common/ pkgdir
cp target/cryptomator-*.jar pkgdir/mods
tar -cJf cryptomator_${{ steps.versions.outputs.ppaVerStr }}.orig.tar.xz -C pkgdir .
tar -cJf cryptomator_${{ github.event.inputs.ppaver }}.orig.tar.xz -C pkgdir .
- name: Patch and rename pkgdir
run: |
cp -r dist/linux/debian/ pkgdir
@@ -75,12 +99,12 @@ jobs:
envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM}' < dist/linux/debian/rules > pkgdir/debian/rules
envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog
find . -name "*.jar" >> pkgdir/debian/source/include-binaries
mv pkgdir cryptomator_${{ steps.versions.outputs.ppaVerStr }}
mv pkgdir cryptomator_${{ github.event.inputs.ppaver }}
env:
SEMVER_STR: ${{ steps.versions.outputs.semVerStr }}
VERSION_NUM: ${{ steps.versions.outputs.semVerNum }}
REVISION_NUM: ${{ steps.versions.outputs.revNum }}
PPA_VERSION: ${{ steps.versions.outputs.ppaVerStr }}-0ppa1
PPA_VERSION: ${{ github.event.inputs.ppaver }}-0ppa1
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -95,7 +119,7 @@ jobs:
env:
DEBSIGN_PROGRAM: gpg --batch --pinentry-mode loopback
DEBSIGN_KEYID: 615D449FE6E6A235
working-directory: cryptomator_${{ steps.versions.outputs.ppaVerStr }}
working-directory: cryptomator_${{ github.event.inputs.ppaver }}
- name: Create detached GPG signatures
run: |
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
@@ -112,13 +136,21 @@ jobs:
cryptomator_*_amd64.deb
cryptomator_*.asc
- name: Publish on PPA
if: startsWith(github.ref, 'refs/tags/') || inputs.dput
if: inputs.dput
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
# If ref is a tag, also upload to GitHub Releases:
- name: Determine tag name
if: startsWith(github.events.inputs.ref, 'refs/tags/')
run: |
REF=${{ github.events.inputs.ref }}
echo "TAG_NAME=${REF##*/}" >> $GITHUB_ENV
- name: Publish Debian package on GitHub Releases
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.events.inputs.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
with:
fail_on_unmatched_files: true
tag_name: ${{ github.env.TAG_NAME }}
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
cryptomator_*_amd64.deb

View File

@@ -48,7 +48,7 @@ jobs:
jq -c 'select(.filename|endswith(".dmg")) | select(.filename|endswith("-arm64.dmg")|not) | {name: "github.releases.downloads", tags: ["file=dmg", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
RESULT=$(jq -s -c "." output.json)
echo "::set-output name=result::${RESULT}"
echo "result=${RESULT}" >> $GITHUB_OUTPUT
env:
INTERVAL: 900
JSON_DATA: ${{ steps.get-stats.outputs.result }}

77
.github/workflows/get-version.yml vendored Normal file
View File

@@ -0,0 +1,77 @@
name: Parse and Validate a version string or tag
on:
workflow_call:
inputs:
version:
description: "A specific version to use"
required: false
type: string
outputs:
semVerStr:
description: "The full version string."
value: ${{ jobs.determine-version.outputs.semVerStr}}
semVerNum:
description: "The numerical part of the version string"
value: ${{ jobs.determine-version.outputs.semVerNum}}
revNum:
description: "The revision number"
value: ${{ jobs.determine-version.outputs.revNum}}
versionType:
description: "Type of the version. Values are [stable, alpha, beta, rc, unknown]"
value: ${{ jobs.determine-version.outputs.type }}
env:
JAVA_VERSION: 19
JAVA_DIST: 'temurin'
JAVA_CACHE: 'maven'
jobs:
determine-version:
name: 'Determines the version following semver'
runs-on: ubuntu-latest
outputs:
semVerNum: ${{ steps.versions.outputs.semVerNum }}
semVerStr: ${{ steps.versions.outputs.semVerStr }}
revNum: ${{ steps.versions.outputs.revNum }}
type: ${{ steps.versions.outputs.type}}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: ${{ env.JAVA_CACHE }}
- id: versions
name: Get version information
run: |
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
elif [[ "${{ inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ github.event.inputs.version }}"
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
REVCOUNT=`git rev-list --count HEAD`
TYPE="unknown"
if [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
TYPE="stable"
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-alpha[1-9] ]]; then
TYPE="alpha"
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-beta[1-9] ]]; then
TYPE="beta"
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-rc[1-9] ]]; then
TYPE="rc"
fi
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
echo "type=${TYPE}" >> $GITHUB_OUTPUT
- name: Validate Version
uses: skymatic/semver-validation-action@v2
with:
version: ${{ steps.versions.outputs.semVerStr }}

View File

@@ -13,9 +13,15 @@ env:
JAVA_VERSION: 19
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ github.event.inputs.version }}
build:
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
runs-on: ${{ matrix.os }}
needs: [get-version]
strategy:
fail-fast: false
matrix:
@@ -30,36 +36,26 @@ jobs:
xcode-path: '/Applications/Xcode_13.2.1.app'
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: ${{ env.JAVA_VERSION }}
java-package: 'jdk+fx'
architecture: ${{ matrix.architecture }}
cache: 'maven'
- id: versions
name: Apply version information
- name: Ensure major jfx version in pom equals in jdk
if: ${{ !contains(matrix.os, 'self-hosted') }}
shell: pwsh
run: |
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR}
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ github.event.inputs.version }}"
mvn versions:set -DnewVersion=${SEM_VER_STR}
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
REVCOUNT=`git rev-list --count HEAD`
echo "::set-output name=semVerStr::${SEM_VER_STR}"
echo "::set-output name=semVerNum::${SEM_VER_NUM}"
echo "::set-output name=revNum::${REVCOUNT}"
- name: Validate Version
uses: skymatic/semver-validation-action@v1
with:
version: ${{ steps.versions.outputs.semVerStr }}
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
exit 1
}
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B clean package -Pdependency-check,mac -DskipTests
- name: Patch target dir
@@ -73,7 +69,7 @@ jobs:
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--strip-native-commands
--no-header-files
--no-man-pages
@@ -92,7 +88,7 @@ jobs:
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2022 Skymatic GmbH"
--app-version "${{ steps.versions.outputs.semVerNum }}"
--app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.mac"
--java-options "-Xss5m"
@@ -100,7 +96,7 @@ jobs:
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\""
@@ -108,7 +104,7 @@ jobs:
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ steps.versions.outputs.revNum }}\""
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources
- name: Patch Cryptomator.app
@@ -118,8 +114,8 @@ jobs:
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
env:
VERSION_NO: ${{ steps.versions.outputs.semVerNum }}
REVISION_NO: ${{ steps.versions.outputs.revNum }}
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
- name: Generate license for dmg
run: >
mvn -B license:add-third-party
@@ -153,6 +149,10 @@ jobs:
CODESIGN_TMP_KEYCHAIN_PW: ${{ secrets.MACOS_CODESIGN_TMP_KEYCHAIN_PW }}
- name: Codesign
run: |
echo "Codesigning jdk files..."
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ -name 'jspawnhelper' -exec codesign --force -o runtime -s ${CODESIGN_IDENTITY} {} \;
echo "Codesigning jar contents..."
find Cryptomator.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
for JAR_PATH in `find Cryptomator.app -name "*.jar"`; do
if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then
@@ -203,7 +203,7 @@ jobs:
--icon ".VolumeIcon.icns" 512 758
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
env:
VERSION_NO: ${{ steps.versions.outputs.semVerNum }}
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/')
uses: cocoalibs/xcode-notarization-action@v1
@@ -214,7 +214,7 @@ jobs:
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
xcode-path: ${{ matrix.xcode-path }}
- name: Add possible alpha/beta tags to installer name
run: mv Cryptomator-*.dmg Cryptomator-${{ steps.versions.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import

40
.github/workflows/post-publish.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Post Release Publish Tasks
on:
release:
types: [published]
jobs:
get-version:
runs-on: ubuntu-latest
steps:
- name: Download source tarball
run: |
curl -L -H "Accept: application/vnd.github+json" ${{ github.event.release.tarball_url }} --output cryptomator-${{ github.event.release.tag_name }}.tar.gz
- name: Sign source tarball with key 615D449FE6E6A235
if: startsWith(github.ref, 'refs/tags/')
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
env:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Publish asc on GitHub Releases
uses: softprops/action-gh-release@v1
with:
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
cryptomator-*.tar.gz.asc
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/debian.yml|deploy to PPA>."
SLACK_FOOTER: false
MSG_MINIMAL: true

43
.github/workflows/release-check.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Release Check
on:
push:
branches:
- 'release/**'
- 'hotfix/**'
env:
JAVA_VERSION: 19
defaults:
run:
shell: bash
jobs:
release-check-precondition:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- id: validate-pom-version
name: Validate POM version
run: |
if [[ $GITHUB_REF =~ refs/heads/(hotfix|release)/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
else
echo "Failed to parse version"
exit 1
fi
if [[ ${SEM_VER_STR} == `mvn help:evaluate -Dexpression=project.version -q -DforceStdout` ]]; then
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
else
echo "Version not set in POM"
exit 1
fi
- name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file
run: |
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\"/>" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
exit 1
fi

View File

@@ -19,40 +19,37 @@ defaults:
shell: bash
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ github.event.inputs.version }}
build-msi:
name: Build .msi Installer
runs-on: windows-latest
needs: [get-version]
env:
LOOPBACK_ALIAS: 'cryptomator-vault'
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
java-package: 'jdk+fx'
cache: ${{ env.JAVA_CACHE }}
- id: versions
name: Apply version information
- name: Ensure major jfx version in pom equals in jdk
shell: pwsh
run: |
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR=${GITHUB_REF##*/}
mvn versions:set -DnewVersion=${SEM_VER_STR}
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${{ github.event.inputs.version }}"
mvn versions:set -DnewVersion=${SEM_VER_STR}
else
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
fi
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
REVCOUNT=`git rev-list --count HEAD`
echo "::set-output name=semVerStr::${SEM_VER_STR}"
echo "::set-output name=semVerNum::${SEM_VER_NUM}"
echo "::set-output name=revNum::${REVCOUNT}"
- name: Validate Version
uses: skymatic/semver-validation-action@v1
with:
version: ${{ steps.versions.outputs.semVerStr }}
$jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\."
$jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\."
if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) {
Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) "
exit 1
}
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: mvn -B clean package -Pdependency-check,win -DskipTests
- name: Patch target dir
@@ -66,7 +63,7 @@ jobs:
--verbose
--output runtime
--module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--strip-native-commands
--no-header-files
--no-man-pages
@@ -85,12 +82,12 @@ jobs:
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2022 Skymatic GmbH"
--app-version "${{ steps.versions.outputs.semVerNum }}.${{ steps.versions.outputs.revNum }}"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=org.cryptomator.jfuse.win"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
@@ -98,8 +95,9 @@ jobs:
--java-options "-Dcryptomator.p12Path=\"~/AppData/Roaming/Cryptomator/key.p12\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/AppData/Roaming/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
--java-options "-Dcryptomator.loopbackAlias=\"${{ env.LOOPBACK_ALIAS }}\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"msi-${{ steps.versions.outputs.revNum }}\""
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\""
--resource-dir dist/win/resources
@@ -107,11 +105,21 @@ jobs:
- name: Patch Application Directory
run: |
cp dist/win/contrib/* appdir/Cryptomator
- name: Set LOOPBACK_ALIAS in patchWebDAV.bat
shell: pwsh
run: |
$patchScript = "appdir\Cryptomator\patchWebDAV.bat"
try {
(Get-Content $patchScript ) -replace '::REPLACE ME', "SET LOOPBACK_ALIAS=`"${{ env.LOOPBACK_ALIAS}}`"" | Set-Content $patchScript
} catch {
Write-Host "Failed to set LOOPBACK_ALIAS for patchWebDAV.bat"
exit 1
}
- name: Fix permissions
run: attrib -r appdir/Cryptomator/Cryptomator.exe
shell: pwsh
- name: Codesign
uses: skymatic/code-sign-action@v1
uses: skymatic/code-sign-action@v2
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
@@ -142,7 +150,7 @@ jobs:
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2022 Skymatic GmbH"
--app-version "${{ steps.versions.outputs.semVerNum }}"
--app-version "${{ needs.get-version.outputs.semVerNum }}"
--win-menu
--win-dir-chooser
--win-shortcut-prompt
@@ -154,7 +162,7 @@ jobs:
env:
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
- name: Codesign MSI
uses: skymatic/code-sign-action@v1
uses: skymatic/code-sign-action@v2
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
@@ -163,7 +171,7 @@ jobs:
timestampUrl: 'http://timestamp.digicert.com'
folder: installer
- name: Add possible alpha/beta tags to installer name
run: mv installer/Cryptomator-*.msi Cryptomator-${{ steps.versions.outputs.semVerStr }}-x64.msi
run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-x64.msi
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -188,23 +196,11 @@ jobs:
files: |
*.msi
*.asc
outputs:
semVerNum: ${{ steps.versions.outputs.semVerNum }}
semVerStr: ${{ steps.versions.outputs.semVerStr }}
revNum: ${{ steps.versions.outputs.revNum }}
call-winget-flow:
needs: [build-msi]
if: github.event.action == 'published'
uses: ./.github/workflows/winget.yml
with:
releaseTag: ${{ github.event.release.tag_name }}
build-exe:
name: Build .exe installer
runs-on: windows-latest
needs: [build-msi]
needs: [get-version, build-msi]
steps:
- uses: actions/checkout@v3
- name: Download .msi
@@ -240,7 +236,7 @@ jobs:
"${WIX}/bin/candle.exe" dist/win/bundle/bundleWithWinfsp.wxs
-ext WixBalExtension
-out dist/win/bundle/
-dBundleVersion="${{ needs.build-msi.outputs.semVerNum }}.${{ needs.build-msi.outputs.revNum }}"
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
-dBundleVendor="Skymatic GmbH"
-dBundleCopyright="(C) 2016 - 2022 Skymatic GmbH"
-dAboutUrl="https://cryptomator.org"
@@ -257,7 +253,7 @@ jobs:
-ib installer/unsigned/Cryptomator-Installer.exe
-o tmp/engine.exe
- name: Codesign burn engine
uses: skymatic/code-sign-action@v1
uses: skymatic/code-sign-action@v2
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
@@ -271,7 +267,7 @@ jobs:
-ab tmp/engine.exe installer/unsigned/Cryptomator-Installer.exe
-o installer/Cryptomator-Installer.exe
- name: Codesign EXE
uses: skymatic/code-sign-action@v1
uses: skymatic/code-sign-action@v2
with:
certificate: ${{ secrets.WIN_CODESIGN_P12_BASE64 }}
password: ${{ secrets.WIN_CODESIGN_P12_PW }}
@@ -280,7 +276,7 @@ jobs:
timestampUrl: 'http://timestamp.digicert.com'
folder: installer
- name: Add possible alpha/beta tags to installer name
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.build-msi.outputs.semVerStr }}-x64.exe
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-x64.exe
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -305,6 +301,18 @@ jobs:
files: |
Cryptomator-*.exe
Cryptomator-*.asc
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "Windows build of ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} finished."
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/winget.yml|deploy to Winget>."
SLACK_FOOTER: false
MSG_MINIMAL: true
allowlist:
name: Anti Virus Allowlisting
@@ -313,12 +321,12 @@ jobs:
needs: [build-msi, build-exe]
steps:
- name: Download .msi
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: msi
path: msi
- name: Download .exe
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: exe
path: exe
@@ -328,7 +336,7 @@ jobs:
cp msi/*.msi files
cp exe/*.exe files
- name: Upload to Kaspersky
uses: SamKirkland/FTP-Deploy-Action@4.3.0
uses: SamKirkland/FTP-Deploy-Action@4.3.3
with:
protocol: ftps
server: allowlist.kaspersky-labs.com

View File

@@ -1,11 +1,6 @@
name: Release to Winget
on:
workflow_call:
inputs:
releaseTag:
required: true
type: string
workflow_dispatch:
inputs:
releaseTag:
@@ -18,7 +13,7 @@ jobs:
name: Publish on winget repo
runs-on: windows-latest
steps:
- name: Get download url for msi artifacts
- name: Get download url for release assets
id: get-release-assets
uses: actions/github-script@v6
with:

View File

@@ -61,7 +61,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
- File names get encrypted
- Folder structure gets obfuscated
- Use as many vaults in your Dropbox as you want, each having individual passwords
- Three thousand commits for the security of your data!! :tada:
- Four thousand commits for the security of your data!! :tada:
### Privacy
@@ -85,7 +85,7 @@ For more information on the security details visit [cryptomator.org](https://doc
### Dependencies
* JDK 17 (e.g. temurin)
* JDK 19 (e.g. temurin)
* Maven 3
### Run Maven
@@ -99,10 +99,6 @@ mvn clean install
This will build all the jars and bundle them together with their OS-specific dependencies under `target`. This can now be used to build native packages.
### Start Cryptomator
If you unzip the buildkit for your OS, you will find a launcher script with some basic settings. You might want to adjust these to your needs. To start Cryptomator, simply execute the launcher script from a terminal, e.g. `launcher-linux.sh`, if you're on a Linux system.
## License
This project is dual-licensed under the GPLv3 for FOSS projects as well as a commercial license for independent software vendors and resellers. If you want to modify this application under different conditions, feel free to contact our support team.

View File

@@ -66,6 +66,7 @@
</content_rating>
<releases>
<release date="2022-12-06" version="1.6.16"/>
<release date="2022-10-06" version="1.6.15"/>
<release date="2022-08-31" version="1.6.14"/>
<release date="2022-07-27" version="1.6.12"/>

View File

@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils
Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-19
Build-Depends: debhelper (>=10), coffeelibs-jdk-19, libgtk2.0-0, libgtk-3-0, libxxf86vm1, libgl1
Standards-Version: 4.5.0
Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git

View File

@@ -5,6 +5,12 @@
#export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-19-coffeelibs
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
ifeq ($(DEB_BUILD_ARCH),amd64)
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
else ifeq ($(DEB_BUILD_ARCH),arm64)
JMODS_PATH = jmods/aarch64:${JAVA_HOME}/jmods
endif
%:
dh $@
@@ -20,7 +26,8 @@ override_dh_auto_build:
ln -s ../common/org.cryptomator.Cryptomator512.png resources/cryptomator.png
$(JAVA_HOME)/bin/jlink \
--output runtime \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
--module-path "${JMODS_PATH}" \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
--strip-native-commands \
--no-header-files \
--no-man-pages \

4
dist/win/build.bat vendored
View File

@@ -9,8 +9,9 @@ SET ABOUT_URL="https://cryptomator.org"
SET UPDATE_URL="https://cryptomator.org/downloads/"
SET HELP_URL="https://cryptomator.org/contact/"
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
SET LOOPBACK_ALIAS="cryptomator-vault"
powershell -NoLogo -NoExit -ExecutionPolicy Unrestricted -Command .\build.ps1^
powershell -NoLogo -ExecutionPolicy Unrestricted -Command .\build.ps1^
-AppName %APPNAME%^
-MainJarGlob "%MAIN_JAR_GLOB%"^
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
@@ -20,4 +21,5 @@ powershell -NoLogo -NoExit -ExecutionPolicy Unrestricted -Command .\build.ps1^
-AboutUrl "%ABOUT_URL%"^
-HelpUrl "%HELP_URL%"^
-UpdateUrl "%UPDATE_URL%"^
-LoopbackAlias "%LOOPBACK_ALIAS%"^
-Clean 1

10
dist/win/build.ps1 vendored
View File

@@ -8,6 +8,7 @@ Param(
[Parameter(Mandatory, HelpMessage="Please provide a help url")][string] $HelpUrl,
[Parameter(Mandatory, HelpMessage="Please provide an update url")][string] $UpdateUrl,
[Parameter(Mandatory, HelpMessage="Please provide an about url")][string] $AboutUrl,
[Parameter(Mandatory, HelpMessage="Please provide an alias for localhost")][string] $LoopbackAlias,
[bool] $clean
)
@@ -87,6 +88,7 @@ if ($clean -and (Test-Path -Path $appPath)) {
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/$AppName/ipc.socket`"" `
--java-options "-Dcryptomator.p12Path=`"~/AppData/Roaming/$AppName/key.p12`"" `
--java-options "-Dcryptomator.mountPointsDir=`"~/$AppName`"" `
--java-options "-Dcryptomator.loopbackAlias=`"$LoopbackAlias`"" `
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
--java-options "-Dcryptomator.integrationsWin.keychainPaths=`"~/AppData/Roaming/$AppName/keychain.json`"" `
--java-options "-Dcryptomator.showTrayIcon=true" `
@@ -107,6 +109,14 @@ if ($clean -and (Test-Path -Path $appPath)) {
# patch app dir
Copy-Item "contrib\*" -Destination "$AppName"
attrib -r "$AppName\$AppName.exe"
# patch batch script to set hostfile
$webDAVPatcher = "$AppName\patchWebDAV.bat"
try {
(Get-Content $webDAVPatcher ) -replace '::REPLACE ME', "SET LOOPBACK_ALIAS=`"$LoopbackAlias`"" | Set-Content $webDAVPatcher
} catch {
Write-Host "Failed to set LOOPBACK_ALIAS for patchWebDAV.bat"
exit 1
}
# create .msi
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"

View File

@@ -1,6 +1,6 @@
@echo off
:: Default values for Cryptomator builds
SET LOOPBACK_ALIAS="cryptomator-vault"
::REPLACE ME
cd %~dp0
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1^

View File

@@ -23,6 +23,11 @@
<?define JpUpgradeVersionOnlyDetectDowngrade="yes"?>
<?endif?>
<!-- Cryptomator defaults -->
<?define IconFileEncryptedData= "Cryptomator-Vault.ico" ?>
<?define ProgIdContentType= "application/vnd.cryptomator.encrypted" ?>
<?define CloseApplicationTarget= "cryptomator.exe" ?>
<?include $(var.JpConfigDir)/overrides.wxi ?>
<Product
@@ -65,16 +70,16 @@
<CustomAction Id="JpDisallowDowngrade" Error="!(loc.DowngradeErrorMessage)" />
<?endif?>
<!-- Looking for legacy Cryptomator versions-->
<Property Id="OLDEXEINSTALLER">
<RegistrySearch Id="InnoSetupInstallation" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\Cryptomator_is1" Type="raw" Name="DisplayName" />
</Property>
<!-- Block installation if innosetup entry of Cryptomator is found -->
<!-- TODO: localize -->
<Condition Message="A lower version of [ProductName] is already installed. Uninstall it first and then start the setup again. Setup will now exit.">
<![CDATA[Installed OR NOT OLDEXEINSTALLER]]>
</Condition>
<?ifndef SkipCryptomatorLegacyCheck ?>
<!-- Block installation if innosetup entry of Cryptomator is found -->
<Property Id="OLDEXEINSTALLER">
<RegistrySearch Id="InnoSetupInstallation" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\Cryptomator_is1" Type="raw" Name="DisplayName" />
</Property>
<!-- TODO: localize -->
<Condition Message="A lower version of [ProductName] is already installed. Uninstall it first and then start the setup again. Setup will now exit.">
<![CDATA[Installed OR NOT OLDEXEINSTALLER]]>
</Condition>
<?endif?>
<!-- Cryptomator uses UNIX Sockets, which are supported starting with Windows 10 v1803-->
<Property Id="WINDOWSBUILDNUMBER" Secure="yes">
<RegistrySearch Id="BuildNumberSearch" Root="HKLM" Key="SOFTWARE\Microsoft\Windows NT\CurrentVersion" Name="CurrentBuildNumber" Type="raw" />
@@ -86,7 +91,7 @@
<!-- Non-Opening ProgID -->
<DirectoryRef Id="INSTALLDIR">
<Component Win64="yes" Id="nonStartingProgID" >
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileC9rC9s)" Name="$(var.IconFileC9rC9s)"></File>
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileEncryptedData)" Name="$(var.IconFileEncryptedData)"></File>
<ProgId Id="$(var.JpAppName).Encrypted.1" Description="$(var.JpAppName) Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
<Extension Id="c9r" Advertise="no" ContentType="$(var.ProgIdContentType)">
<MIME ContentType="$(var.ProgIdContentType)" Default="yes"></MIME>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Stub by design -->
<!--
<!-- jPackage Section
overrides.wxi is a placeholder to set/alter WiX variables referenced from default
main.wxs file.
@@ -30,10 +30,21 @@ Should be defined to enable upgrades and undefined to disable upgrades.
Default value is `yes`.
-->
<!-- Non-opening ProgID settings-->
<?define IconFileC9rC9s= "Cryptomator-Vault.ico" ?>
<?define ProgIdContentType= "application/vnd.cryptomator.encrypted" ?>
<!-- Cryptomator Section
<!-- Close Application util -->
<?define CloseApplicationTarget= "cryptomator.exe" ?>
Non-opening ProgID settings:
- IconFileEncryptedData
Full file name of icon file used for encrypted data files. Default is "Cryptomator-Vault.ico"
- ProgIdContentType
Media Type of the encrypted data files. Default is "application/vnd.cryptomator.encrypted"
Close Application settings:
- CloseApplicationTarget
Full name of executable to be checkd in the close application util. Default is "cryptomator.exe"
Legacy Installation settings:
- SkipCryptomatorLegacyCheck
Should be defined to disable checking for the inno setup installation of Cryptomator and undefined, to enable it.
-->
<Include/>

13
pom.xml
View File

@@ -28,8 +28,7 @@
<nonModularGroupIds>com.github.jnr,org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
<!-- cryptomator dependencies -->
<cryptomator.cryptolib.version>2.1.0-rc1</cryptomator.cryptolib.version>
<cryptomator.cryptofs.version>2.4.5</cryptomator.cryptofs.version>
<cryptomator.cryptofs.version>2.5.3</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.2.0-beta2</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.1.2</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.1.2</cryptomator.integrations.mac.version>
@@ -59,17 +58,12 @@
<!-- build-time dependencies -->
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
<dependency-check.version>7.2.1</dependency-check.version>
<dependency-check.version>7.4.0</dependency-check.version>
<jacoco.version>0.8.8</jacoco.version>
</properties>
<dependencies>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptolib</artifactId>
<version>${cryptomator.cryptolib.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
@@ -373,6 +367,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<!-- sort jars into two buckets (classpath and modulepath). exclude openjfx, which gets jlinked separately -->
<execution>
<id>copy-mods</id>
<phase>prepare-package</phase>
@@ -382,7 +377,7 @@
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/mods</outputDirectory>
<excludeGroupIds>${nonModularGroupIds}</excludeGroupIds>
<excludeGroupIds>org.openjfx,${nonModularGroupIds}</excludeGroupIds>
</configuration>
</execution>
<execution>

View File

@@ -26,6 +26,7 @@ public class Environment {
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
private static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
private static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
private static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
@@ -45,6 +46,7 @@ public class Environment {
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME);
logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME);
@@ -90,6 +92,10 @@ public class Environment {
return getPath(LOG_DIR_PROP_NAME).map(this::replaceHomeDir);
}
public Optional<String> getLoopbackAlias() {
return Optional.ofNullable(System.getProperty(LOOPBACK_ALIAS_PROP_NAME));
}
public Optional<Path> getPluginDir() {
return getPath(PLUGIN_DIR_PROP_NAME).map(this::replaceHomeDir);
}
@@ -112,22 +118,13 @@ public class Environment {
}
public int getMinPwLength() {
return getInt(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH);
}
public boolean showTrayIcon() {
return Boolean.getBoolean(TRAY_ICON_PROP_NAME);
}
private int getInt(String propertyName, int defaultValue) {
String value = System.getProperty(propertyName);
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) { // includes "null" values
return defaultValue;
}
}
private Optional<Path> getPath(String propertyName) {
String value = System.getProperty(propertyName);
return Optional.ofNullable(value).map(Paths::get);

View File

@@ -41,6 +41,8 @@ public class VaultStats {
private final LongProperty totalBytesDecrypted = new SimpleLongProperty();
private final LongProperty filesRead = new SimpleLongProperty();
private final LongProperty filesWritten = new SimpleLongProperty();
private final LongProperty filesAccessed = new SimpleLongProperty();
private final LongProperty totalFilesAccessed = new SimpleLongProperty();
private final ObjectProperty<Instant> lastActivity = new SimpleObjectProperty<>();
@Inject
@@ -82,6 +84,8 @@ public class VaultStats {
var oldAccessCount = filesRead.get() + filesWritten.get();
filesRead.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesRead).orElse(0L));
filesWritten.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesWritten).orElse(0L));
filesAccessed.set(stats.map(CryptoFileSystemStats::pollAmountOfAccesses).orElse(0L));
totalFilesAccessed.set(stats.map(CryptoFileSystemStats::pollTotalAmountOfAccesses).orElse(0L));
var newAccessCount = filesRead.get() + filesWritten.get();
// check for any I/O activity
@@ -188,6 +192,19 @@ public class VaultStats {
public long getFilesWritten() {return filesWritten.get();}
public LongProperty filesAccessed() {
return filesAccessed;}
public long getFilesAccessed() {return filesAccessed.get();}
public LongProperty totalFilesAccessed(){
return totalFilesAccessed;
}
public long getTotalFilesAccessed(){
return totalFilesAccessed.get();
}
public ObjectProperty<Instant> lastActivityProperty() {
return lastActivity;
}

View File

@@ -2,19 +2,20 @@ package org.cryptomator.ui.addvaultwizard;
import dagger.Lazy;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.fxapp.FxApplicationStyle;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.image.Image;
@@ -23,6 +24,7 @@ import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Objects;
import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
@@ -40,12 +42,10 @@ public class ChooseExistingVaultController implements FxController {
private final ObjectProperty<Vault> vault;
private final VaultListManager vaultListManager;
private final ResourceBundle resourceBundle;
private final Settings settings;
private Image screenshot;
private final ObservableValue<Image> screenshot;
@Inject
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle, Settings settings) {
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle, FxApplicationStyle applicationStyle) {
this.window = window;
this.welcomeScene = welcomeScene;
this.successScene = successScene;
@@ -54,16 +54,20 @@ public class ChooseExistingVaultController implements FxController {
this.vault = vault;
this.vaultListManager = vaultListManager;
this.resourceBundle = resourceBundle;
this.settings = settings;
this.screenshot = applicationStyle.appliedThemeProperty().map(this::selectScreenshot);
}
@FXML
public void initialize() {
private Image selectScreenshot(Theme theme) {
String imageResourcePath;
if (SystemUtils.IS_OS_MAC) {
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-mac"+(UiTheme.LIGHT == settings.theme().get()? "":"-dark")+".png").toString());
imageResourcePath = switch (theme) {
case LIGHT -> "/img/select-masterkey-mac.png";
case DARK -> "/img/select-masterkey-mac-dark.png";
};
} else {
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-win.png").toString());
imageResourcePath = "/img/select-masterkey-win.png";
}
return new Image((Objects.requireNonNull(getClass().getResource(imageResourcePath)).toString()));
}
@FXML
@@ -92,8 +96,13 @@ public class ChooseExistingVaultController implements FxController {
/* Getter */
public Image getScreenshot() {
public ObservableValue<Image> screenshotProperty() {
return screenshot;
}
public Image getScreenshot() {
return screenshot.getValue();
}
}

View File

@@ -27,6 +27,7 @@ public enum FontAwesome5Icon {
FILE("\uF15B"), //
FILE_IMPORT("\uF56F"), //
FOLDER_OPEN("\uF07C"), //
FUNNEL("\uF0B0"), //
HAND_HOLDING_HEART("\uF4BE"), //
HEART("\uF004"), //
HDD("\uF0A0"), //

View File

@@ -12,6 +12,8 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import java.util.Optional;
@@ -24,9 +26,10 @@ public class FxApplicationStyle {
private final Optional<UiAppearanceProvider> appearanceProvider;
private final LicenseHolder licenseHolder;
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
private final ObjectProperty<Theme> appliedTheme = new SimpleObjectProperty<>(Theme.LIGHT);
@Inject
public FxApplicationStyle(Settings settings, Optional<UiAppearanceProvider> appearanceProvider, LicenseHolder licenseHolder){
public FxApplicationStyle(Settings settings, Optional<UiAppearanceProvider> appearanceProvider, LicenseHolder licenseHolder) {
this.settings = settings;
this.appearanceProvider = appearanceProvider;
this.licenseHolder = licenseHolder;
@@ -91,6 +94,7 @@ public class FxApplicationStyle {
} else {
Application.setUserAgentStylesheet(stylesheet.toString());
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.LIGHT));
appliedTheme.set(Theme.LIGHT);
}
}
@@ -103,6 +107,11 @@ public class FxApplicationStyle {
} else {
Application.setUserAgentStylesheet(stylesheet.toString());
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.DARK));
appliedTheme.set(Theme.DARK);
}
}
public ObjectProperty<Theme> appliedThemeProperty() {
return appliedTheme;
}
}

View File

@@ -8,15 +8,34 @@ import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.BooleanExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ListView;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.util.StringConverter;
import java.util.Arrays;
import java.util.ResourceBundle;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static org.cryptomator.cryptofs.health.api.DiagnosticResult.Severity;
import static org.cryptomator.ui.health.Result.FixState.FIXABLE;
import static org.cryptomator.ui.health.Result.FixState.FIXED;
import static org.cryptomator.ui.health.Result.FixState.FIXING;
import static org.cryptomator.ui.health.Result.FixState.FIX_FAILED;
import static org.cryptomator.ui.health.Result.FixState.NOT_FIXABLE;
@HealthCheckScoped
public class CheckDetailController implements FxController {
@@ -35,30 +54,48 @@ public class CheckDetailController implements FxController {
private final Binding<Number> countOfCritSeverity;
private final Binding<Boolean> warnOrCritsExist;
private final ResultListCellFactory resultListCellFactory;
private final ResultFixApplier resultFixApplier;
private final ResourceBundle resourceBundle;
private final BooleanProperty fixAllInfoResultsExecuted;
private final BooleanBinding fixAllInfoResultsPossible;
private final ObjectProperty<Predicate<Result>> resultsFilter;
public ListView<Result> resultsListView;
public ChoiceBox<DiagnosticResult.Severity> severityChoiceBox;
public ChoiceBox<Result.FixState> fixStateChoiceBox;
private Subscription resultSubscription;
@Inject
public CheckDetailController(ObjectProperty<Check> selectedTask, ResultListCellFactory resultListCellFactory) {
public CheckDetailController(ObjectProperty<Check> selectedTask, ResultListCellFactory resultListCellFactory, ResultFixApplier resultFixApplier, ResourceBundle resourceBundle) {
this.resultListCellFactory = resultListCellFactory;
this.resultFixApplier = resultFixApplier;
this.resourceBundle = resourceBundle;
this.results = EasyBind.wrapList(FXCollections.observableArrayList());
this.check = selectedTask;
this.checkState = selectedTask.flatMap(Check::stateProperty);
this.checkName = selectedTask.map(Check::getName).orElse("");
this.checkRunning = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.RUNNING::equals).orElse(false));
this.checkScheduled = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SCHEDULED::equals).orElse(false));
this.checkSkipped =BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SKIPPED::equals).orElse(false));
this.checkSkipped = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SKIPPED::equals).orElse(false));
this.checkSucceeded = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SUCCEEDED::equals).orElse(false));
this.checkFailed = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.ERROR::equals).orElse(false));
this.checkCancelled = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.CANCELLED::equals).orElse(false));
this.checkFinished = checkSucceeded.or(checkFailed).or(checkCancelled);
this.countOfWarnSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.WARN));
this.countOfCritSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.CRITICAL));
this.warnOrCritsExist = EasyBind.combine(checkSucceeded, countOfWarnSeverity, countOfCritSeverity, (suceeded, warns, crits) -> suceeded && (warns.longValue() > 0 || crits.longValue() > 0) );
this.countOfWarnSeverity = results.reduce(countSeverity(Severity.WARN));
this.countOfCritSeverity = results.reduce(countSeverity(Severity.CRITICAL));
this.warnOrCritsExist = EasyBind.combine(checkSucceeded, countOfWarnSeverity, countOfCritSeverity, (suceeded, warns, crits) -> suceeded && (warns.longValue() > 0 || crits.longValue() > 0));
this.fixAllInfoResultsExecuted = new SimpleBooleanProperty(false);
this.fixAllInfoResultsPossible = Bindings.createBooleanBinding(() -> results.stream().anyMatch(this::isFixableInfoResult), results) //
.and(fixAllInfoResultsExecuted.not());
this.resultsFilter = new SimpleObjectProperty<>(r -> true);
selectedTask.addListener(this::selectedTaskChanged);
}
private boolean isFixableInfoResult(Result r) {
return r.diagnosis().getSeverity() == Severity.INFO && r.getState() == FIXABLE;
}
private void selectedTaskChanged(ObservableValue<? extends Check> observable, Check oldValue, Check newValue) {
if (resultSubscription != null) {
resultSubscription.unsubscribe();
@@ -66,6 +103,8 @@ public class CheckDetailController implements FxController {
if (newValue != null) {
resultSubscription = EasyBind.bindContent(results, newValue.getResults());
}
severityChoiceBox.setValue(null);
fixStateChoiceBox.setValue(null);
}
private Function<Stream<? extends Result>, Long> countSeverity(DiagnosticResult.Severity severity) {
@@ -74,8 +113,110 @@ public class CheckDetailController implements FxController {
@FXML
public void initialize() {
resultsListView.setItems(results);
resultsListView.setItems(results.filtered(resultsFilter));
resultsListView.setCellFactory(resultListCellFactory);
severityChoiceBox.getItems().add(null);
severityChoiceBox.getItems().addAll(Arrays.stream(DiagnosticResult.Severity.values()).toList());
severityChoiceBox.setConverter(new SeverityStringifier());
severityChoiceBox.setValue(null);
fixStateChoiceBox.getItems().add(null);
fixStateChoiceBox.getItems().addAll(Arrays.stream(Result.FixState.values()).toList());
fixStateChoiceBox.setConverter(new FixStateStringifier());
fixStateChoiceBox.setValue(null);
resultsFilter.bind(Bindings.createObjectBinding(() -> this::filterResults, severityChoiceBox.valueProperty(), fixStateChoiceBox.valueProperty()));
}
private boolean filterResults(Result r) {
var desiredFixState = fixStateChoiceBox.getValue();
var desiredSeverity = severityChoiceBox.getValue();
return (desiredFixState == null || r.getState() == desiredFixState) && (desiredSeverity == null || r.diagnosis().getSeverity() == desiredSeverity);
}
@FXML
public void fixAllInfoResults() {
fixAllInfoResultsExecuted.setValue(true);
results.stream().filter(this::isFixableInfoResult).forEach(resultFixApplier::fix);
}
@FXML
public void copyResultDetails() {
var result = resultsListView.getSelectionModel().getSelectedItem();
if (result != null) {
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString(result.diagnosis().toString());
Clipboard.getSystemClipboard().setContent(clipboardContent);
}
}
/* -- Internal classes -- */
class SeverityStringifier extends StringConverter<Severity> {
@Override
public String toString(Severity object) {
if (object == null) {
return resourceBundle.getString("health.result.severityFilter.all");
}
return switch (object) {
case GOOD -> resourceBundle.getString("health.result.severityFilter.good");
case INFO -> resourceBundle.getString("health.result.severityFilter.info");
case WARN -> resourceBundle.getString("health.result.severityFilter.warn");
case CRITICAL -> resourceBundle.getString("health.result.severityFilter.crit");
};
}
@Override
public Severity fromString(String string) {
if (resourceBundle.getString("health.result.severityFilter.good").equals(string)) {
return Severity.GOOD;
} else if (resourceBundle.getString("health.result.severityFilter.info").equals(string)) {
return Severity.INFO;
} else if (resourceBundle.getString("health.result.severityFilter.warn").equals(string)) {
return Severity.WARN;
} else if (resourceBundle.getString("health.result.severityFilter.crit").equals(string)) {
return Severity.CRITICAL;
} else {
return null;
}
}
}
class FixStateStringifier extends StringConverter<Result.FixState> {
@Override
public String toString(Result.FixState object) {
if (object == null) {
return resourceBundle.getString("health.result.fixStateFilter.all");
}
return switch (object) {
case FIXABLE -> resourceBundle.getString("health.result.fixStateFilter.fixable");
case NOT_FIXABLE -> resourceBundle.getString("health.result.fixStateFilter.notFixable");
case FIXING -> resourceBundle.getString("health.result.fixStateFilter.fixing");
case FIXED -> resourceBundle.getString("health.result.fixStateFilter.fixed");
case FIX_FAILED -> resourceBundle.getString("health.result.fixStateFilter.fixFailed");
};
}
@Override
public Result.FixState fromString(String string) {
if (resourceBundle.getString("health.result.fixStateFilter.fixable").equals(string)) {
return FIXABLE;
} else if (resourceBundle.getString("health.result.fixStateFilter.notFixable").equals(string)) {
return NOT_FIXABLE;
} else if (resourceBundle.getString("health.result.fixStateFilter.fixing").equals(string)) {
return FIXING;
} else if (resourceBundle.getString("health.result.fixStateFilter.fixed").equals(string)) {
return FIXED;
} else if (resourceBundle.getString("health.result.fixStateFilter.fixFailed").equals(string)) {
return FIX_FAILED;
} else {
return null;
}
}
}
/* Getter/Setter */
@@ -175,4 +316,12 @@ public class CheckDetailController implements FxController {
public Check getCheck() {
return check.get();
}
public ObservableValue<Boolean> fixAllInfoResultsPossibleProperty() {
return fixAllInfoResultsPossible;
}
public boolean getFixAllInfoResultsPossible() {
return fixAllInfoResultsPossible.getValue();
}
}

View File

@@ -70,7 +70,7 @@ public class CheckExecutor {
try (var masterkeyClone = masterkey.copy(); //
var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) {
c.getHealthCheck().check(vaultPath, vaultConfig, masterkeyClone, cryptor, diagnosis -> {
Platform.runLater(() -> c.getResults().add(Result.create(diagnosis)));
Platform.runLater(() -> c.getResults().add(Result.create(diagnosis, vaultPath, vaultConfig, masterkeyClone, cryptor)));
highestResultSeverity = Comparators.max(highestResultSeverity, diagnosis.getSeverity());
});
}

View File

@@ -31,7 +31,7 @@ public class CheckStateIconView extends FontAwesome5IconView {
this.severity = EasyBind.wrapNullable(check).mapObservable(Check::highestResultSeverityProperty).asOrdinary();
this.glyph.bind(Bindings.createObjectBinding(this::glyphForState, state, severity));
this.subscriptions = List.of( //
EasyBind.includeWhen(getStyleClass(), "glyph-icon-muted", Bindings.equal(state, Check.CheckState.SKIPPED).or(Bindings.equal(state, Check.CheckState.CANCELLED))), //
EasyBind.includeWhen(getStyleClass(), "glyph-icon-muted", Bindings.equal(state, Check.CheckState.SKIPPED).or(Bindings.equal(state, Check.CheckState.CANCELLED)).or(Bindings.equal(severity, DiagnosticResult.Severity.INFO))), //
EasyBind.includeWhen(getStyleClass(), "glyph-icon-primary", Bindings.equal(severity, DiagnosticResult.Severity.GOOD)), //
EasyBind.includeWhen(getStyleClass(), "glyph-icon-orange", Bindings.equal(severity, DiagnosticResult.Severity.WARN).or(Bindings.equal(severity, DiagnosticResult.Severity.CRITICAL))), //
EasyBind.includeWhen(getStyleClass(), "glyph-icon-red", Bindings.equal(state, Check.CheckState.ERROR)) //

View File

@@ -1,10 +1,14 @@
package org.cryptomator.ui.health;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.Masterkey;
import javafx.beans.Observable;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import java.nio.file.Path;
record Result(DiagnosticResult diagnosis, ObjectProperty<FixState> fixState) {
@@ -16,8 +20,8 @@ record Result(DiagnosticResult diagnosis, ObjectProperty<FixState> fixState) {
FIX_FAILED
}
public static Result create(DiagnosticResult diagnosis) {
FixState initialState = diagnosis.getSeverity() == DiagnosticResult.Severity.WARN ? FixState.FIXABLE : FixState.NOT_FIXABLE;
public static Result create(DiagnosticResult diagnosis, Path vaultPath, VaultConfig config, Masterkey masterkey, Cryptor cryptor) {
FixState initialState = diagnosis.getFix(vaultPath, config, masterkey, cryptor).map( _f -> FixState.FIXABLE).orElse(FixState.NOT_FIXABLE);
return new Result(diagnosis, new SimpleObjectProperty<>(initialState));
}

View File

@@ -23,6 +23,8 @@ import java.util.concurrent.atomic.AtomicReference;
@HealthCheckScoped
class ResultFixApplier {
private static final Logger LOG = LoggerFactory.getLogger(ResultFixApplier.class);
private final Path vaultPath;
private final SecureRandom csprng;
private final Masterkey masterkey;
@@ -40,25 +42,34 @@ class ResultFixApplier {
public CompletionStage<Void> fix(Result result) {
Preconditions.checkArgument(result.getState() == Result.FixState.FIXABLE);
result.setState(Result.FixState.FIXING);
return CompletableFuture.runAsync(() -> fix(result.diagnosis()), sequentialExecutor)
return CompletableFuture.runAsync(() -> result.setState(Result.FixState.FIXING), Platform::runLater) //
.thenRunAsync(() -> fix(result.diagnosis()), sequentialExecutor) //
.whenCompleteAsync((unused, throwable) -> {
var fixed = throwable == null ? Result.FixState.FIXED : Result.FixState.FIX_FAILED;
result.setState(fixed);
final Result.FixState s;
if (throwable == null) {
LOG.debug("Fix for {} applied successful.", result.diagnosis().getClass().getName());
s = Result.FixState.FIXED;
} else {
LOG.error("Failed to apply fix for {}", result.diagnosis().getClass().getName(), throwable);
s = Result.FixState.FIX_FAILED;
}
result.setState(s);
}, Platform::runLater);
}
public void fix(DiagnosticResult diagnosis) {
Preconditions.checkArgument(diagnosis.getSeverity() == DiagnosticResult.Severity.WARN, "Unfixable result");
private void fix(DiagnosticResult diagnosis) {
try (var masterkeyClone = masterkey.copy(); //
var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) {
diagnosis.fix(vaultPath, vaultConfig, masterkeyClone, cryptor);
diagnosis.getFix(vaultPath, vaultConfig, masterkeyClone, cryptor) //
.orElseThrow(() -> new IllegalStateException("No fix for diagnosis " + diagnosis.getClass().getName() + " implemented.")) //
.apply();
} catch (Exception e) {
throw new FixFailedException(e);
}
}
public static class FixFailedException extends CompletionException {
private FixFailedException(Throwable cause) {
super(cause);
}

View File

@@ -12,7 +12,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.ObjectBinding;
@@ -34,7 +33,6 @@ public class ResultListCellController implements FxController {
private static final FontAwesome5Icon WARN_ICON = FontAwesome5Icon.EXCLAMATION_TRIANGLE;
private static final FontAwesome5Icon CRIT_ICON = FontAwesome5Icon.TIMES;
private final Logger LOG = LoggerFactory.getLogger(ResultListCellController.class);
private final ObjectProperty<Result> result;
private final ObservableValue<DiagnosticResult.Severity> severity;
@@ -42,17 +40,17 @@ public class ResultListCellController implements FxController {
private final ResultFixApplier fixApplier;
private final ObservableValue<Result.FixState> fixState;
private final ObjectBinding<FontAwesome5Icon> severityGlyph;
private final ObjectBinding<FontAwesome5Icon> fixGlyph;
private final BooleanBinding fixable;
private final BooleanBinding fixing;
private final BooleanBinding fixed;
private final BooleanBinding fixFailed;
private final BooleanBinding fixRunningOrDone;
private final ObservableValue<FontAwesome5Icon> fixGlyph;
private final List<Subscription> subscriptions;
private final Tooltip fixSuccess;
private final Tooltip fixFail;
private final Tooltip fixStateTip;
private final Tooltip severityTip;
private AutoAnimator fixRunningRotator;
private final ResourceBundle resourceBundle;
/* FXML */
public FontAwesome5IconView severityView;
@@ -60,23 +58,54 @@ public class ResultListCellController implements FxController {
@Inject
public ResultListCellController(ResultFixApplier fixApplier, ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle;
this.result = new SimpleObjectProperty<>(null);
this.severity = result.map(Result::diagnosis).map(DiagnosticResult::getSeverity);
this.description = result.map(Result::getDescription).orElse("");
this.fixApplier = fixApplier;
this.fixState = result.flatMap(Result::fixState);
this.severityGlyph = Bindings.createObjectBinding(this::getSeverityGlyph, result);
this.fixGlyph = Bindings.createObjectBinding(this::getFixGlyph, fixState);
this.fixable = Bindings.createBooleanBinding(this::isFixable, fixState);
this.fixing = Bindings.createBooleanBinding(this::isFixing, fixState);
this.fixed = Bindings.createBooleanBinding(this::isFixed, fixState);
this.fixFailed = Bindings.createBooleanBinding(this::isFixFailed, fixState);
this.fixRunningOrDone = fixing.or(fixed).or(fixFailed);
this.fixGlyph = fixState.map(this::getFixGlyph);
this.subscriptions = new ArrayList<>();
this.fixSuccess = new Tooltip(resourceBundle.getString("health.fix.successTip"));
this.fixFail = new Tooltip(resourceBundle.getString("health.fix.failTip"));
fixSuccess.setShowDelay(Duration.millis(100));
fixFail.setShowDelay(Duration.millis(100));
this.fixStateTip = new Tooltip();
fixStateTip.textProperty().bind(fixState.map(this::getFixStateDescription));
fixStateTip.setShowDelay(Duration.millis(100));
this.severityTip = new Tooltip();
severityTip.textProperty().bind(severity.map(this::getSeverityDescription));
severityTip.setShowDelay(Duration.millis(150));
}
public FontAwesome5Icon getFixGlyph(Result.FixState state) {
return switch (state) {
case FIXING -> FontAwesome5Icon.SPINNER;
case FIXED -> FontAwesome5Icon.CHECK;
case FIX_FAILED -> FontAwesome5Icon.TIMES;
default -> null;
};
}
private String getFixStateDescription(Result.FixState fixState) {
return switch (fixState) {
case FIXED -> resourceBundle.getString("health.fix.successTip");
case FIX_FAILED -> resourceBundle.getString("health.fix.failTip");
default -> "";
};
}
private String getSeverityDescription(DiagnosticResult.Severity severity) {
return resourceBundle.getString(switch (severity) {
case GOOD -> "health.result.severityTip.good";
case INFO -> "health.result.severityTip.info";
case WARN -> "health.result.severityTip.warn";
case CRITICAL -> "health.result.severityTip.crit";
});
}
@FXML
@@ -93,22 +122,19 @@ public class ResultListCellController implements FxController {
.onCondition(fixing) //
.afterStop(() -> fixView.setRotate(0)) //
.build();
fixState.addListener(((observable, oldValue, newValue) -> {
if (newValue == Result.FixState.FIXED || newValue == Result.FixState.FIX_FAILED) {
Tooltip.install(fixView, fixStateTip);
}
}));
Tooltip.install(severityView, severityTip);
}
@FXML
public void fix() {
Result r = result.get();
if (r != null) {
fixApplier.fix(r).whenCompleteAsync(this::fixFinished, Platform::runLater);
}
}
private void fixFinished(Void unused, Throwable exception) {
if (exception != null) {
LOG.error("Failed to apply fix", exception);
Tooltip.install(fixView, fixFail);
} else {
Tooltip.install(fixView, fixSuccess);
fixApplier.fix(r);
}
}
@@ -152,20 +178,12 @@ public class ResultListCellController implements FxController {
};
}
public ObjectBinding<FontAwesome5Icon> fixGlyphProperty() {
public ObservableValue<FontAwesome5Icon> fixGlyphProperty() {
return fixGlyph;
}
public FontAwesome5Icon getFixGlyph() {
if (fixState.getValue() == null) {
return null;
}
return switch (fixState.getValue()) {
case NOT_FIXABLE, FIXABLE -> null;
case FIXING -> FontAwesome5Icon.SPINNER;
case FIXED -> FontAwesome5Icon.CHECK;
case FIX_FAILED -> FontAwesome5Icon.TIMES;
};
return fixGlyph.getValue();
}
public BooleanBinding fixableProperty() {

View File

@@ -9,8 +9,10 @@ import javax.inject.Inject;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.LongBinding;
import javafx.beans.property.LongProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
@@ -21,6 +23,7 @@ import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Arrays;
import java.util.concurrent.Callable;
@VaultStatisticsScoped
public class VaultStatisticsController implements FxController {
@@ -32,6 +35,7 @@ public class VaultStatisticsController implements FxController {
private final VaultStats stats;
private final Series<Number, Number> readData;
private final Series<Number, Number> writeData;
private final Series<Number, Number> accessData;
private final Timeline ioAnimation;
private final LongBinding bpsRead;
private final LongBinding bpsWritten;
@@ -44,15 +48,20 @@ public class VaultStatisticsController implements FxController {
private final LongBinding totalBytesDecrypted;
private final LongBinding filesRead;
private final LongBinding filesWritten;
private final LongBinding filesAccessed;
private final LongBinding totalFilesAccessed;
private final LongBinding bpsEncrypted;
private final LongBinding bpsDecrypted;
public AreaChart<Number, Number> readChart;
public AreaChart<Number, Number> writeChart;
public AreaChart<Number, Number> accessChart;
public NumberAxis readChartXAxis;
public NumberAxis readChartYAxis;
public NumberAxis writeChartXAxis;
public NumberAxis writeChartYAxis;
public NumberAxis accessChartXAxis;
public NumberAxis accessChartYAxis;
@Inject
public VaultStatisticsController(VaultStatisticsComponent component, @VaultStatisticsWindow Stage window, @VaultStatisticsWindow Vault vault) {
@@ -60,6 +69,7 @@ public class VaultStatisticsController implements FxController {
this.stats = vault.getStats();
this.readData = new Series<>();
this.writeData = new Series<>();
this.accessData = new Series<>();
this.bpsRead = WeakBindings.bindLong(stats.bytesPerSecondReadProperty());
this.bpsWritten = WeakBindings.bindLong(stats.bytesPerSecondWrittenProperty());
this.cacheHitRate = WeakBindings.bindDouble(stats.cacheHitRateProperty());
@@ -71,11 +81,13 @@ public class VaultStatisticsController implements FxController {
this.totalBytesEncrypted = WeakBindings.bindLong(stats.totalBytesEncryptedProperty());
this.filesRead = WeakBindings.bindLong(stats.filesRead());
this.filesWritten = WeakBindings.bindLong(stats.filesWritten());
this.filesAccessed = WeakBindings.bindLong(stats.filesAccessed());
this.totalFilesAccessed = WeakBindings.bindLong(stats.totalFilesAccessed());
this.bpsEncrypted = WeakBindings.bindLong(stats.bytesPerSecondEncryptedProperty());
this.bpsDecrypted = WeakBindings.bindLong(stats.bytesPerSecondDecryptedProperty());
this.ioAnimation = new Timeline(); //TODO Research better timer
ioAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(IO_SAMPLING_INTERVAL), new IoSamplingAnimationHandler(readData, writeData)));
ioAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(IO_SAMPLING_INTERVAL), new IoSamplingAnimationHandler(readData, writeData, accessData)));
ioAnimation.setCycleCount(Animation.INDEFINITE);
ioAnimation.play();
@@ -89,6 +101,7 @@ public class VaultStatisticsController implements FxController {
public void initialize() {
readChart.getData().addAll(readData);
writeChart.getData().addAll(writeData);
accessChart.getData().addAll(accessData);
}
private class IoSamplingAnimationHandler implements EventHandler<ActionEvent> {
@@ -96,16 +109,21 @@ public class VaultStatisticsController implements FxController {
private long step = IO_SAMPLING_STEPS;
private final Series<Number, Number> decryptedBytesRead;
private final Series<Number, Number> encryptedBytesWrite;
private final Series<Number, Number> accessedFiles;
private final long[] maxBuf = new long[IO_SAMPLING_STEPS];
private final long[] maxAccessBuf = new long[IO_SAMPLING_STEPS];
public IoSamplingAnimationHandler(Series<Number, Number> readData, Series<Number, Number> writeData) {
public IoSamplingAnimationHandler(Series<Number, Number> readData, Series<Number, Number> writeData, Series<Number, Number> accessData) {
this.decryptedBytesRead = readData;
this.encryptedBytesWrite = writeData;
this.accessedFiles = accessData;
// initialize data once and change value of data points later:
for (int i = 0; i < IO_SAMPLING_STEPS; i++) {
decryptedBytesRead.getData().add(new Data<>(i, 0));
encryptedBytesWrite.getData().add(new Data<>(i, 0));
accessedFiles.getData().add(new Data<>(i, 0));
}
}
@@ -114,17 +132,22 @@ public class VaultStatisticsController implements FxController {
final long currentStep = step++;
final long decBytes = stats.bytesPerSecondReadProperty().get();
final long encBytes = stats.bytesPerSecondWrittenProperty().get();
final long accFiles = stats.filesAccessed().get();
maxBuf[(int) currentStep % IO_SAMPLING_STEPS] = Math.max(decBytes, encBytes);
long allTimeMax = Arrays.stream(maxBuf).max().orElse(0l);
long allTimeMax = Arrays.stream(maxBuf).max().orElse(0L);
maxAccessBuf[(int) currentStep % IO_SAMPLING_STEPS] = accFiles;
long allTimeMaxAccessedFiles = Arrays.stream(maxAccessBuf).max().orElse(0L);
// remove oldest value:
decryptedBytesRead.getData().remove(0);
encryptedBytesWrite.getData().remove(0);
accessedFiles.getData().remove(0);
// add latest value:
decryptedBytesRead.getData().add(new Data<>(currentStep, decBytes));
encryptedBytesWrite.getData().add(new Data<>(currentStep, encBytes));
accessedFiles.getData().add(new Data<>(currentStep, accFiles));
// adjust ranges:
readChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS * 1.0);
@@ -133,6 +156,9 @@ public class VaultStatisticsController implements FxController {
writeChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS * 1.0);
writeChartXAxis.setUpperBound(currentStep);
writeChartYAxis.setUpperBound(allTimeMax);
accessChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS * 1.0);
accessChartXAxis.setUpperBound(currentStep);
accessChartYAxis.setUpperBound(allTimeMaxAccessedFiles);
}
}
@@ -207,4 +233,12 @@ public class VaultStatisticsController implements FxController {
public LongBinding filesWrittenProperty() {return filesWritten;}
public long getFilesWritten() {return filesWritten.get();}
public LongBinding filesAccessedProperty() {return filesAccessed;}
public long getFilesAccessed() {return filesAccessed.get();}
public LongBinding totalFilesAccessedProperty() {return totalFilesAccessed;}
public long getTotalFilesAccessed() {return totalFilesAccessed.get();}
}

View File

@@ -1,5 +1,6 @@
package org.cryptomator.ui.traymenu;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.integrations.common.CheckAvailability;
import org.cryptomator.integrations.common.Priority;
@@ -19,6 +20,8 @@ import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
@CheckAvailability
@@ -28,6 +31,7 @@ public class AwtTrayMenuController implements TrayMenuController {
private static final Logger LOG = LoggerFactory.getLogger(AwtTrayMenuController.class);
private final PopupMenu menu = new PopupMenu();
private TrayIcon trayIcon;
@CheckAvailability
public static boolean isAvailable() {
@@ -37,7 +41,7 @@ public class AwtTrayMenuController implements TrayMenuController {
@Override
public void showTrayIcon(byte[] rawImageData, Runnable defaultAction, String tooltip) throws TrayMenuException {
var image = Toolkit.getDefaultToolkit().createImage(rawImageData);
var trayIcon = new TrayIcon(image, tooltip, menu);
trayIcon = new TrayIcon(image, tooltip, menu);
trayIcon.setImageAutoSize(true);
if (SystemUtils.IS_OS_WINDOWS) {
@@ -59,8 +63,14 @@ public class AwtTrayMenuController implements TrayMenuController {
}
@Override
public void onBeforeOpenMenu(Runnable runnable) {
public void onBeforeOpenMenu(Runnable listener) {
Preconditions.checkNotNull(this.trayIcon);
this.trayIcon.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
listener.run();
}
});
}
private void addChildren(Menu menu, List<TrayMenuItem> items) {

View File

@@ -3,6 +3,7 @@ package org.cryptomator.ui.traymenu;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.integrations.tray.ActionItem;
import org.cryptomator.integrations.tray.SeparatorItem;
import org.cryptomator.integrations.tray.SubMenuItem;
@@ -63,6 +64,11 @@ public class TrayMenuBuilder {
try (var image = getClass().getResourceAsStream(SystemUtils.IS_OS_MAC_OSX ? TRAY_ICON_MAC : TRAY_ICON)) {
trayMenu.showTrayIcon(image.readAllBytes(), this::showMainWindow, "Cryptomator");
trayMenu.onBeforeOpenMenu(() -> {
for (Vault vault : vaults) {
VaultListManager.redetermineVaultState(vault);
}
});
rebuildMenu();
initialized = true;
} catch (IOException e) {

View File

@@ -5,25 +5,59 @@
<?import javafx.scene.layout.VBox?>
<?import org.cryptomator.ui.health.CheckStateIconView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.MenuItem?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.health.CheckDetailController"
spacing="6">
<Label fx:id="detailHeader" styleClass="label-extra-large" text="${controller.checkName}" contentDisplay="LEFT">
<graphic>
<HBox alignment="CENTER" minWidth="25" maxWidth="25">
<CheckStateIconView fx:id="checkStateIconView" check="${controller.check}" glyphSize="20"/>
</HBox>
</graphic>
</Label>
spacing="12">
<HBox alignment="CENTER" >
<VBox spacing="12">
<Label fx:id="detailHeader" styleClass="label-extra-large" text="${controller.checkName}" contentDisplay="RIGHT">
<graphic>
<HBox alignment="CENTER" minWidth="25" maxWidth="25">
<CheckStateIconView check="${controller.check}" glyphSize="20"/>
</HBox>
</graphic>
</Label>
<Label text="%health.check.detail.checkRunning" visible="${controller.checkRunning}" managed="${controller.checkRunning}"/>
<Label text="%health.check.detail.checkScheduled" visible="${controller.checkScheduled}" managed="${controller.checkScheduled}"/>
<Label text="%health.check.detail.checkSkipped" visible="${controller.checkSkipped}" managed="${controller.checkSkipped}"/>
<Label text="%health.check.detail.checkCancelled" visible="${controller.checkCancelled}" managed="${controller.checkCancelled}"/>
<Label text="%health.check.detail.checkFailed" visible="${controller.checkFailed}" managed="${controller.checkFailed}"/>
<Label text="%health.check.detail.checkFinished" visible="${controller.checkSucceeded &amp;&amp; !controller.warnOrCritsExist}" managed="${controller.checkSucceeded &amp;&amp; !controller.warnOrCritsExist}"/>
<Label text="%health.check.detail.checkFinishedAndFound" visible="${controller.checkSucceeded &amp;&amp; controller.warnOrCritsExist}" managed="${controller.checkSucceeded &amp;&amp; controller.warnOrCritsExist}"/>
</VBox>
<Region HBox.hgrow="ALWAYS"/>
<Button text="%health.check.detail.fixAllSpecificBtn" contentDisplay="RIGHT" graphicTextGap="3" visible="${controller.checkFinished}" managed="${controller.checkFinished}" disable="${! controller.fixAllInfoResultsPossible}" onAction="#fixAllInfoResults">
<graphic>
<FontAwesome5IconView glyph="INFO_CIRCLE" glyphSize="12" styleClass="glyph-icon-muted"/>
</graphic>
</Button>
<Label text="%health.check.detail.checkRunning" visible="${controller.checkRunning}" managed="${controller.checkRunning}"/>
<Label text="%health.check.detail.checkScheduled" visible="${controller.checkScheduled}" managed="${controller.checkScheduled}"/>
<Label text="%health.check.detail.checkSkipped" visible="${controller.checkSkipped}" managed="${controller.checkSkipped}"/>
<Label text="%health.check.detail.checkCancelled" visible="${controller.checkCancelled}" managed="${controller.checkCancelled}"/>
<Label text="%health.check.detail.checkFailed" visible="${controller.checkFailed}" managed="${controller.checkFailed}"/>
<Label text="%health.check.detail.checkFinished" visible="${controller.checkSucceeded &amp;&amp; !controller.warnOrCritsExist}" managed="${controller.checkSucceeded &amp;&amp; !controller.warnOrCritsExist}"/>
<Label text="%health.check.detail.checkFinishedAndFound" visible="${controller.checkSucceeded &amp;&amp; controller.warnOrCritsExist}" managed="${controller.checkSucceeded &amp;&amp; controller.warnOrCritsExist}"/>
<ListView fx:id="resultsListView" VBox.vgrow="ALWAYS" visible="${!controller.checkSkipped}" fixedCellSize="25"/>
</HBox>
<VBox spacing="6" VBox.vgrow="ALWAYS">
<HBox alignment="CENTER_LEFT" spacing="6">
<Label fx:id="filterLbl" text="%health.check.detail.listFilters.label">
<graphic>
<FontAwesome5IconView glyph="FUNNEL" glyphSize="${filterLbl.height}" styleClass="glyph-icon-muted"/>
</graphic>
</Label>
<ChoiceBox fx:id="severityChoiceBox" />
<ChoiceBox fx:id="fixStateChoiceBox" />
</HBox>
<ListView fx:id="resultsListView" VBox.vgrow="ALWAYS" visible="${!controller.checkSkipped}" fixedCellSize="25">
<contextMenu>
<ContextMenu>
<items>
<MenuItem text="%generic.button.copy" onAction="#copyResultDetails"/>
</items>
</ContextMenu>
</contextMenu>
</ListView>
</VBox>
</VBox>

View File

@@ -12,7 +12,7 @@
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.health.CheckListController"
prefWidth="600"
prefWidth="650"
prefHeight="400"
spacing="12">
<padding>

View File

@@ -16,7 +16,7 @@
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.health.StartController"
prefWidth="600"
prefWidth="650"
prefHeight="400"
spacing="12">
<padding>

View File

@@ -13,6 +13,7 @@
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Arc?>
<?import javafx.scene.shape.Circle?>
<HBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.cryptomator.ui.stats.VaultStatisticsController"
@@ -22,18 +23,17 @@
</padding>
<!-- Caching -->
<VBox prefWidth="200" prefHeight="200">
<StackPane>
<Group>
<Arc styleClass="cache-arc-background" centerX="100" centerY="100" radiusX="100" radiusY="100" startAngle="225" length="-270"/>
<Arc styleClass="cache-arc-foreground" centerX="100" centerY="100" radiusX="100" radiusY="100" startAngle="225" length="${controller.cacheHitDegrees}"/>
</Group>
<VBox StackPane.alignment="CENTER" alignment="CENTER">
<FormattedLabel styleClass="label-extra-large" format="\%1.0f %%" arg1="${controller.cacheHitPercentage}"/>
<Label text="%stats.cacheHitRate"/>
</VBox>
</StackPane>
</VBox>
<StackPane prefWidth="200" prefHeight="200">
<Group>
<Circle centerX="100" centerY="100" radius="100" opacity="0"/> <!-- for alignment. visible=false does not yield the desired result -->
<Arc styleClass="cache-arc-background" centerX="100" centerY="100" radiusX="100" radiusY="100" startAngle="225" length="-270"/>
<Arc styleClass="cache-arc-foreground" centerX="100" centerY="100" radiusX="100" radiusY="100" startAngle="225" length="${controller.cacheHitDegrees}"/>
</Group>
<VBox StackPane.alignment="CENTER" alignment="CENTER">
<FormattedLabel styleClass="label-extra-large" format="\%1.0f %%" arg1="${controller.cacheHitPercentage}"/>
<Label text="%stats.cacheHitRate"/>
</VBox>
</StackPane>
<!-- Read -->
<VBox prefWidth="300" prefHeight="300" spacing="6" alignment="CENTER">
@@ -72,4 +72,22 @@
<DataLabel byteFormat="%stats.encr.total.data.none" kibFormat="%stats.encr.total.data.kib" mibFormat="%stats.encr.total.data.mib" gibFormat="%stats.encr.total.data.gib" dataInBytes="${controller.totalBytesEncrypted}"/>
<FormattedLabel format="%stats.write.accessCount" arg1="${controller.filesWritten}"/>
</VBox>
<!-- Access -->
<VBox prefWidth="300" prefHeight="300" spacing="6" alignment="CENTER">
<FormattedLabel styleClass="label-large" format="%stats.access.current" arg1="${controller.filesAccessed}"/>
<AreaChart fx:id="accessChart" styleClass="io-stats" createSymbols="false" animated="false">
<xAxis>
<NumberAxis fx:id="accessChartXAxis" styleClass="io-stats" autoRanging="false" forceZeroInRange="false" side="BOTTOM"/>
</xAxis>
<yAxis>
<NumberAxis fx:id="accessChartYAxis" styleClass="io-stats" autoRanging="true" forceZeroInRange="true" side="LEFT" tickUnit="Infinity"/>
</yAxis>
<cursor>
<Cursor fx:constant="DEFAULT"/>
</cursor>
</AreaChart>
<FormattedLabel format="%stats.access.total" arg1="${controller.totalFilesAccessed}"/>
<Label/> <!-- to align with other graphs -->
<Label/> <!-- to align with other graphs -->
</VBox>
</HBox>

View File

@@ -219,7 +219,27 @@ health.check.detail.checkFinished=The check finished successfully.
health.check.detail.checkFinishedAndFound=The check finished running. Please review the results.
health.check.detail.checkFailed=The check exited due to an error.
health.check.detail.checkCancelled=The check was cancelled.
health.check.detail.listFilters.label=Filter
health.check.detail.listFilters.severity=Severity
health.check.detail.listFilters.fixState=Fix state
health.check.detail.fixAllSpecificBtn=Fix all of type
health.check.exportBtn=Export Report
## Result view
health.result.severityFilter.all=Severity - All
health.result.severityFilter.good=Good
health.result.severityFilter.info=Info
health.result.severityFilter.warn=Warning
health.result.severityFilter.crit=Critical
health.result.severityTip.good=Severity: Good\nNormal vault structure.
health.result.severityTip.info=Severity: Info\nVault structure intact, fix suggested.
health.result.severityTip.warn=Severity: Warning\nVault structure corrupted, fix highly advised.
health.result.severityTip.crit=Severity: Critical\nVault structure corrupted, data loss determined.
health.result.fixStateFilter.all=Fix state - All
health.result.fixStateFilter.fixable=Fixable
health.result.fixStateFilter.notFixable=Not fixable
health.result.fixStateFilter.fixing=Fixing…
health.result.fixStateFilter.fixed=Fixed
health.result.fixStateFilter.fixFailed=Fix failed
## Fix Application
health.fix.fixBtn=Fix
health.fix.successTip=Fix successful
@@ -300,6 +320,11 @@ stats.encr.total.data.mib=Data encrypted: %.1f MiB
stats.encr.total.data.gib=Data encrypted: %.1f GiB
stats.write.accessCount=Total writes: %d
## Accesses
stats.access.current=Access: %d
stats.access.total=Total accesses: %d
# Main Window
main.closeBtn.tooltip=Close
main.minimizeBtn.tooltip=Minimize

View File

@@ -155,6 +155,7 @@ migration.impossible.moreInfo=لا يزال ممكناً فتح المخزن ب
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -200,6 +201,9 @@ stats.decr.total.data.none=تم فك تشفير البيانات:-
## Write
stats.encr.total.data.none=البيانات المشفرة: -
## Accesses
# Main Window
main.closeBtn.tooltip=إغلاق
main.minimizeBtn.tooltip=تصغير

View File

@@ -53,6 +53,12 @@ addvaultwizard.new.fileAlreadyExists=Файл або тэчка з такім і
addvaultwizard.new.locationDoesNotExist=Тэчка ва ўказанай сцежцы не існуе альбо ня можа адчыніцца
addvaultwizard.new.locationIsNotWritable=Для гэтай сцежцы бракуе правоў на запіс
addvaultwizard.new.locationIsOk=Пасуючае месца для тваёй скарбніцы
addvaultwizard.new.invalidName=Некарэктная назва скрабніцы
addvaultwizard.new.validName=Карэктная назва скрабніцы
addvaultwizard.new.validCharacters.message=Нчзва скарбніцы мусіць утрымліваць наступныя знакі:
addvaultwizard.new.validCharacters.chars=Слоўныя знакі, накшталт a, ж або
addvaultwizard.new.validCharacters.numbers=Лічбы
addvaultwizard.new.validCharacters.dashes=Злучок (%s) або падкрэслінік (%s)
### Password
addvaultwizard.new.createVaultBtn=Стварыць скарбніцу
addvaultwizard.new.generateRecoveryKeyChoice=Бяз гэтага пароля ты ня зможаш атрымаць доступ да сваіх даных. Ці хочаш ты мець ключ аднаўлення на выпадак, калі ты згубіш свой пароль?
@@ -76,26 +82,30 @@ addvault.new.readme.accessLocation.2=Гэта месца дуступу да т
addvault.new.readme.accessLocation.3=Любы дададзены сюды файл будзе зашыфраваны праз Cryptomator. Ты можаш працаваць тут як са звычайнаю тэчкаю альбо дыскам. Гэта толькі расшыфраваны агляд кантэнту, самі файлы заўжды захоўваюцца зашыфраванымі на тваім цвёрдым дыску.
addvault.new.readme.accessLocation.4=Ты можаш выдаліць гэты файл.
## Existing
addvaultwizard.existing.instruction=Абяры файл "vault.cryptomator" у існуючай скарбніцы. Калі існуе толькі файл "masterkey.cryptomator", абяры яго.
addvaultwizard.existing.chooseBtn=Абраць…
addvaultwizard.existing.filePickerTitle=Абяры файл скарбніцы
addvaultwizard.existing.filePickerMimeDesc=Скрабніца Cryptomator
## Success
addvaultwizard.success.nextStepsInstructions=Скарбніца "%s" дададзеная.\nТабе трэба разамкнуць гэтую скрабніцу, каб атрымаць доступ да зместу альбо дадаць яго. Таксама ты можаш разамкнуць яе ў любы іншы час.
addvaultwizard.success.unlockNow=Разамкнуць зараз
# Remove Vault
removeVault.title=Выдаліць "%s"
removeVault.message=Ці выдаліць скарбніцу?
removeVault.description=Гэта прывядзе толькі да таго, што Cryptomator забудзецца на гэтую скрынку. Ты зможаш дадаць яе пазней ізноў. Аніякія зашыфраваныя файлы з тайго жорсткага дыску не выдаляцьмуцца.
removeVault.confirmBtn=Выдаліць скарбніцу
# Change Password
changepassword.title=Змяніць пароль
changepassword.enterOldPassword=Увядзі бягучы пароль ад "%s"
changepassword.finalConfirmation=Я разумею, што калі забудуся на пароль, то згублю доступ да сваех даных
changepassword.finalConfirmation=Я разумею, што калі забудуся на пароль, то згублю доступ да маіх даных
# Forget Password
forgetPassword.title=Забыўся на пароль
forgetPassword.message=Забыўся на захаваны пароль?
forgetPassword.confirmBtn=Забыўся на пароль
forgetPassword.title=Забыцца на пароль
forgetPassword.message=Ці забыцца на захаваны пароль?
forgetPassword.description=Гэта выдаліць захаваны пароль ад гэтай скарбніцы з твайго мэнэджара ключоў.
forgetPassword.confirmBtn=Забыцца на пароль
# Unlock
unlock.title=Разамкнуць "%s"
@@ -104,45 +114,72 @@ unlock.savePassword=Захоўваць пароль
unlock.unlockBtn=Адамкнуць
## Select
unlock.chooseMasterkey.message=Файл майстра ключоў ня знойдзены
unlock.chooseMasterkey.description=Cryptomator ня змог знайсці файл masterkey у тваёй скарбніцы "%s". Калі ласка, абяры самастойна файл з ключом.
unlock.chooseMasterkey.filePickerTitle=Абяры файл masterkey
unlock.chooseMasterkey.filePickerMimeDesc=Майстар ключоў Cryptomator
## Success
unlock.success.message=Паспяховае размыканне
unlock.success.description=Змест скарбніцы "%s" цяпер дасяжны праз ёйны мантажны пункт.
unlock.success.rememberChoice=Запомні мой выбар і больш не пытай
unlock.success.revealBtn=Паказаць дыск
## Failure
unlock.error.message=Немагчыма разамкнуць скарбніцу
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Пункт мантажу "%s" не з'яўляецца пустой тэчкаю альбо ўвогуле не існуе.
unlock.error.invalidMountPoint.existing=Пункт мантажу "%s" ужо існуе альбо адсутнічае бацькоўская тэчка.
unlock.error.invalidMountPoint.driveLetterOccupied=Дыскавая літара "%s" ужо выкарыстоўваецца.
## Hub
### Waiting
hub.auth.message=Чаканне спраўджання…
hub.auth.description=Ты мусіш аўтаматычна перанакіравацца на старонку ўваходу.
hub.auth.loginLink=Не перанакіраваўся? Пстрыкні сюды, каб адчыніць старонку.
### Receive Key
hub.receive.message=Апрацоўка адказу…
hub.receive.description=Cryptomator атрымлівае ды апрацоўвае адказ ад Hub. Калі ласка, пачакай.
### Register Device
hub.register.message=Патрабуецца назва прылады
hub.register.description=Здаецца, што ты ў першы раз увайшла/-оў у Hub з гэтай прылады. Каб ідэнтыфікаваць яе для спраўджання доступу, табе трэба назваць гэтую прыладу.
hub.register.nameLabel=Назва прылады
hub.register.occupiedMsg=Назва ўжо ўжытая
hub.register.registerBtn=Пацвердзіць
### Registration Success
hub.registerSuccess.message=Прылада атрымала назву
hub.registerSuccess.description=Каб атрымаць доступ да скарбніцы, твая прылада мусіць быць спраўджанай уладальнікам скарбніцы.
### Registration Failed
hub.registerFailed.message=Памылка пры называнні прылады
hub.registerFailed.description=Падчас прысваення імя адбылася памылка. Па дэтальную інфармацыю звярніся да пратаколу праграмы.
### Unauthorized
hub.unauthorized.message=Адмова ў доступе
hub.unauthorized.description=Тваёй прыладзе ў дадзены момант не дазволена мець доступ да гэтай скрабніцы. Запытайся ўладальніка скрабніцы за дазволам.
### License Exceeded
hub.licenseExceeded.message=Ліцэнзія пратэрмінаваная
# Lock
## Force
lock.forced.message=Замыканне не атрымалася
lock.forced.description=Замыканне скарбніцы "%s" было заблакаванае праз аперацыі ў апрацоўцы альбо праз адчыненыя файлы. Ты можаш прымусова замкнуць скарбніцу, але гэта можа прывесці да страты незахаваных даных.
lock.forced.retryBtn=Паспрабаваць ізноў
lock.forced.forceBtn=Замкнуць прымусова
## Failure
lock.fail.message=Не атрымалася замкнуць скарбніцу
lock.fail.description=Скарбніцу "%s" не мажліва замкнуць. Упэўніся, што ты захаваў сваю незахаваную працу дзесьці ў іншым месцы, і што скночыліся ўсе важныя працэсы чытання-пісання. Для замыкання скарбніцы забі працэс Cryptomator.
# Migration
migration.title=Абнавіць скарбніцу
## Start
migration.start.prompt=Тваю скарбніцу "%s" неабходна абнавіць у новы фармат. Перад тым, як пачаць, запэўніся, што не адбываецца працэсу сінхранавання, які можа ўздзейнічаць на скарбніцу.
migration.start.confirm=Так, мая скарбніца цалкам сінхранавана
## Run
migration.run.enterPassword=Увядзіце пароль да "%s"
migration.run.startMigrationBtn=Перамясціць скарбніцу
migration.run.progressHint=Гэта можа заняць пэўны час…
## Success
migration.success.nextStepsInstructions=Міграцыя скарбніцы "%s" прайшла паспяхова.\nЦяпер ты можаш разамкнуць сваю скарбніцу.
migration.success.unlockNow=Разамкнуць зараз
## Missing file system capabilities
migration.error.missingFileSystemCapabilities.title=Файлавая сістэма не падтрымліваецца
migration.error.missingFileSystemCapabilities.description=Міграцыя не пачалася, бо твая скарбніца знаходзіцца ў непрыдатнай файлавай сістэме.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Файлавая сістэма не падтрымлівае доўгія назвы файлаў.
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Файлавая сістэма не падтрымлівае доўгія сцежкі.
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Файлавая сістэма не дазваляе чытаць у ёй.
@@ -152,6 +189,8 @@ migration.impossible.heading=Не мажліва перанесці скарбн
# Health Check
## Start
health.title=Тэст на цэласнасць для "%s"
health.intro.header=Тэст на цэласнасць
health.intro.remarkBackup=Калі даныя пашкоджаныя, дапаможа толькі рэзервовая копія.
## Start Failure
health.fail.moreInfo=Падрабязней
@@ -160,18 +199,43 @@ health.checkList.selectAllButton=Вылучыць усе элементы
health.checkList.deselectAllButton=Адрабіць вылучэнне ўсіх элементаў
health.check.runBatchBtn=Выканаць вылучаныя элементы
## Detail view
health.check.detail.checkCancelled=Праверка была скасавана.
health.check.exportBtn=Экспартаваць справаздачу
## Result view
## Fix Application
health.fix.fixBtn=Выправіць
health.fix.successTip=Паспяхова выпраўлена
health.fix.failTip=Няўдалае выпраўленне, глядзі пратакол з дэталямі
# Preferences
preferences.title=Налады
## General
preferences.general=Агульныя
preferences.general.startHidden=Хаваць акно пры запуску Cryptomator
preferences.general.autoCloseVaults=Замыкаць адчыненыя скарбніцы аўтаматычна пры выхадзе з праграмы
preferences.general.debugLogging=Уключыць пратакаляванне адладкі
preferences.general.debugDirectory=Паказаць файлы пратаколу
preferences.general.autoStart=Запускаць Cryptomator падчас запуску сістэмы
preferences.general.keychainBackend=Захоўваць паролі праз
## Interface
preferences.interface=Інтэрфэйс
preferences.interface.theme=Iнтэрфэйс
preferences.interface.theme.automatic=Аўтаматычна
preferences.interface.theme.dark=Цёмная
preferences.interface.theme.light=Светлая
preferences.interface.unlockThemes=Разблакаваць цёмную тэму
preferences.interface.language=Мова (спатрэбуецца перазапуск)
preferences.interface.language.auto=Сістэма па змаўчанні
preferences.interface.interfaceOrientation=Арыентацыя інтэрфэйсу
preferences.interface.interfaceOrientation.ltr=Злева ўправа
preferences.interface.interfaceOrientation.rtl=Справа ўлева
preferences.interface.showMinimizeButton=Паказаць кнопку згортвання
preferences.interface.showTrayIcon=Паказваць іконку на інфармацыйнай панэлі (спатрэбіцца перазапуск)
## Volume
preferences.volume=Віртуальны дыск
preferences.volume.type=Тып тому
preferences.volume.webdav.port=Порт WebDAV
preferences.volume.webdav.scheme=Схема WebDAV
## Updates
preferences.updates=Абнаўленні
preferences.updates.currentVersion=Бягучая версія: %s
@@ -180,20 +244,55 @@ preferences.updates.checkNowBtn=Праверыць зараз
preferences.updates.updateAvailable=Даступна абнаўленне да версіі %s
## Contribution
preferences.contribute=Падтрымай нас
preferences.contribute.getCertificate=Яшчэ ня маеш такога? Даведайся, як атрымаць.
#<-- Add entries for donations and code/translation/documentation contribution -->
## About
preferences.about=Пра нас
# Vault Statistics
stats.title=Статыстыкі для %s
## Read
stats.read.throughput.idle=Чытанне: -
stats.read.throughput.kibs=Чытанне: %.2f КіБ/с
stats.read.throughput.mibs=Чытанне: %.2f МіБ/с
stats.read.total.data.none=Прачытана: -
stats.read.total.data.kib=Прачытана: %.1f КіБ
stats.read.total.data.mib=Прачытана: %.1f МіБ
stats.read.total.data.gib=Прачытана: %.1f ҐіБ
stats.decr.total.data.none=Расшыфравана: -
stats.decr.total.data.kib=Расшыфравана: %.1f КіБ
stats.decr.total.data.mib=Расшыфравана: %.1f МіБ
stats.decr.total.data.gib=Расшыфравана: %.1f ҐіБ
stats.read.accessCount=Агульная колькасць чытанняў: %d
## Write
stats.write.throughput.idle=Чытанне: -
stats.write.throughput.kibs=Пісанне: %.2f КіБ/с
stats.write.throughput.mibs=Пісанне: %.2f МіБ/с
stats.write.total.data.none=Напісана: -
stats.write.total.data.kib=Запісана: %.1f КіБ
stats.write.total.data.mib=Запісана: %.1f МіБ
stats.write.total.data.gib=Запісана: %.1f ҐіБ
stats.encr.total.data.none=Зашыфравана: -
stats.encr.total.data.kib=Зашыфравана: %.1f КіБ
stats.encr.total.data.mib=Зашыфравана: %.1f МіБ
stats.encr.total.data.gib=Зашыфравана: %.1f ҐіБ
stats.write.accessCount=Агульная колькасць запісаў: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Зачыніць
main.minimizeBtn.tooltip=Згарнуць
main.preferencesBtn.tooltip=Налады
main.debugModeEnabled.tooltip=Функццыя дыягназавання выключана
main.supporterCertificateMissing.tooltip=Калі ласка, падумай пра ахвяраванне
## Drag 'n' Drop
main.dropZone.dropVault=Дадаць гэтую скарбніцу
main.dropZone.unknownDragboardContent=Калі ты хочаш дадаць скарбніцу, перацягні яе ў гэтае акно
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Пстрыкні тут, каб дадаць скарбніцу
main.vaultlist.contextMenu.remove=Выдаліць…
main.vaultlist.contextMenu.lock=Замкнуць
main.vaultlist.contextMenu.unlock=Адамкнуць…
@@ -207,37 +306,98 @@ main.vaultlist.addVaultBtn=Дадаць скарбніцу
main.vaultDetail.lockedStatus=ЗАМКНЁНА
main.vaultDetail.unlockBtn=Адамкнуць…
main.vaultDetail.unlockNowBtn=Разамкнуць зараз
main.vaultDetail.optionsBtn=Параметры скарбніцы
main.vaultDetail.passwordSavedInKeychain=Пароль захаваны
### Unlocked
main.vaultDetail.unlockedStatus=РАЗАМКНЁНА
main.vaultDetail.accessLocation=Змест тваёй скарбніцы даступны тут:
main.vaultDetail.revealBtn=Паказаць дыск
main.vaultDetail.lockBtn=Замкнуць
main.vaultDetail.bytesPerSecondRead=Чытанне:
main.vaultDetail.bytesPerSecondWritten=Пісанне:
main.vaultDetail.throughput.idle=бяздзейны
main.vaultDetail.throughput.kbps=%.1f КіБ/с
main.vaultDetail.throughput.mbps=%.1f МіБ/с
main.vaultDetail.stats=Статыстыка скарбніцы
### Missing
main.vaultDetail.missing.info=Cryptomator ня змог знайсці скарбніцу па гэтай сцежцы.
main.vaultDetail.missing.recheck=Пераправерыць
main.vaultDetail.missing.remove=Выдаліць са спісу скарбніц…
main.vaultDetail.missing.changeLocation=Змяніць месцазнаходжанне скарбніцы…
### Needs Migration
main.vaultDetail.migrateButton=Абнавіць скарбніцу
main.vaultDetail.migratePrompt=Тваю скарбніцу трэба сканвертаваць у новы фармат, перад тым як ты зможаш атрымаць доступ да яе
### Error
main.vaultDetail.error.info=Адбылася памылка пры загрузцы скарбніцы з дыску.
main.vaultDetail.error.reload=Перазагрузіць
main.vaultDetail.error.windowTitle=Памылка загрузкі скарбніцы
# Wrong File Alert
wrongFileAlert.title=Як зашыфраваць файлы
wrongFileAlert.message=Ці спрабаваў ты зашыфраваць гэтыя файлы?
wrongFileAlert.instruction.0=Каб зашыфраваць файлы, зрабі наступныя крокі:
wrongFileAlert.instruction.1=1. Разамкні свая скарбніцу.
wrongFileAlert.instruction.2=2. Пстрыкні па "Паказаць"; каб адчыніць том у тваім файлавым мэнэджары.
wrongFileAlert.instruction.3=3. Дадай свае файлы ў гэты том.
wrongFileAlert.link=Каб атрымаць больш інфармацыі, наведай
# Vault Options
## General
vaultOptions.general=Агульныя
vaultOptions.general.vaultName=Назва скарбніцы
vaultOptions.general.autoLock.lockAfterTimePart1=Замыкаць пры бяздзейнасці праз
vaultOptions.general.autoLock.lockAfterTimePart2=хвілін(ы)
vaultOptions.general.unlockAfterStartup=Размыкаць скрабніцу пры запуску Cryptomator
vaultOptions.general.actionAfterUnlock=Пасля паспяховага размыкання
vaultOptions.general.actionAfterUnlock.ignore=Нічога не рабі
vaultOptions.general.actionAfterUnlock.reveal=Паказаць дыск
vaultOptions.general.actionAfterUnlock.ask=Запытацца
## Mount
vaultOptions.mount=Мантажаванне
vaultOptions.mount.readonly=Толькі для чытання
vaultOptions.mount.customMountFlags=Карыстальніцкія опцыі мантажавання
vaultOptions.mount.winDriveLetterOccupied=занята
vaultOptions.mount.mountPoint=Пункт мантажавання
vaultOptions.mount.mountPoint.auto=Аўтаматычны выбар пасуючага месцазнаходжання
vaultOptions.mount.mountPoint.driveLetter=Назначыць літару для дыску
vaultOptions.mount.mountPoint.custom=Уласная сцежка
vaultOptions.mount.mountPoint.directoryPickerButton=Абраць…
vaultOptions.mount.mountPoint.directoryPickerTitle=Абяры парожнюю тэчку
## Master Key
vaultOptions.masterkey=Пароль
vaultOptions.masterkey.changePasswordBtn=Змяніць пароль
vaultOptions.masterkey.forgetSavedPasswordBtn=Забыцца на захаваны пароль
vaultOptions.masterkey.showRecoveryKeyBtn=Паказаць ключ аднаўлення
vaultOptions.masterkey.recoverPasswordBtn=Скінуць пароль
# Recovery Key
## Display Recovery Key
recoveryKey.display.title=Паказаць пароль аднаўлення
recoveryKey.create.message=Патрабуецца пароль
recoveryKey.create.description=Увядзі пароль ад "%s" каб паказаць ягоны код аднаўлення.
recoveryKey.display.description=Наступны код аднаўлення можа быць выкарастаны для аднаўлення доступу да "%s":
recoveryKey.display.StorageHints=Захоўвай іх у бяспечным месцы, напрыклад:\n • Выкарыстоўваючы мэнэджар пароляў\n • На USB-флэшцы\n • Раздрукаванымі на паперы
## Reset Password
### Enter Recovery Key
recoveryKey.recover.title=Скінуць пароль
recoveryKey.recover.prompt=Увядзі свой ключ аднаўлення для "%s":
recoveryKey.recover.validKey=Гэта валідны ключ аднаўлення
recoveryKey.printout.heading=Ключ аднаўлення Cryptomator\n"%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Скінуць
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Пароль паспяхова скінуты
recoveryKey.recover.resetSuccess.description=Ты можаш разамкнуць сваю скарбніцу з дапамогаю новага паролю.
# New Password
newPassword.promptText=Увядзі новы пароль
newPassword.reenterPassword=Пацвердзі новы пароль
newPassword.passwordsMatch=Паролі супадаюць!
newPassword.passwordsDoNotMatch=Паролі не супадаюць
passwordStrength.messageLabel.tooShort=Выкарыстай ня менш за %d сімвалаў
passwordStrength.messageLabel.0=Вельмі слабы
passwordStrength.messageLabel.1=Слабы
passwordStrength.messageLabel.2=Нармалёвы
passwordStrength.messageLabel.3=Моцны
@@ -246,5 +406,9 @@ passwordStrength.messageLabel.4=Вельмі моцны
# Quit
quit.title=Пакінуць праграму
quit.message=Існуюць разамкнёныя скарбніцы
quit.description=Калі ласка, пацвердзі, што ты збіраешся выйсці. Cryptomator замкне ўсе разомкнутыя скарбніцы, каб прадухіліць страту даных.
quit.lockAndQuitBtn=Замкнуць ды вайсці
# Forced Quit
# Forced Quit
quit.forced.message=Некаторыя скарбніцы не магчыма замкнуць
quit.forced.forceAndQuitBtn=Прымусіць і выйсці

View File

@@ -108,6 +108,7 @@ lock.forced.retryBtn=পুনরায় চেষ্টা করুন
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -124,6 +125,9 @@ lock.forced.retryBtn=পুনরায় চেষ্টা করুন
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=বন্ধ করুন
## Drag 'n' Drop

View File

@@ -154,6 +154,7 @@ migration.impossible.moreInfo=Sef se i dalje može otvoriti sa starijom verzijom
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -215,6 +216,9 @@ stats.encr.total.data.mib=Otključano podataka: %.1f MiB
stats.encr.total.data.gib=Otključano podataka: %.1f GiB
stats.write.accessCount=Ukupno upisano: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Zatvori
main.minimizeBtn.tooltip=Minimiziraj

View File

@@ -210,7 +210,23 @@ health.check.detail.checkFinished=La prova ha finalitzat amb èxit.
health.check.detail.checkFinishedAndFound=La comprovació ha finalitzat. Si us plau, comproveu-ne es resultat.
health.check.detail.checkFailed=La comprovació ha acabat a causa d'un error.
health.check.detail.checkCancelled=S'ha cancel·lat la prova.
health.check.detail.listFilters.label=Filtre
health.check.detail.listFilters.severity=Gravetat
health.check.detail.listFilters.fixState=Estat de la reparació
health.check.detail.fixAllSpecificBtn=Qualsevol reparació
health.check.exportBtn=Exporta informe
## Result view
health.result.severityFilter.all=Gravetat - Totes
health.result.severityFilter.good=
health.result.severityFilter.info=Info
health.result.severityFilter.warn=Avís
health.result.severityFilter.crit=Crític
health.result.fixStateFilter.all=Estat de reparació - Tot
health.result.fixStateFilter.fixable=Es pot arreglar
health.result.fixStateFilter.notFixable=No es pot arreglar
health.result.fixStateFilter.fixing=Reparant…
health.result.fixStateFilter.fixed=Solucionat
health.result.fixStateFilter.fixFailed=Reparació fallida
## Fix Application
health.fix.fixBtn=Corregeix
health.fix.successTip=S'ha corregit amb èxit
@@ -292,6 +308,11 @@ stats.encr.total.data.mib=Dades xifrades: %.1f MiB
stats.encr.total.data.gib=Dades xifrades: %.1f GiB
stats.write.accessCount=Total escrits: %d
## Accesses
stats.access.current=Accés: %d
stats.access.total=Total d'accessos: %d
# Main Window
main.closeBtn.tooltip=Tanca
main.minimizeBtn.tooltip=Minimitza

View File

@@ -192,6 +192,7 @@ health.check.detail.checkFinishedAndFound=Kontrola byla dokončena. Zkontrolujte
health.check.detail.checkFailed=Kontrola byla ukončena z důvodu chyby.
health.check.detail.checkCancelled=Kontrola byla zrušena.
health.check.exportBtn=Exportovat sestavu
## Result view
## Fix Application
health.fix.fixBtn=Opravit
health.fix.successTip=Oprava byla úspěšná
@@ -272,6 +273,9 @@ stats.encr.total.data.mib=Zašifrováno: %.1f MiB
stats.encr.total.data.gib=Zašifrováno: %.1f GiB
stats.write.accessCount=Celkem zapsáno: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Zavřít
main.minimizeBtn.tooltip=Minimalizovat

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=Kontrolproceduren er kørt færdig. Ge
health.check.detail.checkFailed=Kontrolproceduren blev afbrudt af en fejl.
health.check.detail.checkCancelled=Kontrolproceduren blev annulleret.
health.check.exportBtn=Eksportér rapport
## Result view
## Fix Application
health.fix.fixBtn=Reparér
health.fix.successTip=Repareret
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=Data krypteret: %.1f MiB
stats.encr.total.data.gib=Data krypteret: %.1f GiB
stats.write.accessCount=Totalt antal skrivninger: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Luk
main.minimizeBtn.tooltip=Minimér

View File

@@ -218,7 +218,26 @@ health.check.detail.checkFinished=Die Prüfung wurde erfolgreich abgeschlossen.
health.check.detail.checkFinishedAndFound=Die Prüfung wurde abgeschlossen. Bitte überprüfe die Ergebnisse.
health.check.detail.checkFailed=Die Prüfung wurde wegen eines Fehlers abgebrochen.
health.check.detail.checkCancelled=Die Prüfung wurde abgebrochen.
health.check.detail.listFilters.label=Filter
health.check.detail.listFilters.severity=Schweregrad
health.check.detail.listFilters.fixState=Fix Status
health.check.detail.fixAllSpecificBtn=Behebe alle mit Status
health.check.exportBtn=Bericht exportieren
## Result view
health.result.severityFilter.all=Schweregrad - Alle
health.result.severityFilter.good=Gut
health.result.severityFilter.info=Info
health.result.severityFilter.warn=Warnung
health.result.severityFilter.crit=Kritisch
health.result.severityTip.good=Schweregrad: Gut\nNormale Tresorstruktur.
health.result.severityTip.info=Schweregrad: Info\nTresorstruktur intakt, Beheben empfohlen.
health.result.severityTip.warn=Schweregrad: Warnung\nTresorstruktur beschädigt, Beheben dringend empfohlen.
health.result.severityTip.crit=Schweregrad: Kritisch\nTresorstruktur beschädigt, Datenverlust möglich.
health.result.fixStateFilter.all=Fix-Status - Alle
health.result.fixStateFilter.fixable=Fixierbar
health.result.fixStateFilter.notFixable=Nicht behebbar
health.result.fixStateFilter.fixed=Behoben
health.result.fixStateFilter.fixFailed=Fix fehlgeschlagen
## Fix Application
health.fix.fixBtn=Beheben
health.fix.successTip=Fehlerbehebung erfolgreich
@@ -300,6 +319,11 @@ stats.encr.total.data.mib=Verschlüsselt: %.1f MiB
stats.encr.total.data.gib=Verschlüsselt: %.1f GiB
stats.write.accessCount=Schreibzugriffe: %d
## Accesses
stats.access.current=Zugriffe: %d
stats.access.total=Gesamte Zugriffe: %d
# Main Window
main.closeBtn.tooltip=Schließen
main.minimizeBtn.tooltip=Minimieren

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=Ο έλεγχος σταμάτησε
health.check.detail.checkFailed=Ο έλεγχος τερματίστηκε λόγω σφάλματος.
health.check.detail.checkCancelled=Ο έλεγχος ακυρώθηκε.
health.check.exportBtn=Εξαγωγή Αναφοράς
## Result view
## Fix Application
health.fix.fixBtn=Επιδιόρθωση
health.fix.successTip=Επιτυχής επιδιόρθωση
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=Δεδομένα που κρυπτογραφήθηκα
stats.encr.total.data.gib=Δεδομένα που κρυπτογραφήθηκαν: %.1f GiB
stats.write.accessCount=Συνολικές εγγραφές: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Κλείσιμο
main.minimizeBtn.tooltip=Ελαχιστοποίηση

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=La comprobación terminó de ejecutars
health.check.detail.checkFailed=La comprobación terminó debido a un error.
health.check.detail.checkCancelled=La comprobación se canceló.
health.check.exportBtn=Exportar informe
## Result view
## Fix Application
health.fix.fixBtn=Reparar
health.fix.successTip=Reparación exitosa
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=Datos cifrados: %.1f MiB
stats.encr.total.data.gib=Datos cifrados: %.1f GiB
stats.write.accessCount=Total de escrituras: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Cerrar
main.minimizeBtn.tooltip=Minimizar

View File

@@ -69,6 +69,7 @@ unlock.unlockBtn=بازکردن قفل
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -85,6 +86,9 @@ unlock.unlockBtn=بازکردن قفل
## Read
## Write
## Accesses
# Main Window
main.supporterCertificateMissing.tooltip=لطفا کمک مالی در نظر بگیرند
## Drag 'n' Drop

View File

@@ -94,6 +94,7 @@ lock.forced.retryBtn=Subukan muli
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -113,6 +114,9 @@ preferences.interface.theme.light=Light
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=Isara
main.preferencesBtn.tooltip=Mga Kagustuhan

View File

@@ -218,7 +218,27 @@ health.check.detail.checkFinished=La vérification s'est terminée avec succès.
health.check.detail.checkFinishedAndFound=Le test est terminé. Veuillez vérifier les résultats.
health.check.detail.checkFailed=La vérification s'est arrêtée en raison d'une erreur.
health.check.detail.checkCancelled=Vérification annulée.
health.check.detail.listFilters.label=Filtre
health.check.detail.listFilters.severity=Gravité
health.check.detail.listFilters.fixState=Corriger l'état
health.check.detail.fixAllSpecificBtn=Corriger tout le type
health.check.exportBtn=Exporter le rapport
## Result view
health.result.severityFilter.all=Gravité - Tous
health.result.severityFilter.good=Satisfaisant
health.result.severityFilter.info=Information
health.result.severityFilter.warn=Avertissement
health.result.severityFilter.crit=Critique
health.result.severityTip.good=Gravité : structure de coffre normale.
health.result.severityTip.info=Gravité : Info\nStructure du coffre intacte, correction suggérée.
health.result.severityTip.warn=Gravité : Avertissement\nLa structure du coffre est corrompue, correction fortement recommandée.
health.result.severityTip.crit=Gravité : Structure du coffre critique,\ncorruption et perte de données.
health.result.fixStateFilter.all=Réparer l'état - Tous
health.result.fixStateFilter.fixable=Réparable
health.result.fixStateFilter.notFixable=Pas réparable
health.result.fixStateFilter.fixing=Réparation en cours…
health.result.fixStateFilter.fixed=Réparé
health.result.fixStateFilter.fixFailed=Réparation a échouée
## Fix Application
health.fix.fixBtn=Réparer
health.fix.successTip=Réparation réussie
@@ -300,6 +320,11 @@ stats.encr.total.data.mib=Données chiffrées: %.1f MiO
stats.encr.total.data.gib=Données chiffrées: %.1f GiO
stats.write.accessCount=Total des écritures: %d
## Accesses
stats.access.current=Accès : %d
stats.access.total=Total des accès: %d
# Main Window
main.closeBtn.tooltip=Fermer
main.minimizeBtn.tooltip=Réduire

View File

@@ -69,6 +69,7 @@ lock.forced.retryBtn=Tentar de novo
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -85,6 +86,9 @@ lock.forced.retryBtn=Tentar de novo
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=Pechar
## Drag 'n' Drop

View File

@@ -176,6 +176,7 @@ health.check.detail.checkFinishedAndFound=הבדיקה הסתיימה. ניתן
health.check.detail.checkFailed=הבדיקה נכשלה בשל שגיאה.
health.check.detail.checkCancelled=הבדיקה בוטלה.
health.check.exportBtn=יצוא דוחות
## Result view
## Fix Application
health.fix.fixBtn=תיקון
@@ -227,6 +228,9 @@ stats.read.throughput.mibs=קריאה: %.2f MiB/s
stats.read.total.data.none=מידע שנקרא: -
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=סגור
main.preferencesBtn.tooltip=העדפות

View File

@@ -50,6 +50,13 @@ addvaultwizard.new.directoryPickerLabel=अपने पसंद की जग
addvaultwizard.new.directoryPickerButton=चुनें…
addvaultwizard.new.directoryPickerTitle=निर्देशिका चुनें
addvaultwizard.new.fileAlreadyExists=वॉल्ट के वही नाम से एक फाइल या फोल्डर पहले से मौजूद है
addvaultwizard.new.locationDoesNotExist=आपके द्वारा चयनित फ़ोल्डर मौजूद नहीं है
addvaultwizard.new.locationIsNotWritable=इस फ़ोल्डर में डेटा नहीं जोड़ा जा सकता
addvaultwizard.new.locationIsOk=आपकी सुरक्षित तिजोरी के लिए उपयुक्त स्थान
addvaultwizard.new.invalidName=तिजोरी के लिए इस नाम का उपयोग नहीं किया जा सकता
addvaultwizard.new.validName=आप इस नाम का उपयोग कर सकते हैं
addvaultwizard.new.validCharacters.message=नाम के लिए केवल इन वर्णों का उपयोग कर सकते हैं
addvaultwizard.new.validCharacters.numbers=नंबर
### Password
addvaultwizard.new.createVaultBtn=वॉल्ट बनाएं
addvaultwizard.new.generateRecoveryKeyChoice=आप अपने पासवर्ड के बिना अपने डेटा तक नहीं पहुंच पाएंगे। क्या आप उस वक़्त के लिए एक पुनर्प्राप्ति कुंजी चाहते हैं जब आप अपना पासवर्ड खो देते हैं?
@@ -86,8 +93,12 @@ removeVault.confirmBtn=वॉल्ट हटाए
# Change Password
changepassword.title=पासवर्ड बदलें
changepassword.enterOldPassword="%s" का वर्तमान पासवर्ड दर्ज करें
changepassword.finalConfirmation=मैं समझता/समझती हूं कि अगर मैं अपना पासवर्ड भूल जाता हूं, तो मैं अपना डेटा एक्सेस नहीं कर पाऊंगा
# Forget Password
forgetPassword.title=पासवर्ड भूल गए
forgetPassword.message=पासवर्ड भूल गए?
forgetPassword.confirmBtn=पासवर्ड भूल गए
# Unlock
unlock.title=अनलॉक "%s"
@@ -99,13 +110,17 @@ unlock.chooseMasterkey.description=क्रिप्टोमेटर "%s" क
## Success
unlock.success.message=अनलॉक सफल हुआ
unlock.success.rememberChoice=विकल्प याद रखें, दोबारा ना दिखाएं
unlock.success.revealBtn=फोल्डर खोलें
## Failure
unlock.error.message=वॉल्ट को अनलॉक करना असफल हुआ
### Invalid Mount Point
## Hub
### Waiting
### Receive Key
hub.receive.message=अभी संसाधित किया जा रहा है
### Register Device
hub.register.nameLabel=डिवाइस का नाम
hub.register.occupiedMsg=नाम पहले से प्रयोग में है
hub.register.registerBtn=पुष्टि करें
### Registration Success
### Registration Failed
@@ -118,6 +133,7 @@ hub.register.registerBtn=पुष्टि करें
lock.forced.message=लॉक करना विफल हुआ
lock.forced.retryBtn=पुन: प्रयास करें
## Failure
lock.fail.message=लॉक करना विफल
# Migration
migration.title=वाउल्ट को अपग्रेड करें
@@ -135,6 +151,7 @@ migration.success.unlockNow=अब अनलॉक करें
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -145,6 +162,8 @@ preferences.general.autoCloseVaults=एप्लीकेशन बंद कर
preferences.general.autoStart=क्रिप्टोमेटर को सिस्टम स्टार्ट पे खोले
## Interface
preferences.interface.theme.automatic=ऑटोमैटिक
preferences.interface.theme.dark=डार्क
preferences.interface.theme.light=लाइट
preferences.interface.interfaceOrientation.ltr=बाएं से दाएं
preferences.interface.interfaceOrientation.rtl=दाएं से बाएं
## Volume
@@ -159,6 +178,9 @@ preferences.contribute=हमें सपोर्ट करें
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=बंद करें
main.preferencesBtn.tooltip=प्राथमिकताएं
@@ -171,6 +193,7 @@ main.vaultlist.contextMenu.lock=लॉक करें
main.vaultlist.contextMenu.unlock=अनलॉक करें...
main.vaultlist.contextMenu.unlockNow=अब अनलॉक करें
main.vaultlist.contextMenu.vaultoptions=वॉल्ट के विकल्प दिखाए
main.vaultlist.contextMenu.reveal=फोल्डर खोलें
main.vaultlist.addVaultBtn=वाउल्ट डालें
## Vault Detail
### Welcome
@@ -181,6 +204,7 @@ main.vaultDetail.unlockNowBtn=अब अनलॉक करें
main.vaultDetail.optionsBtn=वॉल्ट के विकल्प
### Unlocked
main.vaultDetail.accessLocation=आपके वॉल्ट की चीजें यहाँ एक्सेस कर सकतें हैं:
main.vaultDetail.revealBtn=फोल्डर खोलें
main.vaultDetail.lockBtn=लॉक करें
main.vaultDetail.stats=वॉल्ट के आंकड़े
### Missing
@@ -199,6 +223,7 @@ wrongFileAlert.link=और मदद के लिए, यह जाएं
vaultOptions.general=सामान्य
vaultOptions.general.vaultName=वॉल्ट का नाम
vaultOptions.general.actionAfterUnlock.ignore=कुछ न करें
vaultOptions.general.actionAfterUnlock.reveal=फोल्डर खोलें
## Mount
vaultOptions.mount=माउंट हो रहा है

View File

@@ -192,6 +192,7 @@ health.check.detail.checkFinishedAndFound=Provjera je završena. Molimo pregleda
health.check.detail.checkFailed=Provjera je otkazana zbog pogreške.
health.check.detail.checkCancelled=Provjera je otkazana.
health.check.exportBtn=Izvoz izvješća
## Result view
## Fix Application
health.fix.fixBtn=Popravi
health.fix.successTip=Popravak uspješan
@@ -272,6 +273,9 @@ stats.encr.total.data.mib=Podataka šifrirano: %.1f MiB
stats.encr.total.data.gib=Podataka šifrirano: %.1f GiB
stats.write.accessCount=Ukupno pisanja: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Zatvori
main.minimizeBtn.tooltip=Smanji

View File

@@ -189,6 +189,7 @@ health.check.detail.checkFinishedAndFound=Az ellenőrzés véget ért. Kérem el
health.check.detail.checkFailed=Az ellenőrzés egy hiba miatt megszakadt.
health.check.detail.checkCancelled=Az ellenőrzés meg lett szakítva.
health.check.exportBtn=Jelentés exportálása
## Result view
## Fix Application
health.fix.fixBtn=Javítás
health.fix.successTip=Javítás sikeres
@@ -269,6 +270,9 @@ stats.encr.total.data.mib=Titkosított adat: %.1f MiB
stats.encr.total.data.gib=Titkosított adat: %.1f GiB
stats.write.accessCount=Összes írás: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Bezárás
main.minimizeBtn.tooltip=Minimalizálás

View File

@@ -192,6 +192,7 @@ health.check.detail.checkFinishedAndFound=Pemeriksaan selesai. Silahkan tinjau h
health.check.detail.checkFailed=Pemeriksaan terhenti karena terjadi kesalahan.
health.check.detail.checkCancelled=Pemeriksaan dibatalkan.
health.check.exportBtn=Ekspor Laporan
## Result view
## Fix Application
health.fix.fixBtn=Perbaiki
health.fix.successTip=Perbaikan berhasil
@@ -272,6 +273,9 @@ stats.encr.total.data.mib=Data terenkripsi: %.1f MiB
stats.encr.total.data.gib=Data terenkripsi: %.1f GiB
stats.write.accessCount=Total ditulis: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Tutup
main.minimizeBtn.tooltip=Perkecil

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=Il controllo è terminato. Sei pregato
health.check.detail.checkFailed=Il controllo è terminato a causa di un errore.
health.check.detail.checkCancelled=Il controllo è stato annullato.
health.check.exportBtn=Esporta il Rapporto
## Result view
## Fix Application
health.fix.fixBtn=Correggi
health.fix.successTip=Correzione riuscita
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=Dati crittografati: %.1f MiB
stats.encr.total.data.gib=Dati crittografati: %.1f GiB
stats.write.accessCount=Scritture totali: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Chiudi
main.minimizeBtn.tooltip=Minimizza

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=実行中のチェックが終了し
health.check.detail.checkFailed=エラーが発生したためチェックを終了しました。
health.check.detail.checkCancelled=チェックがキャンセルされました。
health.check.exportBtn=結果をエクスポート
## Result view
## Fix Application
health.fix.fixBtn=修正
health.fix.successTip=正常に修正されました
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=暗号化済みデータ: %.1f MiB
stats.encr.total.data.gib=暗号化済みデータ: %.1f GiB
stats.write.accessCount=合計書き込み: %d
## Accesses
# Main Window
main.closeBtn.tooltip=閉じる
main.minimizeBtn.tooltip=最小化

View File

@@ -182,6 +182,7 @@ health.check.detail.checkRunning=검사가 현재 실행중입니다...
health.check.detail.checkSkipped=선택된 검사항목이 없습니다.
health.check.detail.checkFinished=검사가 성공적으로 완료되었습니다.
health.check.exportBtn=보고서 내보내기
## Result view
## Fix Application
health.fix.fixBtn=문제해결
health.fix.successTip=문제 해결이 성공적으로 완료되었습니다
@@ -262,6 +263,9 @@ stats.encr.total.data.mib=데이터 암호화: %.1f MiB
stats.encr.total.data.gib=데이터 암호화: %.1f GiB
stats.write.accessCount=총 쓰기 횟수: %d
## Accesses
# Main Window
main.closeBtn.tooltip=닫기
main.minimizeBtn.tooltip=최소화

View File

@@ -148,6 +148,7 @@ migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Nav atļaujas
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -181,6 +182,9 @@ preferences.about=Par lietotni
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=Aizvērt
main.minimizeBtn.tooltip=Minimizēt

View File

@@ -57,6 +57,7 @@ hub.register.registerBtn=Потврди
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -73,6 +74,9 @@ hub.register.registerBtn=Потврди
## Read
## Write
## Accesses
# Main Window
## Drag 'n' Drop
## Vault List

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=Helsesjekken er ferdig å kjøre. Venn
health.check.detail.checkFailed=Helsesjekken avsluttet på grunn av en feil.
health.check.detail.checkCancelled=Helsesjekken ble avbrutt.
health.check.exportBtn=Eksporter rapport
## Result view
## Fix Application
health.fix.fixBtn=Reparer
health.fix.successTip=Vellykket reparering
@@ -229,7 +230,7 @@ preferences.title=Innstillinger
## General
preferences.general=Generelt
preferences.general.startHidden=Skjul vinduet når du starter Cryptomator
preferences.general.autoCloseVaults=Låse åpne hvelv automatisk ved avslutning av programmet
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
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=Data kryptert: %.1f MiB
stats.encr.total.data.gib=Data kryptert: %.1f GiB
stats.write.accessCount=Skrivninger totalt: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Lukk
main.minimizeBtn.tooltip=Minimer

View File

@@ -218,7 +218,27 @@ health.check.detail.checkFinished=De controle is succesvol beëindigd.
health.check.detail.checkFinishedAndFound=De controle is beëindigd. Bekijk alstublieft de resultaten.
health.check.detail.checkFailed=De controle is afgesloten door een fout.
health.check.detail.checkCancelled=De controle werd geannuleerd.
health.check.detail.listFilters.label=Filter
health.check.detail.listFilters.severity=Ernst
health.check.detail.listFilters.fixState=Herstel staat
health.check.detail.fixAllSpecificBtn=Repareer alle soorten
health.check.exportBtn=Exporteer rapport
## Result view
health.result.severityFilter.all=Ernst - Alles
health.result.severityFilter.good=Goed
health.result.severityFilter.info=Info
health.result.severityFilter.warn=Waarschuwing
health.result.severityFilter.crit=Kritiek
health.result.severityTip.good=Ernst: Goed\nNormale kluis structuur.
health.result.severityTip.info=Ernst: Info\nKluis structuur intact, oplossing voorgesteld,.
health.result.severityTip.warn=Ernst: Waarschuwing\nKluis structuur beschadigd, reparatie hoogst aanbevolen.
health.result.severityTip.crit=Ernst: Kritiek\nKluis structuur corrupt, data verlies bepaald.
health.result.fixStateFilter.all=Herstel status - Alles
health.result.fixStateFilter.fixable=Repareerbaar
health.result.fixStateFilter.notFixable=Niet repareerbaar
health.result.fixStateFilter.fixing=Repareren…
health.result.fixStateFilter.fixed=Gerepareerd
health.result.fixStateFilter.fixFailed=Reparatie mislukt
## Fix Application
health.fix.fixBtn=Herstel
health.fix.successTip=Hersteld
@@ -300,6 +320,11 @@ stats.encr.total.data.mib=Gegevens versleuteld: %.1f MiB
stats.encr.total.data.gib=Gegevens versleuteld: %.1f GiB
stats.write.accessCount=Totaal geschreven: %d
## Accesses
stats.access.current=Toegang: %d
stats.access.total=Totaal aantal toegangen: %d
# Main Window
main.closeBtn.tooltip=Sluiten
main.minimizeBtn.tooltip=Minimaliseer

View File

@@ -139,6 +139,7 @@ migration.impossible.moreInfo=Kvelven kan framleis opnast viss du bruker ein eld
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -173,6 +174,9 @@ preferences.about=Om
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=Lukk
main.minimizeBtn.tooltip=Minimer

View File

@@ -147,6 +147,7 @@ migration.impossible.moreInfo=ਵਾਲਟ ਨੂੰ ਅਜੇ ਵੀ ਪੁਰ
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -208,6 +209,9 @@ stats.encr.total.data.mib=ਡਾਟਾ ਇੰਕ੍ਰਿਪਟ ਕੀਤਾ: %.
stats.encr.total.data.gib=ਡਾਟਾ ਇੰਕ੍ਰਿਪਟ ਕੀਤਾ: %.1f GiB
stats.write.accessCount=ਕੁੱਲ ਲਿਖੇ: %d
## Accesses
# Main Window
main.closeBtn.tooltip=ਬੰਦ ਕਰੋ
main.minimizeBtn.tooltip=ਘੱਟੋ-ਘੱਟ

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=Test zakończony. Proszę sprawdzić w
health.check.detail.checkFailed=Wystąpił błąd, test zakończony.
health.check.detail.checkCancelled=Test został anulowany.
health.check.exportBtn=Eksportuj raport
## Result view
## Fix Application
health.fix.fixBtn=Napraw
health.fix.successTip=Naprawa udana
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=Dane odszyfrowane: %.1f kiB
stats.encr.total.data.gib=Dane odszyfrowane: %.1f kiB
stats.write.accessCount=Całkowity zapis: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Zamknij
main.minimizeBtn.tooltip=Minimalizuj

View File

@@ -209,6 +209,7 @@ health.check.detail.checkRunning=A verificação está atualmente a decorrer…
health.check.detail.checkSkipped=A verificação não foi selecionada para ser executada.
health.check.detail.checkFinished=A verificação foi concluída com sucesso.
health.check.detail.checkFinishedAndFound=A verificação concluiu. Por favor, reveja os resultados.
## Result view
## Fix Application
health.fix.fixBtn=Corrigir
@@ -247,6 +248,9 @@ stats.title=Estatísticas de %s
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=Fechar
main.minimizeBtn.tooltip=Minimizar

View File

@@ -218,7 +218,10 @@ health.check.detail.checkFinished=A verificação foi concluída com sucesso.
health.check.detail.checkFinishedAndFound=A verificação terminou em execução. Por favor, reveja os resultados.
health.check.detail.checkFailed=A verificação foi encerrada devido a um erro.
health.check.detail.checkCancelled=A verificação foi cancelada.
health.check.detail.listFilters.label=Filtro
health.check.detail.listFilters.severity=Severidade
health.check.exportBtn=Exportar Relatório
## Result view
## Fix Application
health.fix.fixBtn=Corrigir
health.fix.successTip=Consertado com sucesso
@@ -300,6 +303,9 @@ stats.encr.total.data.mib=Dados criptografados: %.1f MiB
stats.encr.total.data.gib=Dados criptografados: %.1f GiB
stats.write.accessCount=Total gravado: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Fechar
main.minimizeBtn.tooltip=Minimizar

View File

@@ -16,6 +16,10 @@ generic.button.print=Tipărește
# Error
error.message=A apărut o eroare
error.description=Cryptomatorul nu se aștepta să se întâmple asta. Puteți căuta soluții existente pentru această eroare. În cazul in care nu s-a raportat încă, nu ezitați să faceți acest lucru.
error.hyperlink.lookup=Caută soluții pentru această eroare
error.hyperlink.report=Raportează această eroare
error.technicalDetails=Detalii:
# Defaults
defaults.vault.vaultName=Seif
@@ -49,6 +53,12 @@ addvaultwizard.new.fileAlreadyExists=Există deja un fișier sau un dosar cu num
addvaultwizard.new.locationDoesNotExist=Dosarul în calea specificată nu există sau nu poate fi accesat
addvaultwizard.new.locationIsNotWritable=Nu există acces la scriere la calea specificată
addvaultwizard.new.locationIsOk=Locația potrivită pentru seiful dumneavoastră
addvaultwizard.new.invalidName=Nume de seif invalid
addvaultwizard.new.validName=Nume de seif valid
addvaultwizard.new.validCharacters.message=Numele seifului poate conține următoarele caractere:
addvaultwizard.new.validCharacters.chars=Caractere (e.x. a, ж or 수)
addvaultwizard.new.validCharacters.numbers=Numere
addvaultwizard.new.validCharacters.dashes=Linii(%s) sau sub-linii(%s)
### Password
addvaultwizard.new.createVaultBtn=Crează seif
addvaultwizard.new.generateRecoveryKeyChoice=Nu veți putea accesa datele dvs. fără parolă. Doriți o cheie de recuperare pentru cazul în care vă pierdeți parola?
@@ -67,18 +77,22 @@ addvault.new.readme.storageLocation.8=2. Deblocați seiful în Cryptomator.
addvault.new.readme.storageLocation.9=3. Deschideți locația de acces făcând clic pe butonul "Reveal".
addvault.new.readme.storageLocation.10=Dacă aveți nevoie de ajutor, vizitați documentația: %s
addvault.new.readme.accessLocation.fileName=BINEATIVENIT.rtf
addvault.new.readme.accessLocation.1=🔐️ UNITATE CRIPTATĂ 🔐️
addvault.new.readme.accessLocation.1=🔐️ VOLUM CRIPTAT 🔐️
addvault.new.readme.accessLocation.2=Aceasta este locația de acces a seifului dvs.
addvault.new.readme.accessLocation.3=Orice fișier adăugat la acest volum va fi criptat de către Cryptomator. Puteți lucra la el ca pe orice altă unitate/folder. Aceasta este doar o vizualizare decriptată a conținutului său, fișierele sunt criptate tot timpul pe hard disk-ul tău.
addvault.new.readme.accessLocation.4=Puteți să ștergeți acest fișier.
## Existing
addvaultwizard.existing.instruction=Alegeți fișierul "vault.cryptomator" al seifului dvs. existent. Dacă există doar un fișier numit "masterkey.cryptomator", alegeți-l pe acesta.
addvaultwizard.existing.chooseBtn=Alege…
addvaultwizard.existing.filePickerTitle=Selectați fișierul seif
addvaultwizard.existing.filePickerMimeDesc=Seif Cryptomator
## Success
addvaultwizard.success.nextStepsInstructions=Seiful "%s" a fost adăugat.\nTrebuie să deblocați acest seif pentru a accesa sau adăuga conținut. Alternativ, îl puteți debloca în orice moment ulterior.
addvaultwizard.success.unlockNow=Deblochează acum
# Remove Vault
removeVault.title=Eliminați seiful
removeVault.message=Ștergeți seiful?
removeVault.description=Acest lucru va face Cryptomator să uite de acest seif. Îl puteţi adăuga din nou mai târziu. Nici un fişier criptat nu va fi şters din hard disk-ul dvs.
removeVault.confirmBtn=Eliminați seiful
@@ -89,6 +103,7 @@ changepassword.finalConfirmation=Înțeleg că nu voi putea accesa datele mele d
# Forget Password
forgetPassword.title=Parolă uitată
forgetPassword.message=Ați uitat parola?
forgetPassword.description=Această acțiune va șterge parola salvată a acestui seif din keychain-ul sistemului de operare.
forgetPassword.confirmBtn=Parolă uitată
@@ -98,32 +113,55 @@ unlock.passwordPrompt=Introduceți parola pentru "%s":
unlock.savePassword=Memorează parola
unlock.unlockBtn=Deblocați
## Select
unlock.chooseMasterkey.message=Fișierul cheii principale nu a fost găsit
unlock.chooseMasterkey.description=Nu s-a putut găsi fișierul masterkey pentru acest seif la locația așteptată. Vă rugăm să alegeți manual fișierul cheie.
unlock.chooseMasterkey.filePickerTitle=Selectaţi fişierul Masterkey
unlock.chooseMasterkey.filePickerMimeDesc=Cheia principală a Cryptomator-ului
## Success
unlock.success.message=Seiful a fost deblocat cu succes
unlock.success.description=Deblocat "%s" cu succes! Seiful dvs. este acum accesibil prin unitatea sa virtuală.
unlock.success.rememberChoice=Ține minte alegerea, nu mai arăta asta din nou
unlock.success.revealBtn=Dezvăluie unitatea
unlock.success.revealBtn=Dezvăluie partiția
## Failure
unlock.error.message=Imposibil de deblocat seiful
unlock.error.message=Seiful nu a fost putut deschis
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Punctul de montare "%s" nu este un dosar, nu este gol sau nu există.
unlock.error.invalidMountPoint.existing=Punctul de montare "%s" există deja sau dosarul părinte lipsește.
unlock.error.invalidMountPoint.driveLetterOccupied=Partiția cu litera "%s" este deja in folosința.
## Hub
### Waiting
hub.auth.message=Se așteaptă autentificarea…
hub.auth.description=Ar trebui să fiți redirecționat automat către pagina de autentificare.
hub.auth.loginLink=Nu ați fost redirecționat? Apăsați aici pentru a deschide pagina.
### Receive Key
hub.receive.message=Se procesează răspunsul…
hub.receive.description=In acest moment Criptomatorul primește și procesează răspunsul de la Hub. Vă rugăm să așteptați.
### Register Device
hub.register.message=Numele dispozitivului este necesar
hub.register.description=Se pare că este prima data când accesați Hub-ul de pe acest dispozitiv. Trebuie sa denumiți acest dispozitiv pentru autorizarea accesului.
hub.register.nameLabel=Numele dispozitivului
hub.register.occupiedMsg=Acest nume este deja utilizat
hub.register.registerBtn=Confirmați
### Registration Success
hub.registerSuccess.message=Dispozitiv numit
hub.registerSuccess.description=Pentru a accesa acest seif, dispozitivul dvs. trebuie să fie autorizat de proprietarul seifului.
### Registration Failed
hub.registerFailed.message=Numirea dispozitivului a eșuat
hub.registerFailed.description=O eroare a fost întâmpinata în procesul de denumire. Pentru mai multe detalii, verificați jurnalul aplicației.
### Unauthorized
hub.unauthorized.message=Acces respins
hub.unauthorized.description=Dispozitivul dvs. nu a fost autorizat să acceseze acest seif. Solicitați proprietarului seifului să va autorizeze accesul.
### License Exceeded
hub.licenseExceeded.message=Numărul de licențe a fost depășit
hub.licenseExceeded.description=Cryptomator Hub a permis accesul la mai mulți utilizatori decât licența permite. Vă rugăm să contactați administratorul Hub-ului dumneavoastră pentru a actualiza licența sau un administrator de seif pentru a elimina utilizatorii din seifuri.
# Lock
## Force
lock.forced.message=Blocarea a eșuat
lock.forced.description=Blocarea "%s" a fost blocată de operațiile în așteptare sau de fișierele deschise. Puteți forța blocarea acestui seif, dar întreruperea I/O poate duce la pierderea datelor nesalvate.
lock.forced.retryBtn=Încercați din nou
lock.forced.forceBtn=Blocare forțată
## Failure
lock.fail.message=Blocarea seifului a eșuat.
lock.fail.description=Seiful "%s" nu a putut fi blocat. Asigurați-vă că lucrările nesalvate sunt salvate altundeva și că operațiunile importante de citire/scriere sunt terminate. Pentru a închide seiful omoară procesul Cryptomator.
@@ -131,7 +169,7 @@ lock.fail.description=Seiful "%s" nu a putut fi blocat. Asigurați-vă că lucr
# Migration
migration.title=Îmbunătățește seiful
## Start
migration.start.prompt=Seiful dvs. "%s" trebuie să fie actualizat la un format mai nou. Înainte de a continua, asigurați-vă că nu există sincronizare în așteptare care să afecteze acest seif.
migration.start.prompt=Seiful dvs. "%s" trebuie să fie actualizat la un format mai nou. Înainte de a continua, asigurați-vă că nu există sincronizări în așteptare care să afecteze acest seif.
migration.start.confirm=Da, seiful meu este complet sincronizat
## Run
migration.run.enterPassword=Introduceți parola pentru "%s"
@@ -141,16 +179,16 @@ migration.run.progressHint=Acest lucru poate dura ceva timp…
migration.success.nextStepsInstructions=Seiful "%s" a fost migrat cu succes.\nAcum puteți debloca seiful dvs.
migration.success.unlockNow=Deblochează acum
## Missing file system capabilities
migration.error.missingFileSystemCapabilities.title=Tip de fișier nesuportat
migration.error.missingFileSystemCapabilities.title=Sistem de fișiere nesuportat
migration.error.missingFileSystemCapabilities.description=Migrarea nu a fost pornită, deoarece seiful dvs. este localizat pe un sistem de fișiere necorespunzător.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Sistemul de fişiere nu acceptă nume de fişiere lungi.
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Sistemul de fișiere nu suportă căi lungi.
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Sistemul de fișiere nu permite citirea.
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Sistemul de fişiere nu permite scrierea.
## Impossible
migration.impossible.heading=Imposibil de migrat seiful
migration.impossible.heading=Seiful nu a putut fi migrat
migration.impossible.reason=Seiful nu poate fi migrat automat deoarece locația sa de stocare sau punctul de acces nu este compatibilă.
migration.impossible.moreInfo=Seiful poate fi deschis în continuare cu o versiune mai veche. Pentru instrucţiuni despre cum să migraţi manual un seif, vizitaţi
migration.impossible.moreInfo=Seiful poate fi deschis cu o versiune mai veche. Pentru instrucţiuni despre cum să migraţi manual un seif, vizitaţi
# Health Check
## Start
@@ -180,7 +218,27 @@ health.check.detail.checkFinished=Verificarea s-a terminat cu succes.
health.check.detail.checkFinishedAndFound=Verificarea s-a terminat. Vă rugăm să examinați rezultatele.
health.check.detail.checkFailed=Verificarea a ieșit din cauza unei erori.
health.check.detail.checkCancelled=Verificarea a fost anulată.
health.check.detail.listFilters.label=Filtru
health.check.detail.listFilters.severity=Severitate
health.check.detail.listFilters.fixState=Repară starea
health.check.detail.fixAllSpecificBtn=Repară tot de tipul
health.check.exportBtn=Exportare raport
## Result view
health.result.severityFilter.all=Severitate - Toate
health.result.severityFilter.good=Bună
health.result.severityFilter.info=Informații
health.result.severityFilter.warn=Atenție
health.result.severityFilter.crit=Critică
health.result.severityTip.good=Severitate: Bună\nStructura seifului este normală.
health.result.severityTip.info=Severitate: Info\nStructura de seif este intactă, reparația este sugerată.
health.result.severityTip.warn=Severitate: Avertisment\nStructura seifului este coruptă, repararea este recomandată.
health.result.severityTip.crit=Severitate: Critică\nStructura de seif este coruptă, pierderea de date a fost determinată.
health.result.fixStateFilter.all=Repară starea - Toate
health.result.fixStateFilter.fixable=Reparabil
health.result.fixStateFilter.notFixable=Nu este reparabil
health.result.fixStateFilter.fixing=In curs de reparare…
health.result.fixStateFilter.fixed=Reparat
health.result.fixStateFilter.fixFailed=Reparația a eșuat
## Fix Application
health.fix.fixBtn=Repară
health.fix.successTip=Remediere reușită
@@ -191,13 +249,25 @@ 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
preferences.general.keychainBackend=Salvează parolele cu
## Interface
preferences.interface=Interfață
preferences.interface.theme=Aspect
preferences.interface.theme.automatic=Automat
preferences.interface.theme.dark=Întunecat
preferences.interface.theme.light=Luminos
preferences.interface.unlockThemes=Deblochează tema întunecată
preferences.interface.language=Limba (necesită repornirea programului)
preferences.interface.language.auto=Prestabilit din sistem
preferences.interface.interfaceOrientation=Orientarea interfeței
preferences.interface.interfaceOrientation.ltr=De la stânga la dreapta
preferences.interface.interfaceOrientation.rtl=De la dreapta la stânga
preferences.interface.showMinimizeButton=Arată butonul de minimizare
preferences.interface.showTrayIcon=Arată tray icon (necesită repornire)
## Volume
preferences.volume=Unitate virtuală
preferences.volume.type=Tip volum
@@ -250,6 +320,11 @@ stats.encr.total.data.mib=Date criptate: %.1f MiB
stats.encr.total.data.gib=Date criptate: %.1f GiB
stats.write.accessCount=Total scrieri: %d
## Accesses
stats.access.current=Acces: %d
stats.access.total=Accesuri totale: %d
# Main Window
main.closeBtn.tooltip=Închide
main.minimizeBtn.tooltip=Minimizează
@@ -297,6 +372,9 @@ main.vaultDetail.missing.changeLocation=Schimbați locația seifului…
main.vaultDetail.migrateButton=Îmbunătățește seiful
main.vaultDetail.migratePrompt=Înainte de a-l putea accesa, seiful dumneavoastră trebuie actualizat la format nou
### Error
main.vaultDetail.error.info=A apărut o eroare la încărcarea seifului de pe disc.
main.vaultDetail.error.reload=Reîncărcare
main.vaultDetail.error.windowTitle=S-a produs o eroare la încărcarea datelor
# Wrong File Alert
wrongFileAlert.title=Cum să criptați fișierele
@@ -338,20 +416,27 @@ vaultOptions.masterkey.changePasswordBtn=Schimbați parola
vaultOptions.masterkey.forgetSavedPasswordBtn=Parolă salvată uitată
vaultOptions.masterkey.recoveryKeyExplanation=O cheie de recuperare este singurul mijloc de a restabili accesul la un seif în caz că vă pierdeți parola.
vaultOptions.masterkey.showRecoveryKeyBtn=Afișează cheia de recuperare
vaultOptions.masterkey.recoverPasswordBtn=Resetează Parola
# Recovery Key
## Display Recovery Key
recoveryKey.display.title=Arată cheia de recuperare
recoveryKey.create.message=Trebuie să introduceți o parolă
recoveryKey.create.description=Introduceți parola pentru a afișa cheia de recuperare pentru "%s:
recoveryKey.display.description=Următoarea cheie de recuperare poate fi folosită pentru a restabili accesul la "%s":
recoveryKey.display.StorageHints=Păstrați cheia de recuperare undeva foarte sigur, de ex.\n • Păstrați-o folosind un manager de parole\n • Salvați-o pe un flash USB stick\n • Imprimați-o pe hârtie
## Reset Password
### Enter Recovery Key
recoveryKey.recover.title=Resetează Parola
recoveryKey.recover.prompt=Introduceți cheia de recuperare pentru "%s:
recoveryKey.recover.validKey=Aceasta este o cheie de recuperare validă
recoveryKey.printout.heading=Cheia de recuperare Cryptomator\n"%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Resetează
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Parola a fost resetată cu succes
recoveryKey.recover.resetSuccess.description=Puteți debloca seiful cu parola noua.
# New Password
newPassword.promptText=Introduceți o parolă nouă
@@ -366,6 +451,12 @@ passwordStrength.messageLabel.3=Puternică
passwordStrength.messageLabel.4=Foarte puternică
# Quit
quit.title=Închide aplicația
quit.message=Există seifuri deblocate
quit.description=Vă rugăm să confirmați că doriți să ieșiți. Cryptomatorul va încuia toate seifurile deblocate pentru a preveni pierderea datelor.
quit.lockAndQuitBtn=Blocați și ieșiți
# Forced Quit
# Forced Quit
quit.forced.message=Unele seifuri nu au putut fi blocate
quit.forced.description=Încuierea seifurilor a fost blocata de operațiuni in curs sau de fișiere deschise. Puteți încuia forțat aceste seifuri dar prin întreruperea operațiunilor I/O poate rezulta în pierderea datelor nesalvate.
quit.forced.forceAndQuitBtn=Încuiați forțat și ieșiți

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=Проверка завершена.
health.check.detail.checkFailed=Проверка прервана из-за ошибки.
health.check.detail.checkCancelled=Проверка была отменена.
health.check.exportBtn=Экспорт отчёта
## Result view
## Fix Application
health.fix.fixBtn=Исправить
health.fix.successTip=Исправлено
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=Зашифровано: %.1f МиБ
stats.encr.total.data.gib=Зашифровано: %.1f ГиБ
stats.write.accessCount=Всего записей: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Закрыть
main.minimizeBtn.tooltip=Свернуть

View File

@@ -72,6 +72,7 @@ unlock.unlockBtn=අගුළුහරින්න
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -88,6 +89,9 @@ unlock.unlockBtn=අගුළුහරින්න
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=වසන්න
## Drag 'n' Drop

View File

@@ -218,7 +218,20 @@ health.check.detail.checkFinished=Kontrola skončila úspešne.
health.check.detail.checkFinishedAndFound=Kontrola skončila. Prosím pozrite si jej výsledky.
health.check.detail.checkFailed=Kontrola skončila kôli chybe.
health.check.detail.checkCancelled=Kontrola bola zrušená.
health.check.detail.listFilters.label=Filter
health.check.detail.listFilters.severity=Závažnosť
health.check.detail.listFilters.fixState=Stav opravy
health.check.exportBtn=Exportovať správu
## Result view
health.result.severityFilter.good=Dobrý
health.result.severityFilter.info=Informácie
health.result.severityFilter.warn=Výstraha
health.result.severityFilter.crit=Kritický
health.result.fixStateFilter.fixable=Opraviteľné
health.result.fixStateFilter.notFixable=Neopraviteľné
health.result.fixStateFilter.fixing=Opravovanie…
health.result.fixStateFilter.fixed=Opravené
health.result.fixStateFilter.fixFailed=Oprava zlyhala
## Fix Application
health.fix.fixBtn=Opraviť
health.fix.successTip=Oprava úspešná
@@ -300,6 +313,11 @@ stats.encr.total.data.mib=Odkódovaných dát: %.1f MiB
stats.encr.total.data.gib=Odkódovaných dát: %.1f GiB
stats.write.accessCount=Suma zápisov: %d
## Accesses
stats.access.current=Sprístupnené: %d
stats.access.total=Celkovo sprístupnené: %d
# Main Window
main.closeBtn.tooltip=Zavrieť
main.minimizeBtn.tooltip=Minimalizovať

View File

@@ -152,6 +152,7 @@ migration.impossible.moreInfo=Sef se i dalje može otvoriti sa starijom verzijom
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -218,6 +219,9 @@ stats.encr.total.data.mib=Шифровано података: %.1f MiB
stats.encr.total.data.gib=Шифровано података: %.1f GiB
stats.write.accessCount=Укупно уписано: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Zatvori
main.minimizeBtn.tooltip=Умањи

View File

@@ -141,6 +141,7 @@ migration.impossible.moreInfo=Sef se i dalje može otvoriti sa starijom verzijom
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -194,6 +195,9 @@ stats.decr.total.data.mib=Dešifrovano podataka: %.1f MiB
stats.decr.total.data.gib=Dešifrovano podataka: %.1f GiB
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=Zatvori
main.preferencesBtn.tooltip=Podešavanja

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=Kontrollen är slutförd. Vänligen gr
health.check.detail.checkFailed=Kontrollen avslutades på grund av ett fel.
health.check.detail.checkCancelled=Kontrollen avbröts.
health.check.exportBtn=Exportera rapport
## Result view
## Fix Application
health.fix.fixBtn=Åtgärda
health.fix.successTip=Åtgärden lyckades
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=Data krypterad: %.1f MiB
stats.encr.total.data.gib=Data krypterad: %.1f GiB
stats.write.accessCount=Totalt skrivet: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Stäng
main.minimizeBtn.tooltip=Minimera

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=Ukaguzi ulimaliza kukimbia. Tafadhali
health.check.detail.checkFailed=Ukaguzi ulitoka kwa sababu ya kosa.
health.check.detail.checkCancelled=Ukaguzi ulikatishwa.
health.check.exportBtn=Hamisha Ripoti
## Result view
## Fix Application
health.fix.fixBtn=Kurekebisha
health.fix.successTip=Rekebisha imefanikiwa
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=Data iliyosimbwa kwa njia fiche: %.1f MiB
stats.encr.total.data.gib=Data iliyosimbwa kwa njia fiche: %.1f GiB
stats.write.accessCount=Jumla ya maandishi anaandika: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Futa
main.minimizeBtn.tooltip=Kupunguza

View File

@@ -179,6 +179,7 @@ health.intro.affirmation=மேலே உள்ள தகவல்களைப
health.fail.header=பெட்டகம் கட்டமைப்பை ஏற்றுவதில் பிழை
## Check Selection
## Detail view
## Result view
## Fix Application
health.fix.fixBtn=சரிசெய்
@@ -226,6 +227,9 @@ stats.encr.total.data.mib=குறியாக்கம் செய்யப
stats.encr.total.data.gib=குறியாக்கம் செய்யப்பட்ட தரவு: %.1f GiB
stats.write.accessCount=மொத்த எழுதப்பட்டது: %d
## Accesses
# Main Window
main.closeBtn.tooltip=மூடு
main.minimizeBtn.tooltip=சிறிதாக்கு

View File

@@ -57,6 +57,7 @@ lock.forced.retryBtn=మళ్ళీ చేయండి
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -75,6 +76,9 @@ preferences.interface.theme.light=కాంతి
## Read
## Write
## Accesses
# Main Window
## Drag 'n' Drop
## Vault List

View File

@@ -115,6 +115,7 @@ unlock.unlockBtn=ปลดล็อก
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -132,6 +133,9 @@ preferences.title=การตั้งค่า
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=ปิด
main.preferencesBtn.tooltip=การตั้งค่า

View File

@@ -218,7 +218,26 @@ health.check.detail.checkFinished=Kontrol başarıyla tamamlandı.
health.check.detail.checkFinishedAndFound=Kontrol işlemi tamamlandı. Lütfen sonuçları inceleyin.
health.check.detail.checkFailed=Bir hata nedeniyle kontrolden çıkıldı.
health.check.detail.checkCancelled=Kontrol iptal edildi.
health.check.detail.listFilters.label=Filtrele
health.check.detail.listFilters.severity=Önem Derecesi
health.check.detail.listFilters.fixState=Onarım durumu
health.check.exportBtn=Raporu Dışa Aktar
## Result view
health.result.severityFilter.all=Önem derecesi - Hepsi
health.result.severityFilter.good=İyi
health.result.severityFilter.info=Bilgi
health.result.severityFilter.warn=Uyarı
health.result.severityFilter.crit=Kritik
health.result.severityTip.good=Önem derecesi: İyi\nKasa yapısı normal.
health.result.severityTip.info=Önem derecesi: Bilgi\nKasa yapısı hasarsız, onarım önerilir.
health.result.severityTip.warn=Önem derecesi: Uyarı\nKasa yapısı bozulmuş, onarım şiddetle tavsiye edilir.
health.result.severityTip.crit=Önem derecesi: Kritik\nKasa yapısı bozulmuş, veri kaybı tespit edildi.
health.result.fixStateFilter.all=Onarım durumu - Hepsi
health.result.fixStateFilter.fixable=Onarılabilir
health.result.fixStateFilter.notFixable=Onarılamaz
health.result.fixStateFilter.fixing=Onarılıyor…
health.result.fixStateFilter.fixed=Onarıldı
health.result.fixStateFilter.fixFailed=Onarım başarısız
## Fix Application
health.fix.fixBtn=Düzelt
health.fix.successTip=Düzeltme başarılı
@@ -300,6 +319,11 @@ stats.encr.total.data.mib=Şifrelenen veri: %.1f MB
stats.encr.total.data.gib=Şifrelenen veri: %.1f GB
stats.write.accessCount=Toplam yazma: %d
## Accesses
stats.access.current=Erişim: %d
stats.access.total=Toplam erişim: %d
# Main Window
main.closeBtn.tooltip=Kapat
main.minimizeBtn.tooltip=Simge Durumuna Küçült

View File

@@ -129,6 +129,7 @@ migration.success.unlockNow=Розблокувати
## Start Failure
## Check Selection
## Detail view
## Result view
## Fix Application
# Preferences
@@ -146,6 +147,9 @@ preferences.title=Властивості
## Read
## Write
## Accesses
# Main Window
main.closeBtn.tooltip=Закрити
main.preferencesBtn.tooltip=Властивості

View File

@@ -74,7 +74,7 @@ addvault.new.readme.storageLocation.5=• dán bất kỳ tệp nào để mã h
addvault.new.readme.storageLocation.6=Nếu bạn muốn mã hóa tệp và xem nội dung của vault, hãy làm như sau:
addvault.new.readme.storageLocation.7=1. Thêm vault này vào Cryptomator.
addvault.new.readme.storageLocation.8=2. Mở khóa vault trong Cryptomator.
addvault.new.readme.storageLocation.9=3. Mở vị trí truy cập bằng cách nhấp vào nút "Mở".
addvault.new.readme.storageLocation.9=3. Mở vị trí truy cập bằng cách nhấp vào nút "Hiện thị".
addvault.new.readme.storageLocation.10=Nếu cần trợ giúp, hãy truy cập tài liệu: %s
addvault.new.readme.accessLocation.fileName=XIN_CHAO.rtf
addvault.new.readme.accessLocation.1=🔐️ PHÂN VÙNG DỮ LIỆU ĐƯỢC MÃ HOÁ 🔐️
@@ -112,14 +112,19 @@ unlock.passwordPrompt=Nhập mật khẩu cho "%s":
unlock.savePassword=Nhớ mật khẩu
unlock.unlockBtn=Mở khoá
## Select
unlock.chooseMasterkey.message=Không tìm thấy tệp Masterkey
unlock.chooseMasterkey.filePickerTitle=Chọn tệp Masterkey
unlock.chooseMasterkey.filePickerMimeDesc=Khóa chính Cryptomator
## Success
unlock.success.message=Mở khóa thành công
unlock.success.description=Nội dung trong vault "%s" đã có thể được truy xuất, thông qua đường dẫn gắn kết với nó.
unlock.success.rememberChoice=Ghi nhớ lựa chọn, không hiện lại
unlock.success.revealBtn=Hiển thị Drive
unlock.success.revealBtn=Hiển thị Ổ đĩa
## Failure
unlock.error.message=Không thể mở vault
### Invalid Mount Point
unlock.error.invalidMountPoint.notExisting=Đường dẫn gắn kết "%s" không phải là một thư mục, hoặc thư mục này không rỗng hoặc không tồn tại.
unlock.error.invalidMountPoint.existing=Đường dẫn liên kết "%s" đã tồn tại hoặc không tìm thấy thư mục cha.
unlock.error.invalidMountPoint.driveLetterOccupied=Ký tự cho Ổ cứng "%s" đã được sử dụng.
## Hub
### Waiting
@@ -135,8 +140,11 @@ hub.register.nameLabel=Tên thiết bị
hub.register.occupiedMsg=Tên đã sử dụng
hub.register.registerBtn=Xác nhận
### Registration Success
hub.registerSuccess.message=Thiết bị đã được đặt tên
hub.registerSuccess.description=Để truy cập vault, thiết bị của bạn cần được chủ sở hữu cho phép.
### Registration Failed
hub.registerFailed.message=Đặt tên thiết bị thất bại
hub.registerFailed.description=Lỗi phát sinh trong quá trình đặt tên. Để biết thêm chi tiết, hãy nhìn vào nhật ký ứng dụng (application log).
### Unauthorized
hub.unauthorized.message=Truy cập bị từ chối
hub.unauthorized.description=Thiết bị của bạn chưa được phép truy cập vault này. Yêu cầu chủ sở hữu cấp phép.
@@ -147,8 +155,10 @@ hub.unauthorized.description=Thiết bị của bạn chưa được phép truy
## Force
lock.forced.message=Khóa thất bại
lock.forced.retryBtn=Thử lại
lock.forced.forceBtn=Ép Khoá
## Failure
lock.fail.message=Khóa vault thất bại.
lock.fail.description=Không thể khoá vault "%s". Hãy chắc rằng phần việc dang dở của bạn đã được lưu ở nơi khác, và các chỉ thị Đọc/Ghi quan trọng đã hoàn tất. Sau đó, bạn có thể tắt tiến trình (process) Cryptomator để khoá vault lại.
# Migration
migration.title=Nâng cấp Vault
@@ -172,12 +182,17 @@ migration.impossible.heading=Không thể di chuyển vault
# Health Check
## Start
health.title=Kiểm tra Ổn định cho "%s"
health.intro.header=Kiểm tra Ổn định
health.intro.text=Kiểm tra tính ổn định là tập hợp các kiểm tra nhằm phát hiện và có thể sửa chữa các vấn đề ở cấu trúc nội bộ trong Vault của bạn. Vui lòng chú ý là:
health.intro.remarkSync=Đảm bảo tất cả các thiết bị được đồng bộ hóa hoàn toàn, điều này giải quyết hầu hết các vấn đề.
health.intro.remarkFix=Không phải tất cả các vấn đề đều có thể được khắc phục.
health.intro.remarkBackup=Nếu dữ liệu bị hỏng, chỉ có bản sao lưu mới có thể giúp được.
health.intro.affirmation=Tôi đã đọc và hiểu thông tin trên
## Start Failure
health.fail.header=Lỗi khi tải cấu hình Vault
health.fail.ioError=Đã xảy ra lỗi khi truy cập và đọc tệp config.
health.fail.parseError=Lỗi xuất hiện khi phân tích cú pháp trong cấu hình của vault.
health.fail.moreInfo=Thêm thông tin
## Check Selection
## Detail view
@@ -185,6 +200,7 @@ health.check.detail.checkScheduled=Việc kiểm tra đã được lên lịch.
health.check.detail.checkFinished=Việc kiểm tra đã kết thúc thành công.
health.check.detail.checkCancelled=Việc kiểm tra đã bị huỷ bỏ.
health.check.exportBtn=Xuất Báo Cáo
## Result view
## Fix Application
health.fix.fixBtn=Sửa
health.fix.successTip=Sửa thành công
@@ -197,11 +213,12 @@ 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ở file log
preferences.general.debugDirectory=Mở tệp nhật ký
preferences.general.autoStart=Khởi chạy Cryptomator khi khởi động hệ thống
preferences.general.keychainBackend=Lưu mật khẩu với
## Interface
preferences.interface=Giao diện
preferences.interface.theme=Cái nhìn và cảm nhận
preferences.interface.theme.automatic=Tự động
preferences.interface.theme.dark=Tối
preferences.interface.theme.light=Sáng
@@ -217,6 +234,7 @@ preferences.interface.showTrayIcon=Hiển thị biểu tượng khay (yêu cầu
preferences.volume=Ổ lưu trữ ảo
preferences.volume.type=Kiểu Phân Vùng
preferences.volume.webdav.port=Cổng của WebDAV
preferences.volume.webdav.scheme=Lược đồ WebDAV
## Updates
preferences.updates=Cập nhật
preferences.updates.currentVersion=Phiên bản hiện tại: %s
@@ -226,6 +244,9 @@ preferences.updates.updateAvailable=Có bản cập nhật lên phiên bản %s.
## Contribution
preferences.contribute=Hỗ trợ chúng tôi
preferences.contribute.registeredFor=Chứng nhận Người Hỗ Trợ được đăng ký cho %s
preferences.contribute.noCertificate=Hỗ trợ Cryptomator và lấy về cho bạn chứng nhận người hỗ trợ. Chứng nhận này giống như giấy phép sử dụng phần mềm, nhưng chỉ dành cho những con người tuyệt vời đang dùng phần mềm tự do. ;-)
preferences.contribute.getCertificate=Bạn chưa có? Tìm hiểu cách bạn có thể lấy được nó.
preferences.contribute.promptText=Vui lòng dán mã chứng nhận người hỗ trợ vào đây
#<-- Add entries for donations and code/translation/documentation contribution -->
## About
@@ -233,13 +254,36 @@ preferences.about=Giới thiệu
# Vault Statistics
stats.title=Thống kê về %s
stats.cacheHitRate=Tỷ lệ truy cập bộ nhớ cache
## Read
stats.read.throughput.idle=Đọc: không có
stats.read.throughput.kibs=Đọc: %.2f kiB/s
stats.read.throughput.mibs=Đọc: %.2f MiB/s
stats.read.total.data.none=Dữ liệu đã đọc: -
stats.read.total.data.kib=Dữ liệu đã đọc: %.1f kiB
stats.read.total.data.mib=Dữ liệu đã đọc: %.1f MiB
stats.read.total.data.gib=Dữ liệu đã đọc: %.1f GiB
stats.decr.total.data.none=Dữ liệu được giải mã: -
stats.decr.total.data.kib=Dữ liệu được giải mã: %.1f kiB
stats.decr.total.data.mib=Dữ liệu được giải mã: %.1f MiB
stats.decr.total.data.gib=Dữ liệu được giải mã: %.1f GiB
stats.read.accessCount=Tổng đọc: %d
## Write
stats.write.throughput.idle=Ghi: tạm ngưng
stats.write.throughput.kibs=Ghi: %.2f kiB/s
stats.write.throughput.mibs=Ghi: %.2f MiB/s
stats.write.total.data.none=Dữ liệu đã ghi: -
stats.write.total.data.kib=Dữ liệu đã ghi: %.1f kiB
stats.write.total.data.mib=Dữ liệu đã ghi: %.1f MiB
stats.write.total.data.gib=Dữ liệu đã ghi: %.1f GiB
stats.encr.total.data.none=Dữ liệu được mã hóa: -
stats.encr.total.data.kib=Dữ liệu được mã hóa: %.1f kiB
stats.encr.total.data.mib=Dữ liệu được mã hóa: %.1f MiB
stats.encr.total.data.gib=Dữ liệu được mã hóa: %.1f GiB
stats.write.accessCount=Tổng ghi: %d
## Accesses
# Main Window
main.closeBtn.tooltip=Đóng
@@ -257,7 +301,7 @@ main.vaultlist.contextMenu.lock=Khoá
main.vaultlist.contextMenu.unlock=Mở khoá…
main.vaultlist.contextMenu.unlockNow=Mở khóa bây giờ
main.vaultlist.contextMenu.vaultoptions=Hiện tùy chọn vault
main.vaultlist.contextMenu.reveal=Hiển thị Drive
main.vaultlist.contextMenu.reveal=Hiển thị Ổ đĩa
main.vaultlist.addVaultBtn=Thêm Vault
## Vault Detail
### Welcome
@@ -271,9 +315,13 @@ main.vaultDetail.passwordSavedInKeychain=Đã lưu mật khẩu
### Unlocked
main.vaultDetail.unlockedStatus=ĐÃ MỞ KHÓA
main.vaultDetail.accessLocation=Nội dung trong vault của bạn có thể truy cập ở đây:
main.vaultDetail.revealBtn=Hiển thị Drive
main.vaultDetail.revealBtn=Hiển thị Ổ đĩa
main.vaultDetail.lockBtn=Khoá
main.vaultDetail.bytesPerSecondRead=Đọc:
main.vaultDetail.bytesPerSecondWritten=Ghi:
main.vaultDetail.throughput.idle=chờ
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Thống kê Vault
### Missing
main.vaultDetail.missing.info=Cryptomator không thể tìm thấy vault tại đường dẫn này.
@@ -293,6 +341,8 @@ wrongFileAlert.title=Cách mã hóa tệp
wrongFileAlert.message=Bạn đã cố gắng mã hóa các tệp này?
wrongFileAlert.instruction.0=Để mã hóa tệp, hãy làm theo các bước sau:
wrongFileAlert.instruction.1=1. Mở Vault của bạn.
wrongFileAlert.instruction.2=2. Bấm vào "Hiển thị" để mở ổ đĩa trên trình quản lý tệp.
wrongFileAlert.instruction.3=3. Thêm tệp của bạn vào ổ đĩa này.
wrongFileAlert.link=Để được hỗ trợ thêm, hãy truy cập
# Vault Options
@@ -304,18 +354,23 @@ vaultOptions.general.autoLock.lockAfterTimePart2=phút
vaultOptions.general.unlockAfterStartup=Mở khóa vault khi khởi động Cryptomator
vaultOptions.general.actionAfterUnlock=Sau khi mở khóa thành công
vaultOptions.general.actionAfterUnlock.ignore=Không làm gì
vaultOptions.general.actionAfterUnlock.reveal=Hiển thị Drive
vaultOptions.general.actionAfterUnlock.reveal=Hiển thị Ổ đĩa
vaultOptions.general.actionAfterUnlock.ask=Hỏi
vaultOptions.general.startHealthCheckBtn=Bắt đầu Kiểm tra sức khỏe
## Mount
vaultOptions.mount.readonly=Chỉ đọc
vaultOptions.mount.winDriveLetterOccupied=Sử dụng
vaultOptions.mount.winDriveLetterOccupied=đã bị chiếm
vaultOptions.mount.mountPoint=Đường dẫn gắn kết
vaultOptions.mount.mountPoint.auto=Tự động chọn một vị trí thích hợp
vaultOptions.mount.mountPoint.driveLetter=Sử dụng ký tự ổ đĩa được chỉ định
vaultOptions.mount.mountPoint.custom=Tuỳ chỉnh đường dẫn
vaultOptions.mount.mountPoint.directoryPickerButton=Chọn…
vaultOptions.mount.mountPoint.directoryPickerTitle=Chọn đường dẫn trống
## Master Key
vaultOptions.masterkey=Mật khẩu
vaultOptions.masterkey.changePasswordBtn=Đổi mật khẩu
vaultOptions.masterkey.forgetSavedPasswordBtn=Quên mật khẩu đã lưu
vaultOptions.masterkey.recoveryKeyExplanation=Khóa khôi phục là cách duy nhất để khôi phục quyền truy cập vào vault nếu bạn mất mật khẩu.
vaultOptions.masterkey.showRecoveryKeyBtn=Hiện Khóa Khôi Phục
vaultOptions.masterkey.recoverPasswordBtn=Đặt lại Mật khẩu
@@ -323,10 +378,15 @@ vaultOptions.masterkey.recoverPasswordBtn=Đặt lại Mật khẩu
# Recovery Key
## Display Recovery Key
recoveryKey.display.title=Hiển thị Khóa Khôi Phục
recoveryKey.create.message=Yêu cầu mật khẩu
recoveryKey.create.description=Nhập mật khẩu cho "%s" để hiện khóa khôi phục.
recoveryKey.display.description=Khóa khôi phục sau có thể được sử dụng để khôi phục quyền truy cập vào "%s":
recoveryKey.display.StorageHints=Giữ ở một nơi rất an toàn, ví dụ:\n • Lưu trữ bằng trình quản lý mật khẩu\n • Lưu vào ổ đĩa flash USB\n • In ra giấy
## Reset Password
### Enter Recovery Key
recoveryKey.recover.title=Đặt lại Mật khẩu
recoveryKey.recover.prompt=Nhập khóa khôi phục của bạn cho "%s":
recoveryKey.recover.validKey=Đây là khóa khôi phục hợp lệ
recoveryKey.printout.heading=Khóa Khôi phục Cryptomator\n"%s"\n
### Reset Password
@@ -349,6 +409,11 @@ passwordStrength.messageLabel.4=Rất mạnh
# Quit
quit.title=Thoát ứng dụng
quit.message=Có vault chưa được khóa
quit.description=Vui lòng xác nhận rằng bạn muốn thoát. Cryptomator sẽ khóa tất cả các vault đã mở khóa để tránh mất dữ liệu.
quit.lockAndQuitBtn=Khóa và Thoát
# Forced Quit
# Forced Quit
quit.forced.message=Không thể khóa một số vault
quit.forced.description=Việc khoá vault đã bị chặn bởi các hoạt động đang chờ xử lý hoặc các tệp đang mở. Bạn có thể buộc khóa các vault còn lại, tuy nhiên việc làm gián đoạn đọc/ghi có thể dẫn đến mất dữ liệu chưa được lưu.
quit.forced.forceAndQuitBtn=Bắt buộc và Thoát

View File

@@ -218,7 +218,27 @@ health.check.detail.checkFinished=成功完成检查
health.check.detail.checkFinishedAndFound=检查运行完成,请查看结果
health.check.detail.checkFailed=检查由于出错而退出
health.check.detail.checkCancelled=检查已被取消
health.check.detail.listFilters.label=筛选
health.check.detail.listFilters.severity=严重性
health.check.detail.listFilters.fixState=修复状态
health.check.detail.fixAllSpecificBtn=修复所有类型
health.check.exportBtn=导出报告
## Result view
health.result.severityFilter.all=严重性 - 全部
health.result.severityFilter.good=良好
health.result.severityFilter.info=信息
health.result.severityFilter.warn=警告
health.result.severityFilter.crit=危急
health.result.severityTip.good=严重性:良好\n普通保险库结构
health.result.severityTip.info=严重性:信息\n保险库结构完好无损建议修复
health.result.severityTip.warn=严重性:警告\n保险库结构损坏强烈建议修复
health.result.severityTip.crit=严重性:危急\n保险库结构损坏已确定数据丢失
health.result.fixStateFilter.all=修复状态 - 全部
health.result.fixStateFilter.fixable=可修复
health.result.fixStateFilter.notFixable=无法修复
health.result.fixStateFilter.fixing=正在修复…
health.result.fixStateFilter.fixed=已修复
health.result.fixStateFilter.fixFailed=修复失败
## Fix Application
health.fix.fixBtn=修复
health.fix.successTip=修复成功
@@ -300,6 +320,11 @@ stats.encr.total.data.mib=已加密数据:%.1f MiB
stats.encr.total.data.gib=已加密数据:%.1f GiB
stats.write.accessCount=写入总数:%d
## Accesses
stats.access.current=访问量:%d
stats.access.total=总访问量:%d
# Main Window
main.closeBtn.tooltip=关闭
main.minimizeBtn.tooltip=最小化

View File

@@ -15,7 +15,7 @@ generic.button.next=繼續
generic.button.print=列印
# Error
error.message=%s 錯誤
error.message=發生錯誤
error.description=糟糕Cryptomator 發生了預期外的錯誤。你可以嘗試查找這錯誤的現有解決方案,如果是新錯誤,請隨時向我們報告。
error.hyperlink.lookup=搜尋此錯誤
error.hyperlink.report=報告此錯誤
@@ -52,7 +52,13 @@ addvaultwizard.new.directoryPickerTitle=選取資料夾
addvaultwizard.new.fileAlreadyExists=已存在與加密庫同名的檔案或資料夾
addvaultwizard.new.locationDoesNotExist=指定的資料夾不存在或無法存取
addvaultwizard.new.locationIsNotWritable=沒有指定路徑的寫入權限
addvaultwizard.new.locationIsOk=適合放置你的加密庫
addvaultwizard.new.locationIsOk=可存放加密庫的的位置
addvaultwizard.new.invalidName=無效的加密庫名稱
addvaultwizard.new.validName=有效的加密庫名稱
addvaultwizard.new.validCharacters.message=加密庫名稱可包含以下字元:
addvaultwizard.new.validCharacters.chars=單詞字符 (例如 a、ж 或 수)
addvaultwizard.new.validCharacters.numbers=數字
addvaultwizard.new.validCharacters.dashes=連字符 (%s) 或下劃線 (%s)
### Password
addvaultwizard.new.createVaultBtn=建立加密庫
addvaultwizard.new.generateRecoveryKeyChoice=若遺失密碼,你將無法存取資料。你是否希望建立一組在遺失密碼時可用作復原的金鑰?
@@ -86,6 +92,7 @@ addvaultwizard.success.unlockNow=立即解鎖
# Remove Vault
removeVault.title=移除加密庫
removeVault.message=移除加密庫?
removeVault.description=這只會讓 Cryptomator 忘記這個加密庫。你可以之後再重新加入。已加密的檔案將不會從硬碟中移除。
removeVault.confirmBtn=移除加密庫
@@ -96,6 +103,7 @@ changepassword.finalConfirmation=我明白如果忘記密碼將無法存取資
# Forget Password
forgetPassword.title=忘記密碼
forgetPassword.message=清除已儲存的密碼?
forgetPassword.description=這將會從系統鑰匙圈中移除這個加密庫已存的密碼。
forgetPassword.confirmBtn=忘記密碼
@@ -105,10 +113,12 @@ unlock.passwordPrompt=輸入「%s」的密碼
unlock.savePassword=記住密碼
unlock.unlockBtn=解鎖
## Select
unlock.chooseMasterkey.message=未找到Masterkey檔案
unlock.chooseMasterkey.description=無法在其預期位置找到此加密庫的主密鑰檔案。請手動選擇密鑰文件。
unlock.chooseMasterkey.filePickerTitle=選擇主金鑰檔案
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator 主密鑰
## Success
unlock.success.message=解鎖成功
unlock.success.description=成功解鎖「%s」現在可以存取你的加密庫。
unlock.success.rememberChoice=記得這個決定,不要再顯示
unlock.success.revealBtn=展示磁碟
@@ -120,13 +130,30 @@ unlock.error.invalidMountPoint.existing=掛載點「%s」已經存在或其上
unlock.error.invalidMountPoint.driveLetterOccupied=磁碟代號「%s」已被使用。
## Hub
### Waiting
hub.auth.message=等待認證中…
hub.auth.description=您將被自動傳送到登入頁面。
hub.auth.loginLink=未被轉送?點擊這裡打開
### Receive Key
hub.receive.message=處理著回應…
hub.receive.description=Cryptomator正在接收並處理來自Hub的回應。 請等待。
### Register Device
hub.register.message=需要設備名稱
hub.register.description=似乎為第一次透過 Hub 訪問此設備。您需要命名此設備,以便識別設備並授予讀取權限。
hub.register.nameLabel=設備名稱
hub.register.occupiedMsg=名称已被占用
hub.register.registerBtn=確認
### Registration Success
hub.registerSuccess.message=設備命名成功
hub.registerSuccess.description=想讀取檔加密庫,你的設備需得到檔案庫擁有者的授權。
### Registration Failed
hub.registerFailed.message=設備命名失敗
hub.registerFailed.description=命名過程中引發錯誤。有關詳細信息,請查看應用程序日誌。
### Unauthorized
hub.unauthorized.message=拒絕存取
hub.unauthorized.description=您的設備權限尚未允許存取加密庫,請聯絡加密庫擁有者
### License Exceeded
hub.licenseExceeded.message=软件許可已滿額
hub.licenseExceeded.description=Cryptomator Hub 的被授予授權的使用者的數量,多於許可證容許的使用者數量。 請聯絡 Cryptomator Hub 管理員升級授權,或聯絡加密庫管理員從加密庫移除使用者。
# Lock
@@ -142,7 +169,7 @@ lock.fail.description=加密庫「%s」無法被鎖定。請確保未存檔的
# Migration
migration.title=升級加密庫
## Start
migration.start.prompt=加密庫「%s」需要升級成新的格式。在開始前請確保不會有檔案同步干擾更新過程
migration.start.prompt=加密庫「%s」需要更新成新的格式。更新前,請確保無檔案同步干擾更新。
migration.start.confirm=是的,我的加密庫已同步完成
## Run
migration.run.enterPassword=輸入「%s」的密碼
@@ -191,7 +218,25 @@ health.check.detail.checkFinished=檢查順利完成。
health.check.detail.checkFinishedAndFound=檢查結束,請查看結果。
health.check.detail.checkFailed=檢查發生錯誤並意外退出。
health.check.detail.checkCancelled=檢查被取消。
health.check.detail.listFilters.severity=過濾精度
health.check.detail.listFilters.fixState=鎖定/維持當前狀態
health.check.exportBtn=匯出報告
## Result view
health.result.severityFilter.all=過濾精度 - 所有
health.result.severityFilter.good=適當
health.result.severityFilter.info=詳細
health.result.severityFilter.warn=警報
health.result.severityFilter.crit=危急
health.result.severityTip.good=檢查結果:良好\n加密庫結構正常
health.result.severityTip.info=程度:詳細\n加密庫結構無缺完整建議維持/鎖定目前狀態
health.result.severityTip.warn=程度:警報\n加密庫結構受損強烈建議修復
health.result.severityTip.crit=程度:危急\n加密庫結構受損已確定有數據丟失或受損
health.result.fixStateFilter.all=修復狀態 - 全部
health.result.fixStateFilter.fixable=可以修復
health.result.fixStateFilter.notFixable=無法修復
health.result.fixStateFilter.fixing=修復中
health.result.fixStateFilter.fixed=已修復
health.result.fixStateFilter.fixFailed=修復失敗
## Fix Application
health.fix.fixBtn=修復
health.fix.successTip=修復成功
@@ -202,6 +247,7 @@ preferences.title=偏好設定
## General
preferences.general=一般
preferences.general.startHidden=啟動 Cryptomator 時隱藏視窗
preferences.general.autoCloseVaults=關閉應用時,自動鎖定打開的加密庫
preferences.general.debugLogging=啟用除錯日誌
preferences.general.debugDirectory=展示日誌檔案
preferences.general.autoStart=系統啟動時同時啟動 Cryptomator
@@ -272,6 +318,11 @@ stats.encr.total.data.mib=資料加密: %.1f MiB
stats.encr.total.data.gib=資料加密: %.1f GiB
stats.write.accessCount=總寫入: %d
## Accesses
stats.access.current=訪問紀錄:%d
stats.access.total=總存取數:%d
# Main Window
main.closeBtn.tooltip=關閉
main.minimizeBtn.tooltip=最小化
@@ -363,20 +414,27 @@ vaultOptions.masterkey.changePasswordBtn=更改密碼
vaultOptions.masterkey.forgetSavedPasswordBtn=清除已儲存密碼
vaultOptions.masterkey.recoveryKeyExplanation=在您遺失密碼時,僅能以復原金鑰恢復存取。
vaultOptions.masterkey.showRecoveryKeyBtn=顯示復原金鑰
vaultOptions.masterkey.recoverPasswordBtn=重設密碼
# Recovery Key
## Display Recovery Key
recoveryKey.display.title=顯示恢復金鑰
recoveryKey.create.message=需要密碼
recoveryKey.create.description=請輸入您的密碼以顯示「%s」的復原金鑰
recoveryKey.display.description=下方的復原金鑰可用來恢復「%s」的存取
recoveryKey.display.StorageHints=請把它保存在非常安全的地方,例如:\n • 使用密碼管理器保管\n • 存在 USB 隨身碟裡\n • 印在紙上
## Reset Password
### Enter Recovery Key
recoveryKey.recover.title=重設密碼
recoveryKey.recover.prompt=輸入「%s」的復原金鑰
recoveryKey.recover.validKey=這是有效的復原金鑰
recoveryKey.printout.heading=Cryptomator 復原金鑰\n「%s」\n
### Reset Password
recoveryKey.recover.resetBtn=重設
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=密碼重設成功
recoveryKey.recover.resetSuccess.description=您現在可以用新密碼解鎖您的加密庫
# New Password
newPassword.promptText=輸入新密碼
@@ -391,6 +449,12 @@ passwordStrength.messageLabel.3=強
passwordStrength.messageLabel.4=非常強
# Quit
quit.title=結束應用程式
quit.message=有加密庫仍是解鎖的
quit.description=請確定您要關閉程式Cryptomator 將穩定有序地鎖定全部加密庫,防止數據丟失。
quit.lockAndQuitBtn=鎖定並離開
# Forced Quit
# Forced Quit
quit.forced.message=無法鎖定某些加密庫
quit.forced.description=仍有未完成的操作或有開啟中的檔案以致無法鎖定。你可以強制鎖定這個加密庫,不過中斷讀寫可能會導致資料遺失或未被儲存。
quit.forced.forceAndQuitBtn=強制退出程式

View File

@@ -219,6 +219,7 @@ health.check.detail.checkFinishedAndFound=檢查結束。請查看結果。
health.check.detail.checkFailed=發生錯誤,檢查意外退出。
health.check.detail.checkCancelled=檢查被取消。
health.check.exportBtn=匯出報告
## Result view
## Fix Application
health.fix.fixBtn=修復
health.fix.successTip=修復成功
@@ -300,6 +301,9 @@ stats.encr.total.data.mib=資料加密:%.1f MiB
stats.encr.total.data.gib=資料加密:%.1f GiB
stats.write.accessCount=總寫入:%d
## Accesses
# Main Window
main.closeBtn.tooltip=關閉
main.minimizeBtn.tooltip=最小化

View File

@@ -35,13 +35,15 @@
<cve>CVE-2022-25366</cve>
</suppress>
<!-- Apache Commons-cli false positives below -->
<suppress>
<notes><![CDATA[
False positive for commons-cli due, see https://github.com/jeremylong/DependencyCheck/pull/4148
]]></notes>
<gav regex="true">^commons\-cli:commons\-cli:.*$</gav>
<cpe>cpe:/a:apache:james</cpe>
<!-- while we are at it exclude also this fp -->
<!-- while we are at it exclude also these fp -->
<cpe>cpe:/a:spirit-project:spirit</cpe>
<cpe>cpe:/a:apache:commons_net</cpe>
</suppress>
</suppressions>