Compare commits

...
Author SHA1 Message Date
Sebastian Stenzel 04bdb2d8c4 fix mocked update check 2025-11-24 20:58:57 +01:00
Sebastian Stenzel f32d24c1c0 Merge branch 'develop' into release/1.19.0 2025-11-24 20:58:25 +01:00
Sebastian Stenzel f4f093bb42 update GitHub Releases Template
[ci skip]
2025-11-24 19:57:24 +01:00
Sebastian Stenzel 3f06460667 bump version 2025-11-24 14:50:19 +01:00
Sebastian Stenzel 617f1bf2c9 CHANGELOG.md: added
[ci skip]
2025-11-24 14:49:12 +01:00
Sebastian StenzelandGitHub 02ad38f871 Merge pull request #3948 from purejava/feature/app-update
Implement new update API
2025-11-24 14:41:55 +01:00
Sebastian Stenzel 7a2944cbea bump integrations version 2025-11-24 14:39:17 +01:00
Sebastian Stenzel 4c47204f92 Merge branch 'develop' into feature/app-update 2025-11-24 14:34:44 +01:00
Tobias HagemannandGitHub f052395a7f Add section on use of Generative AI 2025-11-17 15:03:08 +01:00
Sebastian Stenzel 57be83b38d update license-merges 2025-11-12 11:56:09 +01:00
Sebastian Stenzel c2cd4f5bbf update compiler.xml
[ci skip]
2025-11-12 11:55:59 +01:00
Sebastian Stenzel d3d57312ba Merge branch 'develop' into feature/app-update
# Conflicts:
#	.github/workflows/appimage.yml
#	.github/workflows/check-jdk-updates.yml
#	.github/workflows/debian.yml
#	.github/workflows/mac-dmg-x64.yml
#	.github/workflows/mac-dmg.yml
#	.github/workflows/win-exe.yml
2025-11-12 11:50:21 +01:00
593a64c9bd Bump JDK to version 25 (#4031)
* first draft for JDK 25 migration (including comments)

* fix unresolvable version in setup-java action

* switch to official `openjdk-25-jdk` ubuntu package

see https://packages.ubuntu.com/search?suite=jammy&arch=any&searchon=names&keywords=openjdk-25

* update jacoco to support JDK 25

* use jdk 25.0.1 for building the app

# Conflicts:
#	.github/workflows/win-exe.yml

* Use correct SHA256 value for openjfx linux arm64

* zulu version is different to temurin :rolling_eyes:

* fix check-jdk-updates

---------

Co-authored-by: Armin Schrenk <armin.schrenk@skymatic.de>
2025-11-12 10:50:37 +01:00
sunnyraindyandGitHub 19dc4fb6ff chore: fix typo in comment (#4036)
Signed-off-by: sunnyraindy <sunnyraindy@outlook.com>
2025-11-12 10:49:14 +01:00
Armin Schrenk 81f45012f3 [skip ci] Merge branch 'main' into develop 2025-11-12 10:45:38 +01:00
Armin Schrenk c59554f7bb Merge branch 'release/1.18.0' 2025-11-12 10:43:22 +01:00
Armin Schrenk 11c66e8df7 Use correct SHA256 value for openjfx linux arm64 2025-11-11 16:23:33 +01:00
Armin Schrenk c49bf0f146 finalize 1.18.0 2025-11-11 16:08:00 +01:00
Armin Schrenk e74bd91879 Merge branch 'develop' into release/1.18.0 2025-11-11 15:58:03 +01:00
CryptobotandGitHub 89d9249a08 New Crowdin updates (#4016)
New translations strings.properties

Afrikaans; Arabic; Bashkir; Belarusian; Bengali; Bosnian; Bulgarian; Catalan; Chinese Simplified; Chinese Traditional; Chinese Traditional, Hong Kong; Croatian; Czech; Danish; Dutch; Filipino; Finnish; French; Galician; German; Greek; Hebrew; Hindi; Hungarian; Indonesian; Italian; Japanese; Korean; Latvian; Macedonian; Marathi; Norwegian; Norwegian Bokmal; Norwegian Nynorsk; Persian; Polish; Portuguese; Portuguese, Brazilian; Punjabi; Romanian; Russian; Serbian (Cyrillic); Serbian (Latin); Sinhala; Slovak; Slovenian; Spanish; Swahili, Tanzania; Swedish; Tamil; Telugu; Thai; Turkish; Ukrainian; Urdu (Pakistan); Uyghur; Vietnamese; 

[ci skip]
2025-11-11 15:57:18 +01:00
Armin SchrenkandGitHub 013fff1223 Feature: Sign files with Azure trusted signing (#4038) 2025-11-11 14:24:20 +01:00
Armin Schrenk fd9d27215e adjust wording 2025-11-11 13:20:02 +01:00
Sebastian Stenzel f91cc2374c fix syntax error (really!) 2025-11-08 10:48:29 +01:00
Sebastian Stenzel 48298bb161 fix syntax error 2025-11-08 10:37:10 +01:00
Sebastian Stenzel 7a1cd9026c use dmg update mechanism 2025-11-08 10:24:11 +01:00
Sebastian Stenzel 754e53d8db cleanup 2025-11-08 10:18:40 +01:00
Sebastian Stenzel c36f1bc8d0 moved update mechanism classes to integrations-* 2025-11-07 13:59:10 +01:00
Sebastian Stenzel 7315b59a6d simplify + allow IDE to find used i18n strings 2025-11-06 15:36:08 +01:00
Sebastian Stenzel 8a243a01aa cleanup + error handling 2025-11-06 15:35:12 +01:00
Sebastian Stenzel 9e4006cc89 improved error handling 2025-11-06 10:47:15 +01:00
Sebastian Stenzel 26b69beb87 prevent updates while having unlocked vaults 2025-11-06 10:08:49 +01:00
Sebastian Stenzel f95bf87a4b reordered getters
[ci skip]
2025-11-06 07:41:30 +01:00
Sebastian Stenzel e854c7d189 detect failed update attempt via new setting 2025-11-05 22:26:45 +01:00
Sebastian Stenzel 8a434dcd96 more robust settings, exposed saveNow() 2025-11-05 22:23:34 +01:00
Sebastian Stenzel 6b7324723e apply more suggestions from code review 2025-11-04 22:07:05 +01:00
Sebastian Stenzel 0bdcb2b3be adjust to type-safe api 2025-11-04 21:42:24 +01:00
Sebastian Stenzel c3931d9d29 apply review suggestions 2025-11-04 21:42:14 +01:00
Sebastian Stenzel 307825a339 fix param order 2025-11-04 11:43:08 +01:00
Sebastian Stenzel 093f0e8c94 cleanup updater 2025-11-04 11:12:43 +01:00
Sebastian Stenzel c938c42c00 unified "check for update" and "do update" button 2025-11-02 15:40:32 +01:00
Jerry PanandGitHub 884c6f6bdd Fix : Shortcut doesn’t close SimpleDialogs (#4026) 2025-10-31 13:40:19 +01:00
Tobias Hagemann c5367db971 Fix Hub vault URL to preserve path component 2025-10-29 15:22:24 +01:00
Sebastian Stenzel 59560193ee adjust to new multi-step update API 2025-10-21 11:32:45 +02:00
Sebastian Stenzel a6b31e19b9 Merge branch 'feature/jdk25' into feature/app-update
# Conflicts:
#	.idea/compiler.xml
#	pom.xml
2025-10-17 17:03:43 +02:00
Sebastian Stenzel 8a44115234 update jacoco to support JDK 25 2025-10-17 15:25:18 +02:00
Sebastian Stenzel 43a1f00bea switch to official openjdk-25-jdk ubuntu package
see https://packages.ubuntu.com/search?suite=jammy&arch=any&searchon=names&keywords=openjdk-25
2025-10-17 12:47:13 +02:00
Sebastian Stenzel 3e458060bc Merge branch 'develop' into feature/jdk25
# Conflicts:
#	.github/workflows/debian.yml
#	.github/workflows/dependency-check.yml
#	.github/workflows/win-exe.yml
#	.idea/misc.xml
#	dist/linux/debian/control
2025-10-17 12:27:28 +02:00
Sebastian Stenzel 4bb5a3f10d fix unresolvable version in setup-java action 2025-10-17 12:12:44 +02:00
Armin Schrenk cdcd43a805 first draft for JDK 25 migration (including comments) 2025-09-25 12:28:12 +02:00
Sebastian Stenzel d5245009f4 Merge branch 'feature/self-update-poc' into feature/app-update
# Conflicts:
#	src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java
#	src/main/resources/fxml/preferences_updates.fxml
2025-08-09 09:26:38 +02:00
Sebastian Stenzel 8f4392711e moved update API to integrations-api 2025-08-08 18:21:42 +02:00
Ralph Plawetzki 79bb4a5215 Correlate with API as suggested in a separate PoC: UpdateMechanism and UpdateProcess 2025-08-02 11:32:07 +02:00
Ralph Plawetzki 2ce7fee06d Log error messages 2025-07-30 19:43:39 +02:00
Ralph Plawetzki 99e9e92f10 Fix progress bar due to JavaFX limitations 2025-07-30 19:21:34 +02:00
Ralph Plawetzki 8e6500d93f Implement update button that updates and respawns the app 2025-07-30 19:21:34 +02:00
Ralph Plawetzki 89ce99deaf Wire UpdateService 2025-07-30 19:21:34 +02:00
Ralph Plawetzki 510e134605 Have multiple services 2025-07-30 19:21:34 +02:00
Ralph Plawetzki 62b434f549 Invent UpdatesModule 2025-07-30 19:21:33 +02:00
Ralph Plawetzki 1f1e336d57 Start FlatpakUpdater 2025-07-30 19:21:33 +02:00
Sebastian Stenzel 02186ca17a hooked up in UI 2025-07-06 20:02:41 +02:00
Sebastian Stenzel b0ed133e05 PoC 2025-07-06 14:28:57 +02:00
112 changed files with 2706 additions and 721 deletions
+4
View File
@@ -20,6 +20,10 @@
Translations are not managed directly in this repository. Instead, we use [Crowdin](https://translate.cryptomator.org/), which automatically synchronizes translations with this repository. If you want to help us with translations, please visit our translation project on Crowdin.
## Use of Generative AI
AI tools may assist your work, but every contribution must be fully understood, reviewed, and tested by you. Only submit changes you can clearly explain and justify. Unverified or low-quality AI output that wastes our time and resources will be closed without further review.
## Code of Conduct
Help us keep Cryptomator open and inclusive. Please read and follow our [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md).
@@ -0,0 +1,76 @@
name: 'Windows Code Signing'
description: 'Sign files on Windows with the Azure Trusted Signing'
inputs:
base-dir:
description: 'Absolute path to the base directory to search for files'
required: true
recursive:
description: 'Whether to search recursively in subdirectories'
required: false
default: 'false'
file-extensions:
description: 'List of file extensions to sign, separated by comma'
required: true
default: 'exe,dll,ps1'
description:
description: 'Signature description'
required: true
default: 'Cryptomator'
url:
description: 'Signature URL'
required: false
default: 'https://cryptomator.org'
append-signature:
description: 'Whether to append the signature to existing signatures'
required: false
default: 'false'
tenant-id:
description: 'Azure Tenant ID'
required: true
client-id:
description: 'Azure Client ID'
required: true
client-secret:
description: 'Azure Client Secret'
required: true
runs:
using: 'composite'
steps:
- name: Generate, mask, and output the input secrets
id: set-secrets
run: |
echo "::add-mask::${{ inputs.tenant-id }}"
echo "::add-mask::${{ inputs.client-id }}"
echo "::add-mask::${{ inputs.client-secret }}"
echo "tenant-id=${{ inputs.tenant-id }}" >> "$GITHUB_OUTPUT"
echo "client-id=${{ inputs.client-id }}" >> "$GITHUB_OUTPUT"
echo "client-secret=${{ inputs.client-secret }}" >> "$GITHUB_OUTPUT"
shell: bash
- name: Sign DLLs with Azure Trusted Signing
uses: azure/trusted-signing-action@fc390cf8ed0f14e248a542af1d838388a47c7a7c # v0.5.10
with:
files-folder: ${{ inputs.base-dir }}
files-folder-filter: ${{ inputs.file-extensions }}
files-folder-recurse: ${{ inputs.recursive }}
append-signature: ${{ inputs.append-signature }}
description: ${{ inputs.description }}
description-url: ${{ inputs.url }}
azure-tenant-id: ${{ steps.set-secrets.outputs.tenant-id }}
azure-client-id: ${{ steps.set-secrets.outputs.client-id }}
azure-client-secret: ${{ steps.set-secrets.outputs.client-secret }}
trusted-signing-account-name: cryptomatorSigning
certificate-profile-name: production
endpoint: https://weu.codesigning.azure.net/
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
exclude-environment-credential: false
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-cli-credential: true
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
+2 -2
View File
@@ -19,7 +19,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '24.0.1+9'
JAVA_VERSION: '25.0.1+8.0.LTS'
jobs:
get-version:
@@ -42,7 +42,7 @@ jobs:
- os: ubuntu-24.04-arm
appimage-suffix: aarch64
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip'
openjfx-sha: '951c52481af0ec5885b06f1ebaa8a10da7e8ea23c5e1ef3e2f6f11fa1b3a7ce1'
openjfx-sha: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4'
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Java
+16 -11
View File
@@ -11,7 +11,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 24
JAVA_VERSION: 25
defaults:
run:
@@ -56,21 +56,21 @@ jobs:
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
generate_release_notes: true
body: |-
:construction: Work in Progress
> [!NOTE]
> 🚧 Work in Progress 🚧
>
> Please be patient, the [builds are still running](https://github.com/cryptomator/cryptomator/actions). Binary packages can be found here in a few moments.
<!--REPLACE with auto-generated release notes (see below)
### What's New 🎉
### Bugfixes 🐛
### Other Changes 📎
END REPLACE-->
Feel free to also read our [CHANGELOG.md](https://github.com/cryptomator/cryptomator/blob/develop/CHANGELOG.md).
---
TODO FULL CHANGELOG
📜 List of closed issues is available [here](TODO)
---
⏳ Please be patient, the builds are still [running](https://github.com/cryptomator/cryptomator/actions). New versions of Cryptomator can be found here in a few moments. ⏳
<!-- Don't forget to include the
💾 SHA-256 checksums of release artifacts:
@@ -78,4 +78,9 @@ jobs:
```
-->
As usual, the GPG signatures can be checked using [our public key `5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
> [!TIP]
> You can verify the GPG signature of all assets using our public key: [`5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
<!-- Auto-Generated Release Notes: -->
+2 -2
View File
@@ -6,7 +6,7 @@ on:
workflow_dispatch:
env:
JDK_VERSION: '24.0.1+9'
JDK_VERSION: '25.0.1+8.0.LTS'
JDK_VENDOR: temurin
RUNTIME_VERSION_HELPER: >
public class Test {
@@ -23,7 +23,7 @@ jobs:
JDK_MAJOR_VERSION: 'toBeFilled'
steps:
- name: Determine current major version
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,20) >> "$env:GITHUB_ENV"
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,2) >> "$env:GITHUB_ENV"
shell: pwsh
- name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }}
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
+7 -6
View File
@@ -23,13 +23,12 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '24.0.1+9'
COFFEELIBS_JDK: 24
COFFEELIBS_JDK_VERSION: '24.0.1+9-0ppa3'
JAVA_VERSION: '25.0.1+8.0.LTS'
DEB_BUILD_DEPENDS: 'debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1'
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip'
OPENJFX_JMODS_AMD64_HASH: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip'
OPENJFX_JMODS_AARCH64_HASH: '951c52481af0ec5885b06f1ebaa8a10da7e8ea23c5e1ef3e2f6f11fa1b3a7ce1'
OPENJFX_JMODS_AARCH64_HASH: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4'
jobs:
get-version:
@@ -55,9 +54,11 @@ jobs:
fi
- 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-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }}
sudo apt-get install devscripts dput
sudo apt-get satisfy "${DEB_BUILD_DEPENDS}"
env:
DEB_BUILD_DEPENDS: ${{ env.DEB_BUILD_DEPENDS }}
- name: Setup Java
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
with:
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
with:
runner-os: 'ubuntu-latest'
java-distribution: 'temurin'
java-version: 24
java-version: 25
secrets:
nvd-api-key: ${{ secrets.NVD_API_KEY }}
ossindex-username: ${{ secrets.OSSINDEX_USERNAME }}
+1 -1
View File
@@ -23,7 +23,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 24
JAVA_VERSION: 25
jobs:
determine-version:
+2 -1
View File
@@ -24,7 +24,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '24.0.1+9'
JAVA_VERSION: '25.0.1+8.0.LTS'
jobs:
get-version:
@@ -136,6 +136,7 @@ jobs:
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources
+2 -1
View File
@@ -22,7 +22,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '24.0.1+9'
JAVA_VERSION: '25.0.1+8.0.LTS'
jobs:
get-version:
@@ -134,6 +134,7 @@ jobs:
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
--mac-package-identifier org.cryptomator
+1 -1
View File
@@ -5,7 +5,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 24
JAVA_VERSION: 25
defaults:
run:
+1 -1
View File
@@ -12,7 +12,7 @@ defaults:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 23
JAVA_VERSION: 25
jobs:
check-preconditions:
+42 -14
View File
@@ -8,10 +8,6 @@ on:
version:
description: 'Version'
required: false
isDebug:
description: 'Build debug version with console output'
type: boolean
default: false
sign:
description: 'Sign binaries'
required: false
@@ -51,8 +47,8 @@ jobs:
include:
- arch: x64
os: windows-latest
java-dist: 'zulu'
java-version: '24.0.1+9'
java-dist: 'zulu' #cannot use temurin, see https://github.com/cryptomator/cryptomator/issues/3824#issuecomment-2829827427
java-version: '25.0.1+8'
java-package: 'jdk'
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
@@ -192,6 +188,16 @@ jobs:
New-Item -Path appdir/jpackage-jmod -ItemType Directory
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
- name: Sign DLLs with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\appdir
recursive: true
append-signature: true
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign DLLs with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f # no specific version
@@ -251,16 +257,16 @@ jobs:
env:
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
- name: Sign msi with Actalis CodeSigner
- name: Sign MSI with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f # no specific version
uses: ./.github/actions/win-sign-action
with:
base-dir: 'installer'
file-extensions: 'msi'
sign-description: 'Cryptomator Installer'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
base-dir: ${{ github.workspace }}\installer
file-extensions: msi
description: 'Cryptomator Installer'
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Add possible alpha/beta tags and architecture to installer name
run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.arch }}.msi
- name: Create detached GPG signature with key 615D449FE6E6A235
@@ -357,6 +363,17 @@ jobs:
- name: Detach burn engine in preparation to sign
run: >
wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe
- name: Sign WiX burn engine with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\tmp
file-extensions: exe
append-signature: true
description: 'Cryptomator Bundle Installer'
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign burn engine with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f # no specific version
@@ -370,6 +387,17 @@ jobs:
- name: Reattach signed burn engine to installer
run: >
wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
- name: Sign EXE installer with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\installer
file-extensions: exe
append-signature: true
description: 'Cryptomator Bundle Installer'
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign installer with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f # no specific version
+11 -10
View File
@@ -14,16 +14,15 @@
<option name="dagger.fastInit" value="enabled" />
<option name="dagger.formatGeneratedSource" value="enabled" />
<processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.55/dagger-compiler-2.55.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.55/dagger-2.55.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.57.2/dagger-compiler-2.57.2.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.57.2/dagger-2.57.2.jar" />
<entry name="$MAVEN_REPOSITORY$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.55/dagger-spi-2.55.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.57.2/dagger-spi-2.57.2.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/2.0.21-1.0.28/symbol-processing-api-2.0.21-1.0.28.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/2.1.21-2.0.2/symbol-processing-api-2.1.21-2.0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/33.0.0-jre/guava-33.0.0-jre.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
@@ -31,14 +30,16 @@
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.23.0/error_prone_annotations-2.23.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/2.8/j2objc-annotations-2.8.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.3/checker-compat-qual-2.5.3.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-metadata-jvm/2.1.21/kotlin-metadata-jvm-2.1.21.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
</processorPath>
<module name="cryptomator" />
</profile>
+3 -5
View File
@@ -1,10 +1,8 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="Deprecation" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="DEPRECATED_ATTRIBUTES" />
<inspection_tool class="MarkedForRemoval" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="MARKED_FOR_REMOVAL_ATTRIBUTES" />
<inspection_tool class="RedundantScheduledForRemovalAnnotation" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="MARKED_FOR_REMOVAL_ATTRIBUTES" />
</profile>
</component>
+1 -1
View File
@@ -8,7 +8,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" project-jdk-name="25" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="25" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+19
View File
@@ -0,0 +1,19 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
The changelog starts with version 1.19.0.
Changes to prior versions can be found on the [Github release page](https://github.com/cryptomator/cryptomator/releases).
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.18.0...HEAD)
### Added
* New Self-Update Mechanism (#3948)
* Implemented `.dmg` update mechanism
* Implemented Flatpak update mechanism
### Changed
* Built using JDK 25 (#4031)
* Modernized Templage for GitHub Releases
@@ -83,6 +83,9 @@
</content_rating>
<releases>
<release date="2025-11-12" version="1.18.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.18.0</url>
</release>
<release date="2025-07-08" version="1.17.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.17.1</url>
</release>
+1 -1
View File
@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils
Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-24 (>= 24.0.1+9-0ppa3), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1
Build-Depends: debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1
Standards-Version: 4.5.0
Homepage: https://cryptomator.org
Vcs-Git: https://github.com/cryptomator/cryptomator.git
+2 -1
View File
@@ -4,11 +4,12 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
JAVA_HOME = /usr/lib/jvm/java-24-coffeelibs
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
ifeq ($(DEB_BUILD_ARCH),amd64)
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-amd64
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
else ifeq ($(DEB_BUILD_ARCH),arm64)
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-arm64
JMODS_PATH = jmods/aarch64:${JAVA_HOME}/jmods
endif
+1
View File
@@ -123,6 +123,7 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support${APP_NAME}/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
--resource-dir ../resources
+3 -3
View File
@@ -1,6 +1,6 @@
Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0|Apache-2.0
MIT License|The MIT License (MIT)|The MIT License|MIT license
LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1
Apache License v2.0|Apache License, Version 2.0|The Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0|Apache-2.0
MIT License|MIT|The MIT License (MIT)|The MIT License|MIT license
LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1|GNU Lesser General Public License
GPLv2|GNU General Public License Version 2
GPLv2+CE|CDDL + GPLv2 with classpath exception
Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0
+20 -6
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.18.0</version>
<version>1.19.0</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -26,7 +26,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>24</project.jdk.version>
<project.jdk.version>25</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now -->
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
@@ -34,10 +34,10 @@
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.9.0</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.7.0</cryptomator.integrations.version>
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.5.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.4.1</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.6.1</cryptomator.integrations.linux.version>
<cryptomator.integrations.mac.version>1.5.0-beta2</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.7.0-beta2</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>5.1.0</cryptomator.fuse.version>
<cryptomator.webdav.version>3.0.0</cryptomator.webdav.version>
@@ -62,7 +62,7 @@
<!-- build-time dependencies -->
<jetbrains.annotations.version>26.0.2-1</jetbrains.annotations.version>
<dependency-check.version>12.1.5</dependency-check.version>
<jacoco.version>0.8.13</jacoco.version>
<jacoco.version>0.8.14</jacoco.version>
<license-generator.version>2.7.0</license-generator.version>
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
<mvn-compiler.version>3.14.1</mvn-compiler.version>
@@ -75,6 +75,20 @@
<surefire.jacoco.args></surefire.jacoco.args>
</properties>
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<!-- Cryptomator Libs -->
<dependency>
+1 -1
View File
@@ -50,12 +50,12 @@ open module org.cryptomator.desktop {
requires io.github.coffeelibs.tinyoauth2client;
requires org.slf4j;
requires org.apache.commons.lang3;
requires com.github.benmanes.caffeine;
/* dagger bs */
requires jakarta.inject;
requires static javax.inject;
requires java.compiler;
requires com.github.benmanes.caffeine;
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
uses SSLContextProvider;
@@ -74,13 +74,6 @@ public abstract class CommonsModule {
return new MasterkeyFileAccess(Constants.PEPPER, csprng);
}
@Provides
@Singleton
@Named("SemVer")
static Comparator<String> providesSemVerComparator() {
return new SemVerComparator();
}
@Provides
@Singleton
static Optional<RevealPathService> provideRevealPathService() {
@@ -124,6 +124,15 @@ public class Environment {
return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME));
}
/**
* Returns the app version concatenated with the build number (if defined).
*
* @return version string formatted like {@code 1.2.3-4567} or {@code 1.2.3} if no build number is defined.
*/
public String getAppVersionWithBuildNumber() {
return getAppVersion() + getBuildNumber().map("-"::concat).orElse("");
}
public Optional<Path> getPluginDir() {
return getPath(PLUGIN_DIR_PROP_NAME);
}
@@ -1,81 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2017 Sebastian Stenzel and others.
* All rights reserved.
* This program and the accompanying materials are made available under the terms of the accompanying LICENSE file.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.common;
import org.apache.commons.lang3.StringUtils;
import java.util.Comparator;
/**
* Compares version strings according to <a href="http://semver.org/spec/v2.0.0.html">SemVer 2.0.0</a>.
*/
public class SemVerComparator implements Comparator<String> {
private static final char VERSION_SEP = '.'; // http://semver.org/spec/v2.0.0.html#spec-item-2
private static final String PRE_RELEASE_SEP = "-"; // http://semver.org/spec/v2.0.0.html#spec-item-9
private static final String BUILD_SEP = "+"; // http://semver.org/spec/v2.0.0.html#spec-item-10
@Override
public int compare(String version1, String version2) {
// "Build metadata SHOULD be ignored when determining version precedence.
// Thus two versions that differ only in the build metadata, have the same precedence."
String v1WithoutBuildMetadata = StringUtils.substringBefore(version1, BUILD_SEP);
String v2WithoutBuildMetadata = StringUtils.substringBefore(version2, BUILD_SEP);
if (v1WithoutBuildMetadata.equals(v2WithoutBuildMetadata)) {
return 0;
}
String v1MajorMinorPatch = StringUtils.substringBefore(v1WithoutBuildMetadata, PRE_RELEASE_SEP);
String v2MajorMinorPatch = StringUtils.substringBefore(v2WithoutBuildMetadata, PRE_RELEASE_SEP);
String v1PreReleaseVersion = StringUtils.substringAfter(v1WithoutBuildMetadata, PRE_RELEASE_SEP);
String v2PreReleaseVersion = StringUtils.substringAfter(v2WithoutBuildMetadata, PRE_RELEASE_SEP);
return compare(v1MajorMinorPatch, v1PreReleaseVersion, v2MajorMinorPatch, v2PreReleaseVersion);
}
private int compare(String v1MajorMinorPatch, String v1PreReleaseVersion, String v2MajorMinorPatch, String v2PreReleaseVersion) {
int comparisonResult = compareNumericallyThenLexicographically(v1MajorMinorPatch, v2MajorMinorPatch);
if (comparisonResult == 0) {
if (v1PreReleaseVersion.isEmpty()) {
return 1; // 1.0.0 > 1.0.0-BETA
} else if (v2PreReleaseVersion.isEmpty()) {
return -1; // 1.0.0-BETA < 1.0.0
} else {
return compareNumericallyThenLexicographically(v1PreReleaseVersion, v2PreReleaseVersion);
}
} else {
return comparisonResult;
}
}
private int compareNumericallyThenLexicographically(String version1, String version2) {
final String[] vComps1 = StringUtils.split(version1, VERSION_SEP);
final String[] vComps2 = StringUtils.split(version2, VERSION_SEP);
final int commonCompCount = Math.min(vComps1.length, vComps2.length);
for (int i = 0; i < commonCompCount; i++) {
int subversionComparisonResult = 0;
try {
final int v1 = Integer.parseInt(vComps1[i]);
final int v2 = Integer.parseInt(vComps2[i]);
subversionComparisonResult = v1 - v2;
} catch (NumberFormatException ex) {
// ok, lets compare this fragment lexicographically
subversionComparisonResult = vComps1[i].compareTo(vComps2[i]);
}
if (subversionComparisonResult != 0) {
return subversionComparisonResult;
}
}
// all in common so far? longest version string is considered the higher version:
return vComps1.length - vComps2.length;
}
}
@@ -25,10 +25,8 @@ import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.NodeOrientation;
import java.nio.file.Path;
import java.time.Instant;
import java.util.function.Consumer;
public class Settings {
@@ -53,6 +51,7 @@ public class Settings {
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
public static final Instant DEFAULT_TIMESTAMP = Instant.parse("2000-01-01T00:00:00Z");
private final SettingsProvider provider;
public final ObservableList<VaultSettings> directories;
public final BooleanProperty startHidden;
public final BooleanProperty autoCloseVaults;
@@ -78,13 +77,12 @@ public class Settings {
public final ObjectProperty<Instant> lastUpdateCheckReminder;
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
public final StringProperty lastUpdateAttemptedByVersion;
private Consumer<Settings> saveCmd;
public static Settings create(Environment env) {
public static Settings create(SettingsProvider provider, Environment env) {
var defaults = new SettingsJson();
defaults.showTrayIcon = env.showTrayIcon();
return new Settings(defaults);
return new Settings(provider, defaults);
}
/**
@@ -92,7 +90,8 @@ public class Settings {
*
* @param json The parsed settings.json
*/
Settings(SettingsJson json) {
Settings(SettingsProvider provider, SettingsJson json) {
this.provider = provider;
this.directories = FXCollections.observableArrayList(VaultSettings::observables);
this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden);
this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults);
@@ -118,6 +117,7 @@ public class Settings {
this.lastUpdateCheckReminder = new SimpleObjectProperty<>(this, "lastUpdateCheckReminder", json.lastReminderForUpdateCheck);
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
this.previouslyUsedVaultDirectory = new SimpleObjectProperty<>(this, "previouslyUsedVaultDirectory", json.previouslyUsedVaultDirectory);
this.lastUpdateAttemptedByVersion = new SimpleStringProperty(this, "lastUpdateAttemptedByVersion", json.lastUpdateAttemptedByVersion);
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
@@ -148,6 +148,7 @@ public class Settings {
lastUpdateCheckReminder.addListener(this::somethingChanged);
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
}
@SuppressWarnings("deprecation")
@@ -210,6 +211,7 @@ public class Settings {
json.lastReminderForUpdateCheck = lastUpdateCheckReminder.get();
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
return json;
}
@@ -222,20 +224,12 @@ public class Settings {
}
}
// TODO rename to setChangeListener
void setSaveCmd(Consumer<Settings> saveCmd) {
this.saveCmd = saveCmd;
}
private void somethingChanged(@SuppressWarnings("unused") Observable observable) {
this.save();
provider.scheduleSave(this);
}
void save() {
if (saveCmd != null) {
saveCmd.accept(this);
}
public void saveNow() {
provider.saveNow(this);
}
}
@@ -96,4 +96,7 @@ class SettingsJson {
@JsonProperty("previouslyUsedVaultDirectory")
Path previouslyUsedVaultDirectory;
@JsonProperty("lastUpdateAttemptedByVersion")
String lastUpdateAttemptedByVersion;
}
@@ -26,7 +26,9 @@ import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -61,8 +63,7 @@ public class SettingsProvider implements Supplier<Settings> {
Settings settings = env.getSettingsPath() //
.flatMap(this::tryLoad) //
.findFirst() //
.orElseGet(() -> Settings.create(env));
settings.setSaveCmd(this::scheduleSave);
.orElseGet(() -> Settings.create(this, env));
return settings;
}
@@ -71,7 +72,7 @@ public class SettingsProvider implements Supplier<Settings> {
try (InputStream in = Files.newInputStream(path, StandardOpenOption.READ)) {
var json = JSON.reader().readValue(in, SettingsJson.class);
LOG.info("Settings loaded from {}", path);
var settings = new Settings(json);
var settings = new Settings(this, json);
return Stream.of(settings);
} catch (JacksonException e) {
LOG.warn("Failed to parse json file {}", path, e);
@@ -84,19 +85,33 @@ public class SettingsProvider implements Supplier<Settings> {
}
}
private void scheduleSave(Settings settings) {
if (settings == null) {
return;
void saveNow(Settings settings) {
try {
scheduleSave(settings, 0L).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Saving settings was interrupted.", e);
} catch (ExecutionException e) {
LOG.error("Unexpected exception while saving.", e);
}
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // always save to preferred (first) path
settingsPath.ifPresent(path -> {
Runnable saveCommand = () -> this.save(settings, path);
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
if (previouslyScheduledTask != null) {
previouslyScheduledTask.cancel(false);
}
});
}
void scheduleSave(Settings settings) {
scheduleSave(settings, SAVE_DELAY_MS);
}
private Future<?> scheduleSave(Settings settings, long delayMillis) {
if (settings == null) {
return CompletableFuture.completedFuture(null);
}
final Path settingsPath = env.getSettingsPath().findFirst().orElseThrow(); // always save to preferred (first) path
Runnable saveCommand = () -> this.save(settings, settingsPath);
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, delayMillis, TimeUnit.MILLISECONDS);
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
if (previouslyScheduledTask != null) {
previouslyScheduledTask.cancel(false);
}
return scheduledTask;
}
private void save(Settings settings, Path settingsPath) {
@@ -107,7 +122,7 @@ public class SettingsProvider implements Supplier<Settings> {
Path tmpPath = settingsPath.resolveSibling(settingsPath.getFileName().toString() + ".tmp");
try (OutputStream out = Files.newOutputStream(tmpPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
var jsonObj = settings.serialized();
jsonObj.writtenByVersion = env.getAppVersion() + env.getBuildNumber().map("-"::concat).orElse("");
jsonObj.writtenByVersion = env.getAppVersionWithBuildNumber();
JSON.writerWithDefaultPrettyPrinter().writeValue(out, jsonObj);
}
Files.move(tmpPath, settingsPath, StandardCopyOption.REPLACE_EXISTING);
@@ -20,7 +20,6 @@ import java.util.ResourceBundle;
public class PasswordStrengthUtil {
private static final int PW_TRUNC_LEN = 100; // truncate very long passwords, since zxcvbn memory and runtime depends vastly on the length
private static final String RESSOURCE_PREFIX = "passwordStrength.messageLabel.";
private static final List<String> SANITIZED_INPUTS = List.of("cryptomator");
private final ResourceBundle resourceBundle;
@@ -48,13 +47,15 @@ public class PasswordStrengthUtil {
}
public String getStrengthDescription(Number score) {
if (score.intValue() == -1) {
return String.format(resourceBundle.getString(RESSOURCE_PREFIX + "tooShort"), minPwLength);
} else if (resourceBundle.containsKey(RESSOURCE_PREFIX + score.intValue())) {
return resourceBundle.getString(RESSOURCE_PREFIX + score.intValue());
} else {
return "";
}
return switch (score.intValue()) {
case -1 -> String.format(resourceBundle.getString("passwordStrength.messageLabel.tooShort"), minPwLength);
case 0 -> resourceBundle.getString("passwordStrength.messageLabel.0");
case 1 -> resourceBundle.getString("passwordStrength.messageLabel.1");
case 2 -> resourceBundle.getString("passwordStrength.messageLabel.2");
case 3 -> resourceBundle.getString("passwordStrength.messageLabel.3");
case 4 -> resourceBundle.getString("passwordStrength.messageLabel.4");
default -> "";
};
}
}
@@ -194,7 +194,7 @@ public class DecryptFileNamesViewController implements FxController {
}
}
//obvservable getter
//observable getter
public ObservableValue<String> dropZoneTextProperty() {
return dropZoneText;
@@ -2,6 +2,7 @@ package org.cryptomator.ui.dialogs;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
@@ -19,19 +20,21 @@ public class Dialogs {
private final ResourceBundle resourceBundle;
private final StageFactory stageFactory;
private final DefaultSceneFactory sceneFactory;
private static final String BUTTON_KEY_CLOSE = "generic.button.close";
@Inject
public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory) {
public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory, DefaultSceneFactory sceneFactory) {
this.resourceBundle = resourceBundle;
this.stageFactory = stageFactory;
this.sceneFactory = sceneFactory;
}
private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class);
private SimpleDialog.Builder createDialogBuilder() {
return new SimpleDialog.Builder(resourceBundle, stageFactory);
return new SimpleDialog.Builder(resourceBundle, stageFactory, sceneFactory);
}
public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) {
@@ -1,5 +1,6 @@
package org.cryptomator.ui.dialogs;
import org.cryptomator.ui.common.DefaultSceneFactory;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.StageFactory;
@@ -36,9 +37,9 @@ public class SimpleDialog {
builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, //
() -> builder.okAction.accept(dialogStage), //
() -> builder.cancelAction.accept(dialogStage)), //
Scene::new, builder.resourceBundle);
builder.sceneFactory, builder.resourceBundle);
dialogStage.setScene(new Scene(loaderFactory.load(FxmlFile.SIMPLE_DIALOG.getRessourcePathString()).getRoot()));
dialogStage.setScene(loaderFactory.createScene(FxmlFile.SIMPLE_DIALOG));
}
public void showAndWait() {
@@ -62,6 +63,7 @@ public class SimpleDialog {
private Stage owner;
private final ResourceBundle resourceBundle;
private final StageFactory stageFactory;
private final DefaultSceneFactory sceneFactory;
private String titleKey;
private String[] titleArgs;
private String messageKey;
@@ -73,9 +75,10 @@ public class SimpleDialog {
private Consumer<Stage> okAction = Stage::close;
private Consumer<Stage> cancelAction = Stage::close;
public Builder(ResourceBundle resourceBundle, StageFactory stageFactory) {
public Builder(ResourceBundle resourceBundle, StageFactory stageFactory, DefaultSceneFactory sceneFactory) {
this.resourceBundle = resourceBundle;
this.stageFactory = stageFactory;
this.sceneFactory = sceneFactory;
}
public Builder setOwner(Stage owner) {
@@ -26,7 +26,7 @@ import javafx.scene.image.Image;
import java.io.IOException;
import java.io.InputStream;
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, //
@Module(subcomponents = {TrayMenuComponent.class, //
DecryptNameComponent.class, //
MainWindowComponent.class, //
PreferencesComponent.class, //
@@ -1,135 +0,0 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.common.Environment;
import org.cryptomator.common.SemVerComparator;
import org.cryptomator.common.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Worker;
import javafx.concurrent.WorkerStateEvent;
import javafx.util.Duration;
import java.time.Instant;
import java.util.Comparator;
@FxApplicationScoped
public class UpdateChecker {
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
private static final Duration AUTO_CHECK_DELAY = Duration.seconds(5);
private final Environment env;
private final Settings settings;
private final StringProperty latestVersion = new SimpleStringProperty();
private final ScheduledService<String> updateCheckerService;
private final ObjectProperty<UpdateCheckState> state = new SimpleObjectProperty<>(UpdateCheckState.NOT_CHECKED);
private final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
private final Comparator<String> versionComparator = new SemVerComparator();
private final BooleanBinding updateAvailable;
private final BooleanBinding checkFailed;
@Inject
UpdateChecker(Settings settings, //
Environment env, //
ScheduledService<String> updateCheckerService) {
this.env = env;
this.settings = settings;
this.updateCheckerService = updateCheckerService;
this.lastSuccessfulUpdateCheck = settings.lastSuccessfulUpdateCheck;
this.updateAvailable = Bindings.createBooleanBinding(this::isUpdateAvailable, latestVersion);
this.checkFailed = Bindings.equal(UpdateCheckState.CHECK_FAILED, state);
}
public void automaticallyCheckForUpdatesIfEnabled() {
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
startCheckingForUpdates(AUTO_CHECK_DELAY);
}
}
public void checkForUpdatesNow() {
startCheckingForUpdates(Duration.ZERO);
}
private void startCheckingForUpdates(Duration initialDelay) {
updateCheckerService.cancel();
updateCheckerService.reset();
updateCheckerService.setDelay(initialDelay);
updateCheckerService.setOnRunning(this::checkStarted);
updateCheckerService.setOnSucceeded(this::checkSucceeded);
updateCheckerService.setOnFailed(this::checkFailed);
updateCheckerService.start();
}
private void checkStarted(WorkerStateEvent event) {
LOG.debug("Checking for updates...");
state.set(UpdateCheckState.IS_CHECKING);
}
private void checkSucceeded(WorkerStateEvent event) {
var latestVersionString = updateCheckerService.getValue();
LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersionString);
lastSuccessfulUpdateCheck.set(Instant.now());
latestVersion.set(latestVersionString);
state.set(UpdateCheckState.CHECK_SUCCESSFUL);
}
private void checkFailed(WorkerStateEvent event) {
state.set(UpdateCheckState.CHECK_FAILED);
}
public enum UpdateCheckState {
NOT_CHECKED,
IS_CHECKING,
CHECK_SUCCESSFUL,
CHECK_FAILED;
}
/* Observable Properties */
public BooleanBinding checkingForUpdatesProperty() {
return updateCheckerService.stateProperty().isEqualTo(Worker.State.RUNNING);
}
public ReadOnlyStringProperty latestVersionProperty() {
return latestVersion;
}
public BooleanBinding updateAvailableProperty() {
return updateAvailable;
}
public BooleanBinding checkFailedProperty() {
return checkFailed;
}
public boolean isUpdateAvailable() {
String currentVersion = getCurrentVersion();
String latestVersionString = latestVersion.get();
if (currentVersion == null || latestVersionString == null) {
return false;
} else {
return versionComparator.compare(currentVersion, latestVersionString) < 0;
}
}
public ObjectProperty<Instant> lastSuccessfulUpdateCheckProperty() {
return lastSuccessfulUpdateCheck;
}
public ObjectProperty<UpdateCheckState> updateCheckStateProperty() {
return state;
}
public String getCurrentVersion() {
return env.getAppVersion();
}
}
@@ -1,93 +0,0 @@
package org.cryptomator.ui.fxapp;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Named;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
@Module
public abstract class UpdateCheckerModule {
private static final Logger LOG = LoggerFactory.getLogger(UpdateCheckerModule.class);
private static final URI LATEST_VERSION_URI = URI.create("https://api.cryptomator.org/desktop/latest-version.json");
private static final Duration UPDATE_CHECK_INTERVAL = Duration.hours(3);
private static final Duration DISABLED_UPDATE_CHECK_INTERVAL = Duration.hours(100000); // Duration.INDEFINITE leads to overflows...
@Provides
@FxApplicationScoped
static Optional<HttpClient> provideHttpClient() {
try {
return Optional.of(HttpClient.newBuilder() //
.followRedirects(HttpClient.Redirect.NORMAL) // from version 1.6.11 onwards, Cryptomator can follow redirects, in case this URL ever changes
.build());
} catch (UncheckedIOException e) {
LOG.error("HttpClient for update check cannot be created.", e);
return Optional.empty();
}
}
@Provides
@FxApplicationScoped
static HttpRequest provideCheckForUpdatesRequest(Environment env) {
String userAgent = String.format("Cryptomator VersionChecker/%s %s %s (%s)", //
env.getAppVersion(), //
SystemUtils.OS_NAME, //
SystemUtils.OS_VERSION, //
SystemUtils.OS_ARCH); //
return HttpRequest.newBuilder() //
.uri(LATEST_VERSION_URI) //
.header("User-Agent", userAgent) //
.timeout(java.time.Duration.ofSeconds(10))
.build();
}
@Provides
@Named("checkForUpdatesInterval")
@FxApplicationScoped
static ObjectBinding<Duration> provideCheckForUpdateInterval(Settings settings) {
return Bindings.when(settings.checkForUpdates).then(UPDATE_CHECK_INTERVAL).otherwise(DISABLED_UPDATE_CHECK_INTERVAL);
}
@Provides
@FxApplicationScoped
static ScheduledService<String> provideCheckForUpdatesService(ExecutorService executor, Optional<HttpClient> httpClient, HttpRequest checkForUpdatesRequest, @Named("checkForUpdatesInterval") ObjectBinding<Duration> period) {
ScheduledService<String> service = new ScheduledService<>() {
@Override
protected Task<String> createTask() {
if (httpClient.isPresent()) {
return new UpdateCheckerTask(httpClient.get(), checkForUpdatesRequest);
} else {
return new Task<>() {
@Override
protected String call() {
throw new NullPointerException("No HttpClient present.");
}
};
}
}
};
service.setOnFailed(event -> LOG.error("Failed to execute update service", service.getException()));
service.setExecutor(executor);
service.periodProperty().bind(period);
return service;
}
}
@@ -1,58 +0,0 @@
package org.cryptomator.ui.fxapp;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.ByteStreams;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.concurrent.Task;
import java.io.IOException;
import java.io.InputStream;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class UpdateCheckerTask extends Task<String> {
private static final ObjectMapper JSON = new ObjectMapper();
private static final Logger LOG = LoggerFactory.getLogger(UpdateCheckerTask.class);
private static final long MAX_RESPONSE_SIZE = 10L * 1024; // 10kb should be sufficient. protect against flooding
private final HttpClient httpClient;
private final HttpRequest checkForUpdatesRequest;
UpdateCheckerTask(HttpClient httpClient, HttpRequest checkForUpdatesRequest) {
this.httpClient = httpClient;
this.checkForUpdatesRequest = checkForUpdatesRequest;
setOnFailed(event -> LOG.error("Failed to check for updates", getException()));
}
@Override
protected String call() throws IOException, InterruptedException {
HttpResponse<InputStream> response = httpClient.send(checkForUpdatesRequest, HttpResponse.BodyHandlers.ofInputStream());
if (response.statusCode() == 200) {
return processBody(response);
} else {
throw new IOException("Unexpected HTTP response code " + response.statusCode());
}
}
private String processBody(HttpResponse<InputStream> response) throws IOException {
try (InputStream in = response.body(); //
InputStream limitedIn = ByteStreams.limit(in, MAX_RESPONSE_SIZE)) {
var json = JSON.reader().readTree(limitedIn);
if (SystemUtils.IS_OS_MAC_OSX) {
return json.get("mac").asText();
} else if (SystemUtils.IS_OS_WINDOWS) {
return json.get("win").asText();
} else if (SystemUtils.IS_OS_LINUX) {
return json.get("linux").asText();
} else {
throw new IllegalStateException("Unsupported operating system");
}
}
}
}
@@ -7,7 +7,7 @@ import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.fxapp.UpdateChecker;
import org.cryptomator.updater.UpdateChecker;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -3,7 +3,7 @@ package org.cryptomator.ui.preferences;
import com.google.common.io.CharStreams;
import org.cryptomator.common.Environment;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.UpdateChecker;
import org.cryptomator.updater.UpdateChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -2,7 +2,7 @@ package org.cryptomator.ui.preferences;
import org.cryptomator.common.Environment;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.UpdateChecker;
import org.cryptomator.updater.UpdateChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -2,25 +2,37 @@ package org.cryptomator.ui.preferences;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.update.UpdateStep;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.UpdateChecker;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.updater.UpdateChecker;
import org.cryptomator.updater.FallbackUpdateInfo;
import org.cryptomator.updater.UpdateService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.BooleanExpression;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.concurrent.Worker;
import javafx.concurrent.WorkerStateEvent;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
@@ -34,73 +46,64 @@ import java.util.ResourceBundle;
@PreferencesScoped
public class UpdatesPreferencesController implements FxController {
private static final String DOWNLOADS_URI_TEMPLATE = "https://cryptomator.org/downloads/" //
+ "?utm_source=cryptomator-desktop" //
+ "&utm_medium=update-notification&" //
+ "utm_campaign=app-update-%s";
private static final Logger LOG = LoggerFactory.getLogger(UpdatesPreferencesController.class);
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
private final Application application;
private final Environment environment;
private final ResourceBundle resourceBundle;
private final Settings settings;
private final UpdateChecker updateChecker;
private final ObjectBinding<ContentDisplay> checkForUpdatesButtonState;
private final ReadOnlyStringProperty latestVersion;
private final ObservableValue<Instant> lastSuccessfulUpdateCheck;
private final StringBinding lastUpdateCheckMessage;
private final UpdateService updateService;
private final ObservableList<Vault> unlockedVaults;
private final VaultService vaultService;
private final ObjectBinding<Worker<?>> worker;
private final BooleanExpression running;
private final StringBinding updateButtonTitle;
private final ObjectBinding<ContentDisplay> updateButtonState;
private final ObservableValue<String> timeDifferenceMessage;
private final String currentVersion;
private final BooleanBinding updateAvailable;
private final BooleanBinding checkFailed;
private final StringBinding lastUpdateCheckMessage;
private final BooleanBinding prohibitUpdateWhileUnlocked;
private final BooleanBinding updateButtonDisabled;
private final StringProperty errorMessage = new SimpleStringProperty("");
private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false);
private final DateTimeFormatter formatter;
private final BooleanBinding upToDate;
private final String downloadsUri;
/* FXML */
public CheckBox checkForUpdatesCheckbox;
@Inject
UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker) {
UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
this.application = application;
this.environment = environment;
this.resourceBundle = resourceBundle;
this.settings = settings;
this.updateChecker = updateChecker;
this.checkForUpdatesButtonState = Bindings.when(updateChecker.checkingForUpdatesProperty()).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY);
this.latestVersion = updateChecker.latestVersionProperty();
this.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty();
this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck);
this.currentVersion = environment.getAppVersion();
this.updateAvailable = updateChecker.updateAvailableProperty();
this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion));
this.checkFailed = updateChecker.checkFailedProperty();
this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck);
this.downloadsUri = DOWNLOADS_URI_TEMPLATE.formatted(URLEncoder.encode(currentVersion, StandardCharsets.US_ASCII));
this.updateService = new UpdateService(updateChecker.updateProperty());
this.unlockedVaults = vaults.filtered(Vault::isUnlocked);
this.vaultService = vaultService;
this.worker = Bindings.when(updateChecker.updateAvailableProperty()).<Worker<?>>then(this.updateService).otherwise(this.updateChecker);
this.running = Bindings.createBooleanBinding(this::isRunning, updateService.stateProperty(), updateChecker.stateProperty());
this.updateButtonTitle = Bindings.createStringBinding(this::getUpdateButtonTitle, worker, updateService.stateProperty(), updateService.messageProperty());
this.updateButtonState = Bindings.createObjectBinding(this::getUpdateButtonState, updateChecker.stateProperty(), updateService.stateProperty());
this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, updateChecker.lastSuccessfulUpdateCheckProperty());
this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, updateChecker.lastSuccessfulUpdateCheckProperty());
this.prohibitUpdateWhileUnlocked = Bindings.createBooleanBinding(this::isProhibitUpdateWhileUnlocked, unlockedVaults, updateChecker.updateProperty());
this.updateButtonDisabled = Bindings.when(worker.isEqualTo(updateChecker)).then(running).otherwise(prohibitUpdateWhileUnlocked.or(running));
}
public void initialize() {
checkForUpdatesCheckbox.selectedProperty().bindBidirectional(settings.checkForUpdates);
upToDate.addListener((_, _, newVal) -> {
if (newVal) {
updateChecker.updateAvailableProperty().addListener((_, _, hasUpdate) -> {
if (!hasUpdate) {
upToDateLabelVisible.set(true);
PauseTransition delay = new PauseTransition(javafx.util.Duration.seconds(5));
delay.setOnFinished(_ -> upToDateLabelVisible.set(false));
delay.play();
}
});
}
@FXML
public void checkNow() {
updateChecker.checkForUpdatesNow();
}
@FXML
public void visitDownloadsPage() {
application.getHostServices().showDocument(downloadsUri);
updateChecker.setOnFailed(this::checkFailed);
updateService.setOnSucceeded(this::updateSucceeded);
updateService.setOnFailed(this::updateFailed);
}
@FXML
@@ -108,38 +111,104 @@ public class UpdatesPreferencesController implements FxController {
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
}
@FXML
public void startWork() {
if (worker.get().equals(updateChecker)) {
updateChecker.checkForUpdatesNow();
} else if (!unlockedVaults.isEmpty()) {
LOG.warn("Cannot start update due to unlocked vaults.");
} else if (worker.get().equals(updateService)) {
LOG.info("User started update to version {}", updateChecker.getUpdate().version());
updateService.start();
}
}
private void checkFailed(WorkerStateEvent workerStateEvent) {
assert workerStateEvent.getSource() == updateChecker;
LOG.error("Update check failed.", updateChecker.getException());
errorMessage.set(resourceBundle.getString("preferences.updates.checkFailed"));
}
private void updateSucceeded(WorkerStateEvent workerStateEvent) {
assert workerStateEvent.getSource() == updateService;
var lastStep = updateService.getValue();
if (lastStep == UpdateStep.EXIT) {
// Record that this version attempted an update, so next launch can choose fallback if needed
settings.lastUpdateAttemptedByVersion.set(environment.getAppVersionWithBuildNumber());
settings.saveNow();
LOG.info("Exiting app to update...");
Platform.exit();
} else if (lastStep == UpdateStep.RETRY) {
updateService.reset();
} else {
LOG.info("Update succeeded.");
}
}
private void updateFailed(WorkerStateEvent workerStateEvent) {
assert workerStateEvent.getSource() == updateService;
LOG.error("Update failed.", updateService.getException());
updateService.reset();
errorMessage.set(resourceBundle.getString("preferences.updates.updateFailed"));
// try fallback mechanism:
updateChecker.recheckWithFallbackMechanism();
}
@FXML
public void lockAllGracefully() {
vaultService.lockAll(unlockedVaults, false);
}
/* Observable Properties */
public ObjectBinding<ContentDisplay> checkForUpdatesButtonStateProperty() {
return checkForUpdatesButtonState;
public UpdateChecker getUpdateChecker() {
return updateChecker;
}
public ContentDisplay getCheckForUpdatesButtonState() {
return checkForUpdatesButtonState.get();
public ObjectBinding<Worker<?>> workerProperty() {
return worker;
}
public ReadOnlyStringProperty latestVersionProperty() {
return latestVersion;
public Worker<?> getWorker() {
return worker.get();
}
public String getLatestVersion() {
return latestVersion.get();
public BooleanExpression runningProperty() {
return running;
}
public String getCurrentVersion() {
return currentVersion;
public boolean isRunning() {
return updateChecker.getState() == Worker.State.RUNNING || updateService.getState() == Worker.State.RUNNING;
}
public StringBinding lastUpdateCheckMessageProperty() {
return lastUpdateCheckMessage;
public StringBinding updateButtonTitleProperty() {
return updateButtonTitle;
}
public String getLastUpdateCheckMessage() {
Instant lastCheck = lastSuccessfulUpdateCheck.getValue();
if (lastCheck != null && !lastCheck.equals(Settings.DEFAULT_TIMESTAMP)) {
return formatter.format(LocalDateTime.ofInstant(lastCheck, ZoneId.systemDefault()));
public String getUpdateButtonTitle() {
if (worker.get() == updateChecker) {
return resourceBundle.getString("preferences.updates.checkNowBtn");
} else {
return "-";
return switch (updateService.getState()) {
case READY -> updateChecker.getUpdate().updateMechanism().getName();
case SCHEDULED, RUNNING -> updateService.getMessage();
case SUCCEEDED -> resourceBundle.getString("generic.button.done");
case FAILED, CANCELLED -> "failed"; // should never be visible
};
}
}
public ObjectBinding<ContentDisplay> updateButtonStateProperty() {
return updateButtonState;
}
public ContentDisplay getUpdateButtonState() {
if (updateService.isRunning()) { // isRunning() covers RUNNING and SCHEDULED states
return ContentDisplay.BOTTOM;
} else if (updateChecker.getState() == Worker.State.RUNNING) {
return ContentDisplay.LEFT;
} else {
return ContentDisplay.TEXT_ONLY;
}
}
@@ -148,7 +217,7 @@ public class UpdatesPreferencesController implements FxController {
}
public String getTimeDifferenceMessage() {
var lastSuccessCheck = lastSuccessfulUpdateCheck.getValue();
var lastSuccessCheck = updateChecker.getLastSuccessfulUpdateCheck();
var duration = Duration.between(lastSuccessCheck, Instant.now());
var hours = duration.toHours();
if (lastSuccessCheck.equals(Settings.DEFAULT_TIMESTAMP)) {
@@ -162,6 +231,44 @@ public class UpdatesPreferencesController implements FxController {
}
}
public StringBinding lastUpdateCheckMessageProperty() {
return lastUpdateCheckMessage;
}
public String getLastUpdateCheckMessage() {
Instant lastCheck = updateChecker.getLastSuccessfulUpdateCheck();
if (lastCheck != null && !lastCheck.equals(Settings.DEFAULT_TIMESTAMP)) {
return FORMATTER.format(LocalDateTime.ofInstant(lastCheck, ZoneId.systemDefault()));
} else {
return "-";
}
}
public String getErrorMessage() {
return errorMessage.get();
}
public ReadOnlyStringProperty errorMessageProperty() {
return errorMessage;
}
public boolean isProhibitUpdateWhileUnlocked() {
// If the result of the last update check was from the fallback mechanism, we don't need to show the warning
return !unlockedVaults.isEmpty() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate());
}
public BooleanBinding prohibitUpdateWhileUnlockedProperty() {
return prohibitUpdateWhileUnlocked;
}
public boolean isUpdateButtonDisabled() {
return updateButtonDisabled.get();
}
public BooleanBinding updateButtonDisabledProperty() {
return updateButtonDisabled;
}
public BooleanProperty upToDateLabelVisibleProperty() {
return upToDateLabelVisible;
}
@@ -170,20 +277,4 @@ public class UpdatesPreferencesController implements FxController {
return upToDateLabelVisible.get();
}
public BooleanBinding updateAvailableProperty() {
return updateAvailable;
}
public boolean isUpdateAvailable() {
return updateAvailable.get();
}
public BooleanBinding checkFailedProperty() {
return checkFailed;
}
public boolean isCheckFailed() {
return checkFailed.getValue();
}
}
@@ -58,9 +58,15 @@ public class ShareVaultController implements FxController {
private static URI getHubUri(Vault vault) {
try {
var keyID = new URI(vault.getVaultConfigCache().get().getKeyId().toString());
assert keyID.getScheme().startsWith(SCHEME_PREFIX);
return new URI(keyID.getScheme().substring(SCHEME_PREFIX.length()) + "://" + keyID.getHost() + "/app/vaults");
var keyId = new URI(vault.getVaultConfigCache().get().getKeyId().toString());
assert keyId.getScheme().startsWith(SCHEME_PREFIX);
var path = keyId.getPath();
var apiIdx = path.indexOf("/api/");
if (apiIdx < 0) {
throw new IllegalArgumentException("Path does not contain /api/: " + path);
}
var appPath = path.substring(0, apiIdx) + "/app/vaults";
return new URI(keyId.getScheme().substring(SCHEME_PREFIX.length()), keyId.getAuthority(), appPath, null, null);
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (URISyntaxException e) {
@@ -2,7 +2,7 @@ package org.cryptomator.ui.updatereminder;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.UpdateChecker;
import org.cryptomator.updater.UpdateChecker;
import javax.inject.Inject;
import javafx.fxml.FXML;
@@ -0,0 +1,86 @@
package org.cryptomator.updater;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import java.io.IOException;
import java.net.Authenticator;
import java.net.CookieHandler;
import java.net.ProxySelector;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
abstract class DelegatingHttpClient extends HttpClient {
private final HttpClient delegate;
public DelegatingHttpClient(HttpClient delegate) {
this.delegate = Objects.requireNonNull(delegate, "delegate must not be null");
}
@Override
public Optional<CookieHandler> cookieHandler() {
return delegate.cookieHandler();
}
@Override
public Optional<Duration> connectTimeout() {
return delegate.connectTimeout();
}
@Override
public Redirect followRedirects() {
return delegate.followRedirects();
}
@Override
public Optional<ProxySelector> proxy() {
return delegate.proxy();
}
@Override
public SSLContext sslContext() {
return delegate.sslContext();
}
@Override
public SSLParameters sslParameters() {
return delegate.sslParameters();
}
@Override
public Optional<Authenticator> authenticator() {
return delegate.authenticator();
}
@Override
public Version version() {
return delegate.version();
}
@Override
public Optional<Executor> executor() {
return delegate.executor();
}
@Override
public <T> HttpResponse<T> send(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) throws IOException, InterruptedException {
return delegate.send(request, responseBodyHandler);
}
@Override
public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) {
return delegate.sendAsync(request, responseBodyHandler);
}
@Override
public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler, HttpResponse.PushPromiseHandler<T> pushPromiseHandler) {
return delegate.sendAsync(request, responseBodyHandler, pushPromiseHandler);
}
}
@@ -0,0 +1,6 @@
package org.cryptomator.updater;
import org.cryptomator.integrations.update.UpdateInfo;
import org.cryptomator.integrations.update.UpdateMechanism;
public record FallbackUpdateInfo(String version, UpdateMechanism<FallbackUpdateInfo> updateMechanism) implements UpdateInfo<FallbackUpdateInfo> {}
@@ -0,0 +1,105 @@
package org.cryptomator.updater;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import org.cryptomator.integrations.common.LocalizedDisplayName;
import org.cryptomator.integrations.update.UpdateMechanism;
import org.cryptomator.integrations.update.UpdateStep;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.application.Platform;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
@FxApplicationScoped
@LocalizedDisplayName(bundle = "i18n.strings", key = "preferences.updates.visitDownloadPage")
public class FallbackUpdateMechanism implements UpdateMechanism<FallbackUpdateInfo> {
private static final Logger LOG = LoggerFactory.getLogger(FallbackUpdateMechanism.class);
private static final String LATEST_VERSION_API_URL = "https://api.cryptomator.org/connect/apps/desktop/latest-version";
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String DOWNLOADS_URI_TEMPLATE = "https://cryptomator.org/downloads/" //
+ "?utm_source=cryptomator-desktop" //
+ "&utm_medium=update-notification&" //
+ "utm_campaign=app-update-%s";
private final Application app;
private final Environment env;
@Inject
public FallbackUpdateMechanism(Application app, Environment env) {
this.app = app;
this.env = env;
}
@Override
public FallbackUpdateInfo checkForUpdate(String currentVersion, HttpClient httpClient) {
try {
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(LATEST_VERSION_API_URL)).build();
HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
if (response.statusCode() != 200) {
throw new RuntimeException("Failed to fetch release: " + response.statusCode());
}
var release = MAPPER.readValue(response.body(), LatestVersion.class);
var updateVersion = release.versionForCurrentOS();
if (UpdateMechanism.isUpdateAvailable(updateVersion, currentVersion)) {
return new FallbackUpdateInfo(updateVersion, this);
} else {
return null;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.warn("Update check interrupted", e);
return null;
} catch (IOException e) {
LOG.warn("Update check failed", e);
return null;
}
}
@Override
public UpdateStep firstStep(FallbackUpdateInfo updateInfo) {
return UpdateStep.of("Go to download page", this::openDownloadPage); // TODO localize
}
private UpdateStep openDownloadPage() {
var downloadUrl = DOWNLOADS_URI_TEMPLATE.formatted(URLEncoder.encode(env.getAppVersion(), StandardCharsets.US_ASCII));
Platform.runLater(() -> {
app.getHostServices().showDocument(downloadUrl);
});
return UpdateStep.RETRY; // allow running this "update mechanism" as many times as the user wants
}
@JsonIgnoreProperties(ignoreUnknown = true)
public record LatestVersion(
@JsonProperty("mac") String macVersion,
@JsonProperty("win") String winVersion,
@JsonProperty("linux") String linuxVersion
) {
public String versionForCurrentOS() {
if (SystemUtils.IS_OS_MAC_OSX) {
return macVersion;
} else if (SystemUtils.IS_OS_WINDOWS) {
return winVersion;
} else if (SystemUtils.IS_OS_LINUX) {
return linuxVersion;
} else {
throw new IllegalStateException("Unsupported operating system");
}
}
}
}
@@ -0,0 +1,198 @@
package org.cryptomator.updater;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.update.UpdateFailedException;
import org.cryptomator.integrations.update.UpdateInfo;
import org.cryptomator.integrations.update.UpdateMechanism;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.binding.StringExpression;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;
import java.time.Instant;
import java.util.concurrent.Executors;
@FxApplicationScoped
public class UpdateChecker extends ScheduledService<UpdateInfo<?>> {
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
private static final Duration AUTO_CHECK_DELAY = Duration.seconds(5);
private static final Duration UPDATE_CHECK_INTERVAL = Duration.hours(3);
private static final Duration DISABLED_UPDATE_CHECK_INTERVAL = Duration.hours(100000); // Duration.INDEFINITE leads to overflows...
public enum UpdateCheckState {
NOT_CHECKED,
IS_CHECKING,
CHECK_SUCCESSFUL,
CHECK_FAILED
}
private final Environment env;
private final Settings settings;
private final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
private final ObjectProperty<UpdateInfo<?>> update = new SimpleObjectProperty<>();
private final StringExpression latestVersion = StringExpression.stringExpression(update.map(UpdateInfo::version));
private final BooleanBinding updateAvailable = update.isNotNull();
private final ObjectBinding<UpdateCheckState> updateState = Bindings.createObjectBinding(this::getUpdateCheckState, stateProperty());
private final BooleanBinding checkFailed = Bindings.equal(UpdateCheckState.CHECK_FAILED, updateState);
private final UpdateMechanism<?> fallbackUpdateMechanism;
private UpdateMechanism<?> updateMechanism;
@Inject
UpdateChecker(Settings settings, //
Environment env,
FallbackUpdateMechanism fallbackUpdateMechanism) {
this.env = env;
this.settings = settings;
this.lastSuccessfulUpdateCheck = settings.lastSuccessfulUpdateCheck;
this.fallbackUpdateMechanism = fallbackUpdateMechanism;
// Prefer the safer fallback mechanism if the last update attempt was already made by this app version
var currentVersion = env.getAppVersionWithBuildNumber();
var lastAttemptedBy = settings.lastUpdateAttemptedByVersion.get();
if (currentVersion != null && currentVersion.equals(lastAttemptedBy)) {
this.updateMechanism = fallbackUpdateMechanism; // immediately use fallback mechanism
} else {
this.updateMechanism = UpdateMechanism.get().orElse(fallbackUpdateMechanism);
}
setExecutor(Executors.newVirtualThreadPerTaskExecutor());
periodProperty().bind(Bindings.when(settings.checkForUpdates).then(UPDATE_CHECK_INTERVAL).otherwise(DISABLED_UPDATE_CHECK_INTERVAL));
}
public void automaticallyCheckForUpdatesIfEnabled() {
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
startCheckingForUpdates(AUTO_CHECK_DELAY);
}
}
public void recheckWithFallbackMechanism() {
if (updateMechanism == fallbackUpdateMechanism) {
return; // already using fallback mechanism
}
updateMechanism = fallbackUpdateMechanism;
checkForUpdatesNow();
}
public void checkForUpdatesNow() {
startCheckingForUpdates(Duration.ZERO);
}
private void startCheckingForUpdates(Duration initialDelay) {
cancel();
reset();
setDelay(initialDelay);
start();
}
@Override
protected void succeeded() {
var updateInfo = getValue();
super.succeeded(); // this will nil the value property!
lastSuccessfulUpdateCheck.set(Instant.now());
if (updateInfo != null) {
LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), updateInfo.version());
update.set(updateInfo);
}
}
@Override
protected Task<UpdateInfo<?>> createTask() {
return new UpdateCheckTask();
}
/* Observable Properties */
public UpdateInfo<?> getUpdate() {
return update.get();
}
public ObjectProperty<UpdateInfo<?>> updateProperty() {
return update;
}
public String getLatestVersion() {
return latestVersion.get();
}
public StringExpression latestVersionProperty() {
return latestVersion;
}
public boolean isUpdateAvailable() {
return updateAvailable.get();
}
public BooleanBinding updateAvailableProperty() {
return updateAvailable;
}
public boolean isCheckFailed() {
return checkFailed.get();
}
public BooleanBinding checkFailedProperty() {
return checkFailed;
}
public Instant getLastSuccessfulUpdateCheck() {
return lastSuccessfulUpdateCheck.get();
}
public ObjectProperty<Instant> lastSuccessfulUpdateCheckProperty() {
return lastSuccessfulUpdateCheck;
}
public ObjectBinding<UpdateCheckState> updateCheckStateProperty() {
return updateState;
}
private UpdateCheckState getUpdateCheckState() {
return switch (getState()) {
case READY -> UpdateCheckState.NOT_CHECKED;
case SCHEDULED, RUNNING -> UpdateCheckState.IS_CHECKING;
case SUCCEEDED -> UpdateCheckState.CHECK_SUCCESSFUL;
case FAILED, CANCELLED -> UpdateCheckState.CHECK_FAILED;
};
}
public String getCurrentVersion() {
return env.getAppVersion();
}
private class UpdateCheckTask extends Task<UpdateInfo<?>> {
@Override
protected UpdateInfo<?> call() {
try (var httpClient = new UpdateCheckerHttpClient(env)) {
var result = updateMechanism.checkForUpdate(env.getAppVersion(), httpClient);
if (result != null) {
return result;
}
} catch (UpdateFailedException e) {
LOG.error("Update check using {} failed.", updateMechanism.getClass(), e);
}
if (updateMechanism == fallbackUpdateMechanism) {
return null;
}
LOG.debug("Trying fallback update check...");
try (var httpClient = new UpdateCheckerHttpClient(env)) {
return fallbackUpdateMechanism.checkForUpdate(env.getAppVersion(), httpClient);
} catch (UpdateFailedException e) {
LOG.error("Fallback update check failed.", e);
return null;
}
}
}
}
@@ -0,0 +1,50 @@
package org.cryptomator.updater;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Environment;
import java.io.IOException;
import java.net.ProxySelector;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
public class UpdateCheckerHttpClient extends DelegatingHttpClient {
private final String userAgent;
public UpdateCheckerHttpClient(Environment env) {
var delegate = HttpClient.newBuilder() //
.followRedirects(HttpClient.Redirect.NORMAL) // from version 1.6.11 onwards, Cryptomator can follow redirects, in case this URL ever changes
.proxy(ProxySelector.getDefault()).build();
super(delegate);
this.userAgent = String.format("Cryptomator VersionChecker/%s %s %s (%s)", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
}
@Override
public <T> HttpResponse<T> send(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) throws IOException, InterruptedException {
return super.send(decorateRequest(request), responseBodyHandler);
}
@Override
public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) {
return super.sendAsync(decorateRequest(request), responseBodyHandler);
}
@Override
public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler, HttpResponse.PushPromiseHandler<T> pushPromiseHandler) {
return super.sendAsync(decorateRequest(request), responseBodyHandler, pushPromiseHandler);
}
private HttpRequest decorateRequest(HttpRequest request) {
return HttpRequest.newBuilder(request, (_, _) -> true) //
.header("User-Agent", this.userAgent) //
.timeout(Duration.ofSeconds(10)) //
.build();
}
}
@@ -0,0 +1,81 @@
package org.cryptomator.updater;
import org.cryptomator.integrations.update.UpdateInfo;
import org.cryptomator.integrations.update.UpdateMechanism;
import org.cryptomator.integrations.update.UpdateStep;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* A service that performs all update steps provided by the given {@link UpdateMechanism} in sequence.
*/
public class UpdateService extends Service<UpdateStep> {
private final BooleanBinding updateFailed = Bindings.equal(State.FAILED, stateProperty());
private ObservableValue<UpdateInfo<?>> updateInfo;
public UpdateService(ObservableValue<UpdateInfo<?>> updateInfo) {
setExecutor(Executors.newVirtualThreadPerTaskExecutor());
this.updateInfo = updateInfo;
}
@Override
protected Task<UpdateStep> createTask() {
return new RunAllStepsTask(updateInfo.getValue());
}
private static class RunAllStepsTask extends Task<UpdateStep> {
@SuppressWarnings("rawtypes")
private final UpdateInfo updateInfo;
public RunAllStepsTask(UpdateInfo<?> updateInfo) {
this.updateInfo = Objects.requireNonNull(updateInfo);
}
@Override
protected UpdateStep call() throws IOException {
try {
UpdateStep step = updateInfo.useToPrepareFirstStep();
UpdateStep lastStep;
do {
step.start();
observeAndWaitFor(step);
lastStep = step;
step = step.nextStep();
} while (step != null);
return lastStep;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new InterruptedIOException("Update interrupted");
}
}
private void observeAndWaitFor(UpdateStep step) throws InterruptedException {
do {
updateProgress(step.preparationProgress(), 1.0);
updateMessage(step.description());
} while (!step.await(100, TimeUnit.MILLISECONDS));
}
}
/* Observable Properties */
public boolean isUpdateFailed() {
return updateFailed.get();
}
public BooleanBinding updateFailedProperty() {
return updateFailed;
}
}
@@ -3,55 +3,73 @@
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import org.cryptomator.ui.controls.FontAwesome5Spinner?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import org.cryptomator.ui.controls.FormattedString?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.text.TextFlow?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.text.TextFlow?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.preferences.UpdatesPreferencesController"
spacing="12">
<fx:define>
<FormattedString fx:id="linkLabel" format="%preferences.updates.updateAvailable" arg1="${controller.latestVersion}"/>
</fx:define>
<padding>
<Insets topRightBottomLeft="24"/>
</padding>
<FormattedLabel format="%preferences.updates.currentVersion" arg1="${controller.currentVersion}" textAlignment="CENTER" wrapText="true"/>
<FormattedLabel format="%preferences.updates.currentVersion" arg1="${controller.updateChecker.currentVersion}" textAlignment="CENTER" wrapText="true"/>
<CheckBox fx:id="checkForUpdatesCheckbox" text="%preferences.updates.autoUpdateCheck"/>
<VBox alignment="CENTER" spacing="12">
<Button text="%preferences.updates.checkNowBtn" defaultButton="true" onAction="#checkNow" contentDisplay="${controller.checkForUpdatesButtonState}">
<FormattedLabel format="%preferences.updates.updateAvailable" arg1="${controller.updateChecker.latestVersion}" textAlignment="CENTER" wrapText="true" visible="${controller.updateChecker.updateAvailable}"/>
<Button text="${controller.updateButtonTitle}" defaultButton="true" onAction="#startWork" disable="${controller.updateButtonDisabled}" contentDisplay="${controller.updateButtonState}">
<graphic>
<FontAwesome5Spinner glyphSize="12"/>
<VBox spacing="5" alignment="CENTER">
<ProgressBar maxWidth="200"
maxHeight="12"
visible="${controller.running &amp;&amp; controller.worker.progress != -1.0}"
managed="${controller.running &amp;&amp; controller.worker.progress != -1.0}"
progress="${controller.worker.progress}"/>
<FontAwesome5Spinner glyphSize="12"
visible="${controller.running &amp;&amp; controller.worker.progress == -1.0}"
managed="${controller.running &amp;&amp; controller.worker.progress == -1.0}"/>
</VBox>
</graphic>
</Button>
<TextFlow styleClass="text-flow" textAlignment="CENTER" visible="${controller.checkFailed}" managed="${controller.checkFailed}">
<TextFlow styleClass="text-flow" textAlignment="CENTER" visible="${controller.prohibitUpdateWhileUnlocked}" managed="${controller.prohibitUpdateWhileUnlocked}">
<FontAwesome5IconView glyphSize="12" styleClass="glyph-icon-primary" glyph="LOCK_OPEN"/>
<Text text=" "/>
<Text text="%preferences.updates.prohibitedDueToUnlockedVaults.1"/>
<Text text=" "/>
<Hyperlink styleClass="hyperlink-underline" text="%preferences.updates.prohibitedDueToUnlockedVaults.2" onAction="#lockAllGracefully"/>
<Text text=" "/>
<Text text="%preferences.updates.prohibitedDueToUnlockedVaults.3"/>
</TextFlow>
<TextFlow styleClass="text-flow" textAlignment="CENTER" visible="${!controller.errorMessage.empty}" managed="${!controller.errorMessage.empty}">
<FontAwesome5IconView glyphSize="12" styleClass="glyph-icon-orange" glyph="EXCLAMATION_TRIANGLE"/>
<Text text=" "/>
<Text text="%preferences.updates.checkFailed"/>
<Text text="${controller.errorMessage}"/>
<Text text=" "/>
<Hyperlink styleClass="hyperlink-underline" text="%preferences.general.debugDirectory" onAction="#showLogfileDirectory"/>
</TextFlow>
<FormattedLabel format="%preferences.updates.lastUpdateCheck" arg1="${controller.timeDifferenceMessage}" textAlignment="CENTER" wrapText="true">
<FormattedLabel format="%preferences.updates.lastUpdateCheck" arg1="${controller.timeDifferenceMessage}" textAlignment="CENTER" wrapText="true" visible="${!controller.updateChecker.updateAvailable}" managed="${!controller.updateChecker.updateAvailable}">
<tooltip>
<Tooltip text="${controller.lastUpdateCheckMessage}" showDelay="10ms"/>
</tooltip>
</FormattedLabel>
<Label text="%preferences.updates.upToDate" visible="${controller.upToDateLabelVisible}" managed="${controller.upToDateLabelVisible}">
<graphic>
<FontAwesome5IconView glyphSize="12" styleClass="glyph-icon-primary" glyph="CHECK"/>
</graphic>
</Label>
<Hyperlink text="${linkLabel.value}" onAction="#visitDownloadsPage" textAlignment="CENTER" wrapText="true" styleClass="hyperlink-underline" visible="${controller.updateAvailable}" managed="${controller.updateAvailable}"/>
</VBox>
</VBox>
+7 -2
View File
@@ -330,8 +330,13 @@ preferences.updates.lastUpdateCheck.never=never
preferences.updates.lastUpdateCheck.recently=recently
preferences.updates.lastUpdateCheck.daysAgo=%s days ago
preferences.updates.lastUpdateCheck.hoursAgo=%s hours ago
preferences.updates.prohibitedDueToUnlockedVaults.1=Please
preferences.updates.prohibitedDueToUnlockedVaults.2=lock your vaults
preferences.updates.prohibitedDueToUnlockedVaults.3=to install the update.
preferences.updates.checkFailed=Looking for updates failed. Please check your internet connection or try again later.
preferences.updates.updateFailed=Update failed. Please install the update manually.
preferences.updates.upToDate=Cryptomator is up-to-date.
preferences.updates.visitDownloadPage=Visit Download Page
## Contribution
preferences.contribute=Support Us
@@ -522,8 +527,8 @@ recoveryKey.recover.recoverBtn=Recover
recoveryKey.recover.resetSuccess.message=Password reset successful
recoveryKey.recover.resetSuccess.description=You can unlock your vault with the new password.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetVaultConfigSuccess.message=Vault config reset successful
recoveryKey.recover.resetMasterkeyFileSuccess.message=Masterkey file reset successful
recoveryKey.recover.resetVaultConfigSuccess.message=Vault configuration recovered
recoveryKey.recover.resetMasterkeyFileSuccess.message=Masterkey file recovered
recoveryKey.recover.resetMasterkeyFileSuccess.description=You can unlock your vault with your password now.
# Recover Vault Config File and/or Masterkey
@@ -87,6 +87,7 @@
### Locked
### Unlocked
### Missing
### Missing Vault Config
### Needs Migration
### Error
@@ -105,6 +106,25 @@
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
+21 -2
View File
@@ -394,8 +394,6 @@ main.vaultlist.contextMenu.unlockNow=افتح الان
main.vaultlist.contextMenu.vaultoptions=إظهار خيارات المخزن
main.vaultlist.contextMenu.reveal=اظهار القرص
main.vaultlist.contextMenu.share=مشاركة…
main.vaultlist.addVaultBtn.menuItemNew=إنشاء مخزن جديد...
main.vaultlist.addVaultBtn.menuItemExisting=افتح مخزن موجود...
main.vaultlist.showEventsButton.tooltip=عرض الإشعارات
##Notificaition
main.notification.updateAvailable=هناك تحديث متاح.
@@ -433,6 +431,7 @@ main.vaultDetail.missing.info=لم يتمكن Cryptomator من العثور عل
main.vaultDetail.missing.recheck=إعادة الفحص
main.vaultDetail.missing.remove=حذف من قائمة الخزنات…
main.vaultDetail.missing.changeLocation=تغيير موقع الخزنة…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=ترقية الحافظة
main.vaultDetail.migratePrompt=يجب ترقية المخزن الخاص بك إلى تنسيق جديد، قبل أن تتمكن من الوصول إليه
@@ -512,6 +511,26 @@ recoveryKey.recover.resetBtn=إعادة الضبط
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=تم إعادة تعيين كلمة المرور بنجاح
recoveryKey.recover.resetSuccess.description=يمكنك فتح الخزانة الخاصة بك بكلمة المرور الجديدة.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=خزانة Hub
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=تحويل الخزانة
@@ -394,6 +394,7 @@ main.vaultDetail.missing.info=Cryptomator был юлдан һаҡлағыс т
main.vaultDetail.missing.recheck=Яңынан тикшер
main.vaultDetail.missing.remove=Һаҡлағыс исемлегенән алып ташла…
main.vaultDetail.missing.changeLocation=Һаҡлағыс урынын үҙгәрт…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Һаҡлағысты яңыртыу
main.vaultDetail.migratePrompt=Һаҡлағысҡа инер алдынан уны яңы форматҡа тиклем яңыртыу кәрәк
@@ -472,6 +473,26 @@ recoveryKey.recover.resetBtn=Яңынан башла
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Серһүҙ яңыртыу уңышлы тамамланды
recoveryKey.recover.resetSuccess.description=Яңы серһүҙ менән һаҡлағысты аса алаһығыҙ.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Һаҡлағыс хабы
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Һаҡлағысты үҙгәртеү
@@ -381,6 +381,7 @@ main.vaultDetail.missing.info=Cryptomator ня змог знайсці скар
main.vaultDetail.missing.recheck=Пераправерыць
main.vaultDetail.missing.remove=Выдаліць са спісу скарбніц…
main.vaultDetail.missing.changeLocation=Змяніць месцазнаходжанне скарбніцы…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Абнавіць скарбніцу
main.vaultDetail.migratePrompt=Тваю скарбніцу трэба сканвертаваць у новы фармат, перад тым як ты зможаш атрымаць доступ да яе
@@ -454,6 +455,25 @@ recoveryKey.recover.resetBtn=Скінуць
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Пароль паспяхова скінуты
recoveryKey.recover.resetSuccess.description=Ты можаш разамкнуць сваю скарбніцу з дапамогаю новага паролю.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Перабудаваць скарбніцу
@@ -395,6 +395,7 @@ main.vaultDetail.missing.info=Криптоматор не намира хран
main.vaultDetail.missing.recheck=Повторен опит
main.vaultDetail.missing.remove=Премахване от списъка с хранилищата…
main.vaultDetail.missing.changeLocation=Смяна на мястото на хранилището…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Надграждане на хранилище
main.vaultDetail.migratePrompt=Преди да можете да го достъпвате, хранилището трябва да бъде надградено до новия формат
@@ -473,6 +474,26 @@ recoveryKey.recover.resetBtn=Нулиране
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Паролата е променена
recoveryKey.recover.resetSuccess.description=Можете да отключите хранилището с новата парола.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Хранилище на Hub
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Преобразуване на хранилище
@@ -149,6 +149,7 @@ main.vaultlist.contextMenu.lock=লক করুন
### Unlocked
main.vaultDetail.lockBtn=লক করুন
### Missing
### Missing Vault Config
### Needs Migration
### Error
@@ -169,6 +170,25 @@ vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন ক
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
@@ -241,6 +241,7 @@ main.vaultDetail.missing.info=Cryptomator nije mogao pronaći disk na ovoj lokac
main.vaultDetail.missing.recheck=Provjeri ponovo
main.vaultDetail.missing.remove=Ukloni sa liste sefova…
main.vaultDetail.missing.changeLocation=Promijeni lokaciju sefa…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Nadogradi sef
main.vaultDetail.migratePrompt=Da biste mogli pristupiti svom sefu, morate ga nadograditi na novi format
@@ -294,6 +295,25 @@ recoveryKey.recover.correctKey=Ključ za oporavak je ispravan
recoveryKey.printout.heading=Cryptomator Ključ za oporavak za \n"%s"\n
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
+21 -2
View File
@@ -394,8 +394,6 @@ main.vaultlist.contextMenu.unlockNow=Desbloqueja ara
main.vaultlist.contextMenu.vaultoptions=Opcions de la caixa forta
main.vaultlist.contextMenu.reveal=Mostra la unitat
main.vaultlist.contextMenu.share=Compateix…
main.vaultlist.addVaultBtn.menuItemNew=Crea una nova caixa forta…
main.vaultlist.addVaultBtn.menuItemExisting=Obri una caixa forta existent...
##Notificaition
main.notification.updateAvailable=Hi ha una actualització disponible.
main.notification.support=Doneu suport a Cryptomator.
@@ -429,6 +427,7 @@ main.vaultDetail.missing.info=Cryptomator no ha trobat una caixa forta en aquest
main.vaultDetail.missing.recheck=Torna a comprovar
main.vaultDetail.missing.remove=Eliminar de la llista de la caixa forta…
main.vaultDetail.missing.changeLocation=Canvia la localització de la caixa forta…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Actualitza la caixa forta
main.vaultDetail.migratePrompt=Per accedir a la vostra caixa forta abans cal actualitzar-la al nou format
@@ -508,6 +507,26 @@ recoveryKey.recover.resetBtn=Reinicia
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=S'ha modificat la contrasenya correctament
recoveryKey.recover.resetSuccess.description=Pots desbloquejar la caixa forta amb la nova contrasenya.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Caixa forta de Hub
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Convertir la caixa forta
@@ -396,6 +396,7 @@ main.vaultDetail.missing.info=Cryptomator nemohl najít trezor na této cestě.
main.vaultDetail.missing.recheck=Znovu zkontrolovat
main.vaultDetail.missing.remove=Odebrat ze seznamu trezorů…
main.vaultDetail.missing.changeLocation=Změnit umístění trezoru…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Upgrade trezoru
main.vaultDetail.migratePrompt=Váš trezor musí být aktualizován na nový formát, než k němu budete mít přístup
@@ -473,6 +474,26 @@ recoveryKey.recover.resetBtn=Resetovat
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Resetování hesla bylo úspěšné
recoveryKey.recover.resetSuccess.description=Můžete odemknout váš trezor pomocí nového hesla.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub trezor
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Převést trezor
+87 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Fjern denne fil hvis du har lyst.
## Existing
addvaultwizard.existing.title=Tilføj Eksisterende Boks
addvaultwizard.existing.instruction=Vælgt filen "vault.cryptomator" i mappen med dine boks-filer. Hvis der kun findes en fil med navnet "masterkey.cryptomator", skal du vælge den i stedet.
addvaultwizard.existing.restore=Gendan…
addvaultwizard.existing.chooseBtn=Vælg…
addvaultwizard.existing.filePickerTitle=Vælg boks-fil
addvaultwizard.existing.filePickerMimeDesc=Cryptomator boks
@@ -127,6 +128,7 @@ unlock.unlockBtn=Lås op
## Select
unlock.chooseMasterkey.message=Hovednøgle-fil ikke fundet
unlock.chooseMasterkey.description=Cryptomator kunne ikke finde hovednøgle-filen for boksen "%s". Vælg venligst filen manuelt.
unlock.chooseMasterkey.restoreInstead=Gendan i stedet masterkey-filen
unlock.chooseMasterkey.filePickerTitle=Vælg hovednøgle-fil
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator hovednøgle
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Lås op nu
main.vaultlist.contextMenu.vaultoptions=Vis boksindstillinger
main.vaultlist.contextMenu.reveal=Vis drev
main.vaultlist.contextMenu.share=Del…
main.vaultlist.addVaultBtn.menuItemNew=Opret ny boks...
main.vaultlist.addVaultBtn.menuItemExisting=Åbn eksisterende boks...
main.vaultlist.addVaultBtn.menuItemNew=Opret ny boks
main.vaultlist.addVaultBtn.menuItemExisting=Åbn eksisterende boks
main.vaultlist.addVaultBtn.menuItemRecover=Genopret eksisterende boks…
main.vaultlist.showEventsButton.tooltip=Åbn begivenhedsvisning
##Notificaition
main.notification.updateAvailable=Opdatering er tilgængelig.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=Cryptomator kunne ikke finde en boks på denne sti
main.vaultDetail.missing.recheck=Kontrollér igen
main.vaultDetail.missing.remove=Fjern fra listen over bokse…
main.vaultDetail.missing.changeLocation=Skift placering for boks…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=Bokskonfiguration mangler.
main.vaultDetail.missingVaultConfig.restore=Gendan bokskonfiguration
### Needs Migration
main.vaultDetail.migrateButton=Opgradér boks
main.vaultDetail.migratePrompt=Din boks skal opgraderes til et nyt format, før du kan tilgå den
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=Konvertér til adgangskodebaseret boks
recoveryKey.display.title=Vis gendannelsesnøgle
recoveryKey.create.message=Adgangskode krævet
recoveryKey.create.description=Indtast adgangskoden for "%s", for at vise gendannelsesnøglen.
recoveryKey.recover.description=Indtast adgangskoden for "%s" for at gendanne bokskonfiguration.
recoveryKey.display.description=Den følgende gendannelsesnøgle kan bruges til at genskabe adgang til "%s":
recoveryKey.display.StorageHints=Opbevar den et meget sikkert sted, som fx:\n • Gem den i din adgangskode-manager\n • Gem den på et USB-drev\n • Print den ud på papir
## Reset Password
@@ -509,9 +516,57 @@ recoveryKey.recover.invalidKey=Denne gendannelsesnøgle er ikke gyldig
recoveryKey.printout.heading=Cryptomator gendannelsesnøgle\n"%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Nulstil
recoveryKey.recover.recoverBtn=Gendan
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Adgangskod nulstillet
recoveryKey.recover.resetSuccess.description=Du kan nu låse din boks op med den nye adgangskode.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetMasterkeyFileSuccess.description=Du kan låse din boks op med din adgangskode nu.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Boks tilføjet
recover.existing.message=Boksen blev tilføjet med succes
recover.existing.description=Din boks "%s" er blevet tilføjet til bokslisten. Ingen genoprettelsesproces var nødvendig.
##Vault Already Exists - Dialog
recover.alreadyExists.title=Boksen findes allerede
recover.alreadyExists.message=Denne boks er allerede tilføjet
recover.alreadyExists.description=Din boks "%s" er allerede til stede i din boksliste og blev derfor ikke tilføjet igen.
##Invalid Selection - Dialog
recover.invalidSelection.title=Ugyldigt valg
recover.invalidSelection.message=Dit valg er ikke en boks
recover.invalidSelection.description=Den valgte mappe skal være en gyldig Cryptomator boks.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub Boks
contactHubVaultOwner.message=Denne boks blev oprettet med Cryptomator Hub
contactHubVaultOwner.description=Tag venligst kontakt til boksenejer for at gendanne den manglende fil. De kan downloade boksskabelon fra Cryptomator Hub.
##Dialog Title
recover.recoverVaultConfig.title=Gendan bokskonfiguration
recover.recoverMasterkey.title=Gendan Masterkey
## OnBoarding
recover.onBoarding.chooseMethod=Vælg genoprettelsesmetode:
recover.onBoarding.useRecoveryKey=Brug gendannelsesnøgle
recover.onBoarding.usePassword=Brug adgangskode
recover.onBoarding.intro=Sørg for at tjekke følgende:
recover.onBoarding.pleaseConfirm=Før der fortsættes, bekræftes at:
recover.onBoarding.otherwisePleaseConfirm=I modsat fald bekræftes følgende:
recover.onBoarding.allMissing.intro=Hvis denne boks administreres af Cryptomator Hub, skal boksejer gendanne den for dig.
recover.onBoarding.intro.ensure=Alle filer er fuldt synkroniserede.
recover.onBoarding.affirmation=Jeg har læst og forstået disse krav
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=Du har gendannelsesnøglen og ved, om ekspertindstillinger blev brugt.
recover.onBoarding.intro.password=Du har boksens adgangskode og ved, om ekspertindstillinger blev brugt.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=Du har boksens gendannelsesnøgle.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=Denne værdi skal matche den, der blev brugt før gendannelse for at sikre kompatibilitet med tidligere krypterede data.
# Convert Vault
convertVault.title=Konvertér boks
@@ -561,6 +616,7 @@ dokanySupportEnd.preferencesBtn=Åbn Indstillinger
retryIfReadonly.title=Begrænset boksadgang
retryIfReadonly.message=Ingen skriveadgang til boksens mappe
retryIfReadonly.description=Cryptomator kan ikke skrive til boksens mappe. Du kan ændre boksen til at være skrivebeskyttet og prøve igen. Denne indstilling kan være deaktiveret i boksens indstillinger.
retryIfReadonly.retry=Skift og prøv igen
# Share Vault
shareVault.title=Del Boks
@@ -584,10 +640,39 @@ shareVault.hub.openHub=Åben Cryptomator Hub
# Decrypt File Names
decryptNames.title=Dekryptér Filnavne
decryptNames.filePicker.title=Vælg krypteret fil
decryptNames.filePicker.extensionDescription=Cryptomator krypteret fil
decryptNames.copyTable.tooltip=Kopiér tabel
decryptNames.clearTable.tooltip=Ryd tabel
decryptNames.copyHint=Kopiér celleindhold med %s
decryptNames.dropZone.message=Slip filer eller klik for at vælge
decryptNames.dropZone.error.vaultInternalFiles=Boks interne filer med intet dekrypterbart navn valgt
decryptNames.dropZone.error.foreignFiles=Filer hører ikke til boksen "%s"
decryptNames.dropZone.error.noDirIdBackup=Mappe af valgte filer indeholder ikke dirId.c9r fil
decryptNames.dropZone.error.generic=Kunne ikke dekryptere filnavne
# Event View
eventView.title=Begivenheder
eventView.filter.allVaults=Alle
eventView.clearListButton.tooltip=Ryd liste
## event list entries
eventView.entry.vaultLocked.description=Lås "%s" op for detaljer
eventView.entry.conflictResolved.message=Løst konflikt
eventView.entry.conflictResolved.showDecrypted=Vis dekrypteret fil
eventView.entry.conflictResolved.copyDecrypted=Kopiér dekrypteret sti
eventView.entry.conflict.message=Konfliktløsning mislykkedes
eventView.entry.conflict.showDecrypted=Vis dekrypteret, original fil
eventView.entry.conflict.copyDecrypted=Kopier dekrypteret, original sti
eventView.entry.conflict.showEncrypted=Vis modstridende, krypteret fil
eventView.entry.conflict.copyEncrypted=Kopier modstridende, krypteret sti
eventView.entry.decryptionFailed.message=Dekryptering mislykkedes
eventView.entry.decryptionFailed.showEncrypted=Vis krypteret fil
eventView.entry.decryptionFailed.copyEncrypted=Kopiér krypteret sti
eventView.entry.brokenDirFile.message=Brudt mappelink
eventView.entry.brokenDirFile.showEncrypted=Vis brudt, krypteret link
eventView.entry.brokenDirFile.copyEncrypted=Kopiér sti til brudt link
eventView.entry.brokenFileNode.message=Brudt filsystem-node
eventView.entry.brokenFileNode.showEncrypted=Vis brudt, krypteret node
eventView.entry.brokenFileNode.copyEncrypted=Kopiér sti af brudt, krypteret node
eventView.entry.brokenFileNode.copyDecrypted=Kopiér dekrypteret sti
+59 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Du kannst diese Datei löschen.
## Existing
addvaultwizard.existing.title=Bestehenden Tresor hinzufügen
addvaultwizard.existing.instruction=Wähle die Datei „vault.cryptomator“ deines bestehenden Tresors aus. Falls nur eine Datei mit der Bezeichnung „masterkey.cryptomator“ vorhanden ist, nutze stattdessen diese.
addvaultwizard.existing.restore=Wiederherstellen…
addvaultwizard.existing.chooseBtn=Durchsuchen …
addvaultwizard.existing.filePickerTitle=Tresordatei auswählen
addvaultwizard.existing.filePickerMimeDesc=Cryptomator-Tresor
@@ -127,6 +128,7 @@ unlock.unlockBtn=Entsperren
## Select
unlock.chooseMasterkey.message=Masterkey-Datei nicht gefunden
unlock.chooseMasterkey.description=Cryptomator konnte die Masterkey-Datei des Tresors „%s“ nicht finden. Bitte wähle die Datei manuell aus.
unlock.chooseMasterkey.restoreInstead=Masterkey Datei wiederherstellen
unlock.chooseMasterkey.filePickerTitle=Masterkey-Datei auswählen
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator-Masterkey
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Jetzt entsperren
main.vaultlist.contextMenu.vaultoptions=Tresoroptionen anzeigen
main.vaultlist.contextMenu.reveal=Laufwerk anzeigen
main.vaultlist.contextMenu.share=Teilen …
main.vaultlist.addVaultBtn.menuItemNew=Neuen Tresor erstellen...
main.vaultlist.addVaultBtn.menuItemExisting=Bestehenden Tresor öffnen...
main.vaultlist.addVaultBtn.menuItemNew=Neuen Tresor erstellen
main.vaultlist.addVaultBtn.menuItemExisting=Bestehenden Tresor öffnen
main.vaultlist.addVaultBtn.menuItemRecover=Bestehenden Tresor wiederherstellen…
main.vaultlist.showEventsButton.tooltip=Ereignis-Ansicht öffnen
##Notificaition
main.notification.updateAvailable=Eine neue Version ist verfügbar.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=Cryptomator konnte keinen Tresor mit diesem Pfad f
main.vaultDetail.missing.recheck=Erneut prüfen
main.vaultDetail.missing.remove=Aus Tresorliste entfernen …
main.vaultDetail.missing.changeLocation=Speicherort des Tresors ändern …
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=Tresorkonfiguration fehlt.
main.vaultDetail.missingVaultConfig.restore=Tresorkonfiguration wiederherstellen
### Needs Migration
main.vaultDetail.migrateButton=Tresor upgraden
main.vaultDetail.migratePrompt=Dein Tresor muss in ein neues Format konvertiert werden, bevor du auf ihn zugreifen kannst
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=In passwortgeschützten Tresor umwandeln
recoveryKey.display.title=Wiederherstellungsschlüssel anzeigen
recoveryKey.create.message=Passwort erforderlich
recoveryKey.create.description=Gib das Passwort für „%s“ ein, um dessen Wiederherstellungsschlüssel anzuzeigen.
recoveryKey.recover.description=Gib das Passwort für "%s" ein, um die Tresorkonfiguration wiederherzustellen.
recoveryKey.display.description=Mit folgendem Wiederherstellungsschlüssel kannst du den Zugriff auf „%s“ wiederherstellen:
recoveryKey.display.StorageHints=Bewahre ihn möglichst sicher auf, z. B.\n • in einem Passwortmanager\n • auf einem USB-Speicherstick\n • auf Papier ausgedruckt
## Reset Password
@@ -509,9 +516,59 @@ recoveryKey.recover.invalidKey=Dieser Wiederherstellungsschlüssel ist ungültig
recoveryKey.printout.heading=Cryptomator-Wiederherstellungsschlüssel\n„%s“\n
### Reset Password
recoveryKey.recover.resetBtn=Zurücksetzen
recoveryKey.recover.recoverBtn=Wiederherstellen
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Passwort erfolgreich zurückgesetzt
recoveryKey.recover.resetSuccess.description=Du kannst deinen Tresor mit dem neuen Passwort entsperren.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetVaultConfigSuccess.message=Tresorkonfiguration wiederhergestellt
recoveryKey.recover.resetMasterkeyFileSuccess.message=Masterkey-Datei wiederhergestellt
recoveryKey.recover.resetMasterkeyFileSuccess.description=Du kannst deinen Tresor mit dem neuen Passwort jetzt entsperren.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Tresor hinzugefügt
recover.existing.message=Der Tresor wurde erfolgreich hinzugefügt
recover.existing.description=Dein Tresor "%s" wurde zur Tresorliste hinzugefügt. Es war kein Wiederherstellungsprozess nötig.
##Vault Already Exists - Dialog
recover.alreadyExists.title=Tresor existiert bereits
recover.alreadyExists.message=Dieser Tresor wurde bereits hinzugefügt
recover.alreadyExists.description=Dein Tresor "%s" ist bereits in der Tresorliste vorhanden und wurde daher nicht erneut hinzugefügt.
##Invalid Selection - Dialog
recover.invalidSelection.title=Ungültige Auswahl
recover.invalidSelection.message=Der ausgewählte Ordner ist kein Tresor
recover.invalidSelection.description=Der ausgewählte Ordner muss ein gültiger Cryptomator-Tresor sein.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hubtresor
contactHubVaultOwner.message=Dieser Tresor wurde mit Cryptomator Hub erstellt
contactHubVaultOwner.description=Bitte wenden dich an den Tresorbesitzer, um die fehlende Datei wiederherzustellen. Dieser kann die Tresorvorlage von Cryptomator Hub herunterladen.
##Dialog Title
recover.recoverVaultConfig.title=Tresorkonfiguration wiederherstellen
recover.recoverMasterkey.title=Masterkey wiederherstellen
## OnBoarding
recover.onBoarding.chooseMethod=Wähle die Wiederherstellungsmethode:
recover.onBoarding.useRecoveryKey=Wiederherstellungsschlüssel verwenden
recover.onBoarding.usePassword=Passwort verwenden
recover.onBoarding.intro=Stelle sicher, folgendes geprüft zu haben:
recover.onBoarding.pleaseConfirm=Bevor es weiter geht, bestätige bitte:
recover.onBoarding.otherwisePleaseConfirm=Andernfalls, bestätige bitte:
recover.onBoarding.allMissing.intro=Wird der Tresor von Cryptomator Hub verwaltet, muss der Tresorbesitzer ihn für dich wiederherstellen.
recover.onBoarding.intro.ensure=Alle Dateien sind vollständig synchronisiert.
recover.onBoarding.affirmation=Ich habe die Anforderungen gelesen und verstanden
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=Du besitzt den Wiederherstellungsschlüssel und weißt, ob Experteneinstellungen verwendet wurden.
recover.onBoarding.intro.password=Du besitzt das Tresorpasswort und weißt, ob Experteneinstellungen verwendet wurden.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=Du besitzt den Wiederherstellungsschlüssel.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=Um Kompatibilität mit den verschlüsselten Dateien zu gewährleisten, muss der gleiche Wert wie zur Tresorerstellung verwendet werden.
# Convert Vault
convertVault.title=Tresor umwandeln
+21 -2
View File
@@ -394,8 +394,6 @@ main.vaultlist.contextMenu.unlockNow=Ξεκλείδωμα τώρα
main.vaultlist.contextMenu.vaultoptions=Εμφάνιση επιλογών Vault
main.vaultlist.contextMenu.reveal=Αποκάλυψη εικονικού δίσκου
main.vaultlist.contextMenu.share=Κοινοποίηση…
main.vaultlist.addVaultBtn.menuItemNew=Δημιουργία Νέας Κρύπτης...
main.vaultlist.addVaultBtn.menuItemExisting=Άνοιγμα Υπάρχοντος Κρύπτης...
main.vaultlist.showEventsButton.tooltip=Άνοιγμα προβολής συμβάντων
##Notificaition
main.notification.updateAvailable=Η ενημέρωση είναι διαθέσιμη.
@@ -433,6 +431,7 @@ main.vaultDetail.missing.info=Cryptomator δεν βρήκε vault σε αυτό
main.vaultDetail.missing.recheck=Επανέλεγχος
main.vaultDetail.missing.remove=Κατάργηση από την λίστα των Vault…
main.vaultDetail.missing.changeLocation=Αλλαγή τοποθεσίας Vault…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Αναβάθμιση Vault
main.vaultDetail.migratePrompt=Το vault σας πρέπει να αναβαθμιστεί σε νέα μορφή, προτού να έχετε πρόσβαση σε αυτό
@@ -512,6 +511,26 @@ recoveryKey.recover.resetBtn=Επαναφορά
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Επιτυχής επαναφορά κωδικού πρόσβασης
recoveryKey.recover.resetSuccess.description=Μπορείτε να ξεκλειδώσετε την κρύπτη σας με το νέο κωδικό πρόσβασης.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Κρύπτη Hub
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Μετατροπή Θησαυ/κιου
+29 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=No dude en eliminar este archivo.
## Existing
addvaultwizard.existing.title=Añadir bóveda existente
addvaultwizard.existing.instruction=Elija el archivo "vault.cryptomator" de su bóveda existente. Si solo existe un archivo llamado "masterkey.cryptomator", selecciónelo en su lugar.
addvaultwizard.existing.restore=
addvaultwizard.existing.chooseBtn=Elegir…
addvaultwizard.existing.filePickerTitle=Seleccionar archivo de bóveda
addvaultwizard.existing.filePickerMimeDesc=Bóveda de Cryptomator
@@ -127,6 +128,7 @@ unlock.unlockBtn=Desbloquear
## Select
unlock.chooseMasterkey.message=Archivo de la clave maestra no encontrado
unlock.chooseMasterkey.description=No se pudo encontrar el archivo de la clave maestra para la bóveda "%s". Por favor, elija manualmente el archivo de la clave.
unlock.chooseMasterkey.restoreInstead=
unlock.chooseMasterkey.filePickerTitle=Seleccione el archivo de la clave maestra
unlock.chooseMasterkey.filePickerMimeDesc=Clave maestra de Cryptomator
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Desbloquear ahora
main.vaultlist.contextMenu.vaultoptions=Mostrar opciones de la bóveda
main.vaultlist.contextMenu.reveal=Revelar unidad
main.vaultlist.contextMenu.share=Compartir…
main.vaultlist.addVaultBtn.menuItemNew=Crear Bóveda Nueva...
main.vaultlist.addVaultBtn.menuItemExisting=Abrir Bóveda Existente...
main.vaultlist.addVaultBtn.menuItemNew=
main.vaultlist.addVaultBtn.menuItemExisting=
main.vaultlist.addVaultBtn.menuItemRecover=
main.vaultlist.showEventsButton.tooltip=Abrir vista de evento
##Notificaition
main.notification.updateAvailable=Existen actualizaciones disponibles.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=Cryptomator no pudo encontrar una bóveda en esta
main.vaultDetail.missing.recheck=Volver a comprobar
main.vaultDetail.missing.remove=Eliminar de la lista de bóveda…
main.vaultDetail.missing.changeLocation=Cambiar ubicación de la bóveda…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=Falta la configuración de la bóveda.
main.vaultDetail.missingVaultConfig.restore=
### Needs Migration
main.vaultDetail.migrateButton=Actualizar bóveda
main.vaultDetail.migratePrompt=Su bóveda necesita ser actualizada a un formato nuevo antes de poder acceder a ella
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=Convertir a Bóveda con Contraseña
recoveryKey.display.title=Mostrar clave de recuperación
recoveryKey.create.message=Contraseña requerida
recoveryKey.create.description=Ingresar la contraseña para mostrar la clave de recuperación para "%s":
recoveryKey.recover.description=
recoveryKey.display.description=La siguiente clave de recuperación puede usarse para restaurar el acceso a "%s":
recoveryKey.display.StorageHints=Manténgala en algún lugar seguro, p.ej.:\n • Almacenarla en un administrador de contraseñas\n • Guardarla en una llave USB\n • Imprimirla en un papel
## Reset Password
@@ -512,6 +519,26 @@ recoveryKey.recover.resetBtn=## Reiniciar contraseña\nrecoveryKey.recover.reset
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Contraseña restablecida con éxito
recoveryKey.recover.resetSuccess.description=Puede desbloquear su bóveda con la contraseña nueva.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Bóveda de Hub
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Convertir bóveda
@@ -183,6 +183,7 @@ main.vaultlist.contextMenu.reveal=نمایش درایو
main.vaultDetail.revealBtn=نمایش درایو
main.vaultDetail.lockBtn=قفل
### Missing
### Missing Vault Config
### Needs Migration
### Error
@@ -204,6 +205,25 @@ vaultOptions.mount.mountPoint.directoryPickerButton=انتخاب کنید…
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
+20 -2
View File
@@ -393,8 +393,6 @@ main.vaultlist.contextMenu.unlockNow=Avaa Nyt
main.vaultlist.contextMenu.vaultoptions=Näytä holvin asetukset
main.vaultlist.contextMenu.reveal=Paljasta Asema
main.vaultlist.contextMenu.share=Jaa…
main.vaultlist.addVaultBtn.menuItemNew=Luo uusi holvi...
main.vaultlist.addVaultBtn.menuItemExisting=Avaa olemassa oleva holvi...
main.vaultlist.showEventsButton.tooltip=Avaa tapahtumanäkymä
##Notificaition
main.notification.updateAvailable=Päivitys on saatavilla.
@@ -432,6 +430,7 @@ main.vaultDetail.missing.info=Cryptomator ei löytänyt täältä holvia.
main.vaultDetail.missing.recheck=Tarkista uudelleen
main.vaultDetail.missing.remove=Poista holvilistalta…
main.vaultDetail.missing.changeLocation=Vaihda holvin sijaintia…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Päivitä Holvi
main.vaultDetail.migratePrompt=Holvisi täytyy muuntaa uuteen muotoon ennen kuin voit avata sen
@@ -511,6 +510,25 @@ recoveryKey.recover.resetBtn=Palauta
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Salasanan palautus onnistui
recoveryKey.recover.resetSuccess.description=Voit avata holvin uudella salasanalla.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Muunna holvi
@@ -416,6 +416,7 @@ main.vaultDetail.missing.info=Hindi makahanap ng vault ang Cryptomator sa landas
main.vaultDetail.missing.recheck=Suriin muli
main.vaultDetail.missing.remove=Alisin sa Listahan ng Vault…
main.vaultDetail.missing.changeLocation=Baguhin ang Lokasyon ng Vault…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=I-upgrade ang Vault
main.vaultDetail.migratePrompt=Kailangang i-upgrade ang iyong vault sa bagong format, bago mo ito ma-access
@@ -495,6 +496,26 @@ recoveryKey.recover.resetBtn=I-reset
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Matagumpay ang pag-reset ng password
recoveryKey.recover.resetSuccess.description=Maaari mong i-unlock ang iyong vault gamit ang bagong password.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub Vault
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=I-convert ang Vault
+57 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Vous pouvez supprimer ce fichier.
## Existing
addvaultwizard.existing.title=Ajouter un coffre existant
addvaultwizard.existing.instruction=Choisissez le fichier « vault.cryptomator » de votre volume existant. Si seul le fichier « masterkey.cryptomator » est présent, sélectionnez celui-là.
addvaultwizard.existing.restore=Restaurer…
addvaultwizard.existing.chooseBtn=Choisir…
addvaultwizard.existing.filePickerTitle=Sélectionnez le fichier correspondant au volume chiffré
addvaultwizard.existing.filePickerMimeDesc=Coffre-fort Cryptomator
@@ -127,6 +128,7 @@ unlock.unlockBtn=Déverrouiller
## Select
unlock.chooseMasterkey.message=Fichier Masterkey introuvable
unlock.chooseMasterkey.description=Impossible de trouver le fichier clef à l'adresse attendue pour l'espace chiffré "%s". Veuillez sélectionner le fichier clef manuellement.
unlock.chooseMasterkey.restoreInstead=Restaurez le fichier masterkey à la place
unlock.chooseMasterkey.filePickerTitle=Sélectionner le fichier clef
unlock.chooseMasterkey.filePickerMimeDesc=Clé principale Cryptomator
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Déverrouiller maintenant
main.vaultlist.contextMenu.vaultoptions=Afficher les options du volume chiffré
main.vaultlist.contextMenu.reveal=Afficher le lecteur
main.vaultlist.contextMenu.share=Partager…
main.vaultlist.addVaultBtn.menuItemNew=Créer un nouveau coffre...
main.vaultlist.addVaultBtn.menuItemExisting=Ouvrir un coffre existant...
main.vaultlist.addVaultBtn.menuItemNew=Créer un nouveau coffre
main.vaultlist.addVaultBtn.menuItemExisting=Ouvrir le coffre existant
main.vaultlist.addVaultBtn.menuItemRecover=Restaurer le coffre existant…
main.vaultlist.showEventsButton.tooltip=Ouvrir la vue Événements
##Notificaition
main.notification.updateAvailable=Mise à jour disponible.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=Cryptomator n'a pas pu trouver de volume chiffré
main.vaultDetail.missing.recheck=Revérifier
main.vaultDetail.missing.remove=Retirer de la liste des volumes…
main.vaultDetail.missing.changeLocation=Changer l'emplacement du volume…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=La configuration du coffre est manquante.
main.vaultDetail.missingVaultConfig.restore=Restaurer la configuration du coffre
### Needs Migration
main.vaultDetail.migrateButton=Mettre le volume à jour
main.vaultDetail.migratePrompt=Votre coffre doit être converti dans un nouveau format avant d'y accéder
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=Convertir en coffre-fort basé sur mot de passe
recoveryKey.display.title=Montrer la clé de récupération
recoveryKey.create.message=Mot de passe requis
recoveryKey.create.description=Entrez le mot de passe de "%s" pour afficher sa clé de récupération.
recoveryKey.recover.description=Entrez le mot de passe pour "%s" pour restaurer la configuration du coffre.
recoveryKey.display.description=La clé de récupération suivante peut être utilisée pour restaurer l'accès à "%s " :
recoveryKey.display.StorageHints=Gardez-la dans un endroit sûr, par ex. :\n • Stockez-la en utilisant un gestionnaire de mots de passe\n • Enregistrez-la sur une clé USB\n • Imprimez-la
## Reset Password
@@ -509,9 +516,57 @@ recoveryKey.recover.invalidKey=Cette clé de récupération n'est pas valide
recoveryKey.printout.heading=Clé de récupération Cryptomator "%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Réinitialiser
recoveryKey.recover.recoverBtn=Restaurer
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Réinitialisation du mot de passe réussie
recoveryKey.recover.resetSuccess.description=Vous pouvez déverrouiller votre coffre avec le nouveau mot de passe.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetMasterkeyFileSuccess.description=Vous pouvez maintenant déverrouiller votre coffre avec votre mot de passe.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Coffre ajouté
recover.existing.message=Le coffre a été ajouté avec succès
recover.existing.description=Votre coffre "%s" a été ajouté à la liste des coffres. Aucun processus de récupération n'a été nécessaire.
##Vault Already Exists - Dialog
recover.alreadyExists.title=Le coffre-fort existe déjà
recover.alreadyExists.message=Ce coffre a déjà été ajouté
recover.alreadyExists.description=Votre coffre «%s» est déjà présent dans votre liste de coffres et n'a donc pas été ajouté à nouveau.
##Invalid Selection - Dialog
recover.invalidSelection.title=Sélection invalide
recover.invalidSelection.message=Votre sélection n'est pas un coffre
recover.invalidSelection.description=Le dossier sélectionné doit être un coffre Cryptomator valide.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Coffre dans Hub
contactHubVaultOwner.message=Ce coffre a été créé avec le Hub Cryptomator
contactHubVaultOwner.description=.
##Dialog Title
recover.recoverVaultConfig.title=Récupérer la configuration du coffre
recover.recoverMasterkey.title=Récupérer la clé principale
## OnBoarding
recover.onBoarding.chooseMethod=Choisissez la méthode de récupération :
recover.onBoarding.useRecoveryKey=Utiliser la clé de récupération
recover.onBoarding.usePassword=Utiliser mot de passe
recover.onBoarding.intro=Assurez-vous de vérifier ce qui suit :
recover.onBoarding.pleaseConfirm=Avant de procéder, veuillez confirmer ceci :
recover.onBoarding.otherwisePleaseConfirm=Sinon, veuillez confirmer ceci:
recover.onBoarding.allMissing.intro=Si ce coffre est géré par Cryptomator Hub, le propriétaire du coffre doit le restaurer pour vous.
recover.onBoarding.intro.ensure=.
recover.onBoarding.affirmation=J'ai lu et compris ces prérequis
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=Vous avez la clé de récupération et et savez si les paramètres experts ont été utilisés.
recover.onBoarding.intro.password=Vous avez le mot de passe du coffre et savez si les paramètres experts ont été utilisés.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=Vous avez la clé de récupération du coffre.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=
# Convert Vault
convertVault.title=Convertir le coffre
@@ -101,6 +101,7 @@ lock.forced.retryBtn=Tentar de novo
### Locked
### Unlocked
### Missing
### Missing Vault Config
### Needs Migration
### Error
@@ -119,6 +120,25 @@ lock.forced.retryBtn=Tentar de novo
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
@@ -384,6 +384,7 @@ main.vaultDetail.missing.info=Cryptomator לא הצליח למצוא כספת ב
main.vaultDetail.missing.recheck=בדיקה נוספת
main.vaultDetail.missing.remove=הסר מרשימה הכספות…
main.vaultDetail.missing.changeLocation=שנה את מיקום הכספת…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=שדרג הכספת
main.vaultDetail.migratePrompt=צריך לשדרג את הכספת שלך לגרסה חדשה לפני שניתן לגשת אליה
@@ -455,6 +456,25 @@ recoveryKey.recover.resetBtn=איפוס
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=איפוס סיסמה הצליח
recoveryKey.recover.resetSuccess.description=ניתן לפתוח את הכספת עם הסיסמה החדשה.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.convert.convertBtn.before=להמיר
@@ -256,6 +256,7 @@ main.vaultDetail.lockBtn=लॉक करें
main.vaultDetail.stats=वॉल्ट के आंकड़े
### Missing
main.vaultDetail.missing.remove=वॉल्ट की सूची से हटाए।
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=वाउल्ट को अपग्रेड करें
### Error
@@ -291,6 +292,25 @@ recoveryKey.create.description=रिकवरी-की दिखाने क
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
@@ -298,6 +298,7 @@ main.vaultDetail.missing.info=Cryptomator nije mogao pronaći trezor s ovom puta
main.vaultDetail.missing.recheck=Ponovo provjeri
main.vaultDetail.missing.remove=Ukloni iz liste trezora…
main.vaultDetail.missing.changeLocation=Promijeni lokaciju trezora…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Nadogradi trezor
main.vaultDetail.migratePrompt=Vaš trezor treba se nadograditi na novi format, prije nego mu možete pristupiti
@@ -357,6 +358,25 @@ recoveryKey.recover.correctKey=Ovo je valjani ključ za oporavak
recoveryKey.printout.heading=Cryptomator-ov ključ za oporavak\n"%s"\n
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
+21 -2
View File
@@ -394,8 +394,6 @@ main.vaultlist.contextMenu.unlockNow=Azonnali feloldás
main.vaultlist.contextMenu.vaultoptions=Széf beállítások
main.vaultlist.contextMenu.reveal=Széf megjelenítése
main.vaultlist.contextMenu.share=Megosztás…
main.vaultlist.addVaultBtn.menuItemNew=Új széf létrehozása...
main.vaultlist.addVaultBtn.menuItemExisting=Meglévő széf megnyitása...
##Notificaition
main.notification.updateAvailable=Frissítés elérhető.
main.notification.support=Cryptomator támogatása.
@@ -429,6 +427,7 @@ main.vaultDetail.missing.info=A Cryptomator nem talált széfet ezen az útvonal
main.vaultDetail.missing.recheck=Ellenőrizze újra
main.vaultDetail.missing.remove=A széf eltávolítása a listából…
main.vaultDetail.missing.changeLocation=A széf helyének megváltoztatása…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Széf frissítése
main.vaultDetail.migratePrompt=A széfet új formátumra kell frissíteni, mielőtt hozzáférhet
@@ -508,6 +507,26 @@ recoveryKey.recover.resetBtn=Visszaállítás
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=A jelszó alaphelyzetbe állítása sikeresen megtörtént
recoveryKey.recover.resetSuccess.description=Feloldhatja a széfet az új jelszóval.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub széf
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Széf átalakítása
+21 -2
View File
@@ -394,8 +394,6 @@ main.vaultlist.contextMenu.unlockNow=Buka Kunci Sekarang
main.vaultlist.contextMenu.vaultoptions=Tampilkan Opsi Vault
main.vaultlist.contextMenu.reveal=Buka Drive
main.vaultlist.contextMenu.share=Bagikan…
main.vaultlist.addVaultBtn.menuItemNew=Buat Vault Baru...
main.vaultlist.addVaultBtn.menuItemExisting=Buka Vault yang Tersedia...
##Notificaition
main.notification.updateAvailable=Pembaruan tersedia.
main.notification.support=Dukung Cryptomator.
@@ -429,6 +427,7 @@ main.vaultDetail.missing.info=Cryptomator tidak dapat menemukan vault di path in
main.vaultDetail.missing.recheck=Periksa kembali
main.vaultDetail.missing.remove=Hapus dari Daftar Vault…
main.vaultDetail.missing.changeLocation=Ganti Lokasi Vault…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Tingkatkan Vault
main.vaultDetail.migratePrompt=Vault Anda perlu ditingkatkan ke format baru, sebelum Anda dapat mengaksesnya
@@ -508,6 +507,26 @@ recoveryKey.recover.resetBtn=Atur ulang
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Atur ulang kata sandi berhasil
recoveryKey.recover.resetSuccess.description=Anda dapat membuka kunci vault Anda dengan kata sandi baru.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub Vault
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Konversi Vault
+57 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Sentiti libero di rimuovere questo file.
## Existing
addvaultwizard.existing.title=Aggiungi una Cassaforte Esistente
addvaultwizard.existing.instruction=Scegliere il file "vault.cryptomator" della tua cassaforte. Se esiste solo un file chiamato "masterkey.cryptomator", allora scegli quello.
addvaultwizard.existing.restore=Ripristina…
addvaultwizard.existing.chooseBtn=Scegli…
addvaultwizard.existing.filePickerTitle=Seleziona file cassaforte
addvaultwizard.existing.filePickerMimeDesc=Cassaforte di Cryptomator
@@ -127,6 +128,7 @@ unlock.unlockBtn=Sblocca
## Select
unlock.chooseMasterkey.message=File Masterkey non trovato
unlock.chooseMasterkey.description=Impossibile trovare il file Masterkey per questa cassaforte alla sua posizione prevista. Sei pregato di sceglierlo manualmente.
unlock.chooseMasterkey.restoreInstead=Ripristina invece il file con la chiave principale
unlock.chooseMasterkey.filePickerTitle=Seleziona il File Masterkey
unlock.chooseMasterkey.filePickerMimeDesc=Chiave principale di Cryptomator
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Sblocca Ora
main.vaultlist.contextMenu.vaultoptions=Mostra le Opzioni della Cassaforte
main.vaultlist.contextMenu.reveal=Rivela Unità
main.vaultlist.contextMenu.share=Condividi…
main.vaultlist.addVaultBtn.menuItemNew=Crea una nuova cassaforte...
main.vaultlist.addVaultBtn.menuItemExisting=Apri una cassaforte esistente...
main.vaultlist.addVaultBtn.menuItemNew=Crea una nuova cassaforte
main.vaultlist.addVaultBtn.menuItemExisting=Apri una cassaforte esistente
main.vaultlist.addVaultBtn.menuItemRecover=Recupera una cassaforte esistente…
main.vaultlist.showEventsButton.tooltip=Apri vista eventi
##Notificaition
main.notification.updateAvailable=Aggiornamento disponibile.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=Cryptomator non è riuscito a trovare una cassafor
main.vaultDetail.missing.recheck=Ricontrolla
main.vaultDetail.missing.remove=Rimuovi dall'elenco delle casseforti…
main.vaultDetail.missing.changeLocation=Cambia la Posizione della Cassaforte…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=Manca la configurazione della cassaforte.
main.vaultDetail.missingVaultConfig.restore=Ripristina la configurazione della cassaforte
### Needs Migration
main.vaultDetail.migrateButton=Aggiorna la Cassaforte
main.vaultDetail.migratePrompt=La tua cassaforte dev'esser aggiornata a un nuovo formato, prima di potervi accedere
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=Converti in cassaforte basata su password
recoveryKey.display.title=Mostra Chiave Di Recupero
recoveryKey.create.message=Password richiesta
recoveryKey.create.description=Inserisci la password per visualizzare la chiave di recupero per "%s":
recoveryKey.recover.description=Inserisci la password di "%s" per recuperare la configurazione della cassaforte.
recoveryKey.display.description=La seguente chiave di recupero può essere utilizzata per ripristinare l'accesso a %s":
recoveryKey.display.StorageHints=Conservala da qualche parte in modo sicuro, es.\n • Archiviarla usando un gestore di password\n • Salvarla su un'unità flash USB\n • Stamparla su carta
## Reset Password
@@ -509,9 +516,57 @@ recoveryKey.recover.invalidKey=Questa chiave di recupero non é valida
recoveryKey.printout.heading=Chiave di recupero Cryptomator\n"%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Reimposta
recoveryKey.recover.recoverBtn=Recupera
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Password reimpostata correttamente
recoveryKey.recover.resetSuccess.description=Puoi sbloccare la tua cassaforte con la nuova password.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetMasterkeyFileSuccess.description=Ora puoi sbloccare la cassaforte con la tua password.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Cassaforte aggiunta
recover.existing.message=La cassaforte è stata aggiunta con successo
recover.existing.description=La tua cassaforte "%s" è stata aggiunta alla lista. Non è stata necessaria alcuna azione di recupero.
##Vault Already Exists - Dialog
recover.alreadyExists.title=La cassaforte esiste già
recover.alreadyExists.message=Questa cassaforte è già stata aggiunta
recover.alreadyExists.description=La tua cassaforte "%s" è già presente nella lista, quindi non è stata aggiunta di nuovo.
##Invalid Selection - Dialog
recover.invalidSelection.title=Selezione non valida
recover.invalidSelection.message=Quello che hai selezionato non è una cassaforte
recover.invalidSelection.description=La cartella selezionata deve essere una cassaforte Cryptomator.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Centrale delle Casseforti
contactHubVaultOwner.message=Questa cassaforte è stata creata con Cryptomator Hub
contactHubVaultOwner.description=Contatta il proprietario della cassaforte per ripristinare il file mancante. Si può scaricare il modello di cassaforte da Cryptomator Hub.
##Dialog Title
recover.recoverVaultConfig.title=Ripristina la configurazione della cassaforte
recover.recoverMasterkey.title=Recupera il file con la chiave principale
## OnBoarding
recover.onBoarding.chooseMethod=Scegli metodo di recupero:
recover.onBoarding.useRecoveryKey=Usa la chiave di recupero
recover.onBoarding.usePassword=Utilizza la password
recover.onBoarding.intro=Assicurati di controllare quanto segue:
recover.onBoarding.pleaseConfirm=Prima di procedere, conferma che:
recover.onBoarding.otherwisePleaseConfirm=In caso contrario, conferma che:
recover.onBoarding.allMissing.intro=Se questa cassaforte è gestita da Cryptomator Hub, è il proprietario della cassaforte che deve ripristinarla.
recover.onBoarding.intro.ensure=Tutti i file sono completamente sincronizzati.
recover.onBoarding.affirmation=Ho letto e compreso questi requisiti
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=Hai la chiave di recupero e sai se sono state utilizzate configurazioni avanzate.
recover.onBoarding.intro.password=Hai la chiave di recupero e sai se sono state utilizzate configurazioni avanzate.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=Hai la chiave di ripristino della cassaforte.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=Questo valore deve corrispondere a quello usato prima del recupero per garantire la compatibilità con i dati precedentemente crittografati.
# Convert Vault
convertVault.title=Convertire Cassaforte
+27 -2
View File
@@ -391,8 +391,6 @@ main.vaultlist.contextMenu.unlockNow=今すぐ解錠
main.vaultlist.contextMenu.vaultoptions=金庫のオプションを表示
main.vaultlist.contextMenu.reveal=ドライブを表示
main.vaultlist.contextMenu.share=共有…
main.vaultlist.addVaultBtn.menuItemNew=新しい金庫を作成…
main.vaultlist.addVaultBtn.menuItemExisting=既存の金庫を開く…
##Notificaition
main.notification.updateAvailable=アップデートがあります。
main.notification.support=Cryptomator を支援する。
@@ -429,6 +427,7 @@ main.vaultDetail.missing.info=Cryptomator はこの場所に金庫を見つけ
main.vaultDetail.missing.recheck=再確認
main.vaultDetail.missing.remove=金庫のリストから削除...
main.vaultDetail.missing.changeLocation=金庫の場所を変更...
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=金庫をアップグレード
main.vaultDetail.migratePrompt=金庫にアクセスする前に、 金庫を新しい形式にアップグレードする必要があります
@@ -508,6 +507,32 @@ recoveryKey.recover.resetBtn=リセット
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=パスワードをリセットしました
recoveryKey.recover.resetSuccess.description=新しいパスワードで金庫の施錠ができます。
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
recover.invalidSelection.title=無効な選択
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=
##Dialog Title
## OnBoarding
recover.onBoarding.useRecoveryKey=回復キーを利用
recover.onBoarding.usePassword=パスワードを使用
recover.onBoarding.pleaseConfirm=続行する前に、以下を確認してください:
recover.onBoarding.intro.ensure=全てのファイルは完全に同期されています。
recover.onBoarding.affirmation=私はこれらの要件を読み、理解しました
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=金庫を変換
+21 -2
View File
@@ -394,8 +394,6 @@ main.vaultlist.contextMenu.unlockNow=지금 잠금 해제
main.vaultlist.contextMenu.vaultoptions=Vault 옵션 보기
main.vaultlist.contextMenu.reveal=드라이브 표시
main.vaultlist.contextMenu.share=공유하기…
main.vaultlist.addVaultBtn.menuItemNew=새 Vault 생성...
main.vaultlist.addVaultBtn.menuItemExisting=기존 Vault 열기...
main.vaultlist.showEventsButton.tooltip=이벤트 뷰어 열기
##Notificaition
main.notification.updateAvailable=업데이트가 있습니다.
@@ -433,6 +431,7 @@ main.vaultDetail.missing.info=Cryptomator가 이 경로에 있는 Vault를 찾
main.vaultDetail.missing.recheck=다시 시도
main.vaultDetail.missing.remove=Vault 목록에서 제거...
main.vaultDetail.missing.changeLocation=Vault 위치 변경
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Vault 업그레이드
main.vaultDetail.migratePrompt=Vault에 접근하기 전, 새로운 포맷으로 업그레이드가 필요합니다.
@@ -512,6 +511,26 @@ recoveryKey.recover.resetBtn=초기화
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=비밀번호 재설정 성공
recoveryKey.recover.resetSuccess.description=이제 해당 vault를 새 비밀번호로 잠금 해제할 수 있습니다.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub Vault
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Vault 변환
+57 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Šo datni var droši noņemt.
## Existing
addvaultwizard.existing.title=Pievienot esošu glabātavu
addvaultwizard.existing.instruction=Jāizvēlas esošas glabātavas datne "vault.cryptomator". Ja pastāv tikai datne ar nosaukumu "masterkey.cryptomator", tad jāatlasā tā.
addvaultwizard.existing.restore=Atjaunot…
addvaultwizard.existing.chooseBtn=Izvēlēties…
addvaultwizard.existing.filePickerTitle=Atlasīt glabātavas datni
addvaultwizard.existing.filePickerMimeDesc=Cryptomator glabātava
@@ -127,6 +128,7 @@ unlock.unlockBtn=Atslēgt
## Select
unlock.chooseMasterkey.message=Galvenās atslēgas datne nav atrasta
unlock.chooseMasterkey.description=Cryptomator nevarēja atrast galvenās atslēgas datni glabātavai "%s". Lūgums pašrocīgi izvēlēties atslēgas datni.
unlock.chooseMasterkey.restoreInstead=Tā vietā atjaunot galvenās atslēgas datni
unlock.chooseMasterkey.filePickerTitle=Atlasīt galvenās atslēgas datni
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator galvenā atslēga
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Atslēgt tagad
main.vaultlist.contextMenu.vaultoptions=Rādīt glabātavas iespējas
main.vaultlist.contextMenu.reveal=Atklāt disku
main.vaultlist.contextMenu.share=Kopīgot…
main.vaultlist.addVaultBtn.menuItemNew=Izveidot jaunu glabātavu...
main.vaultlist.addVaultBtn.menuItemExisting=Atvērt esošu glabātavu...
main.vaultlist.addVaultBtn.menuItemNew=Izveidot jaunu glabātavu
main.vaultlist.addVaultBtn.menuItemExisting=Atvērt esošu glabātavu
main.vaultlist.addVaultBtn.menuItemRecover=Atjaunot esošu glabātavu…
main.vaultlist.showEventsButton.tooltip=Atvērt notikumu skatu
##Notificaition
main.notification.updateAvailable=Ir pieejams atjauninājums.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=Cryptomator šajā ceļā nevarēja atrast glabāt
main.vaultDetail.missing.recheck=Pārbaudīt atkārtoti
main.vaultDetail.missing.remove=Noņemt no glabātavu saraksta…
main.vaultDetail.missing.changeLocation=Mainīt glabātavas atrašanās vietu…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=Trūkst glabātavas konfigurācijas.
main.vaultDetail.missingVaultConfig.restore=Atjaunot glabātavas konfigurāciju
### Needs Migration
main.vaultDetail.migrateButton=Jaunināt glabātavu
main.vaultDetail.migratePrompt=Glabātavu ir nepieciešams jaunināt uz jaunu veidolu, pirms tai varēs piekļūt
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=Pārveidot par uz paroli balstītu glabātavu
recoveryKey.display.title=Parādīt atkopes atslēgu
recoveryKey.create.message=Nepieciešama parole
recoveryKey.create.description=Jāievada "%s" parole, lai parādītu tās atkopes atslēgu.
recoveryKey.recover.description=Jāievada “%s” parole, lai atkoptu glabātavas konfigurāciju.
recoveryKey.display.description=Zemāk esošā atkopes atslēga var tikt izmantota, lai atjaunotu piekļuvi "%s":
recoveryKey.display.StorageHints=Tā ir jātur ļoti drošā vietā, piemēram:\n • jāglabā paroļu pārvaldniekā;\n • jāsaglabā USB zibatmiņā;\n • jāizdrukā uz papīra.
## Reset Password
@@ -509,9 +516,57 @@ recoveryKey.recover.invalidKey=Šī atkopes atslēga nav derīga
recoveryKey.printout.heading=Cryptomator atkopes atslēga\n"%s" \n
### Reset Password
recoveryKey.recover.resetBtn=Atiestatīt
recoveryKey.recover.recoverBtn=Atkopt
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Paroles atiestatīšana sekmīga
recoveryKey.recover.resetSuccess.description=Savu glabātavu var atslēgt ar jauno paroli.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetMasterkeyFileSuccess.description=Savu glabātavu tagad var atslēgt ar jauno paroli.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Glabātava pievienota
recover.existing.message=Glabātava tika sekmīgi pievienota
recover.existing.description=Glabātava “%s” tika pievienota glabātavu sarakstam. Atkope nebija nepieciešama.
##Vault Already Exists - Dialog
recover.alreadyExists.title=Glabātava jau pastāv
recover.alreadyExists.message=Šī glabātava jau ir pievienota
recover.alreadyExists.description=Glabātava “%s” jau ir glabātavu sarakstā, tādējādi tā netika pievienota vēlreiz.
##Invalid Selection - Dialog
recover.invalidSelection.title=Nederīga atlase
recover.invalidSelection.message=Atlasītais nav glabātava
recover.invalidSelection.description=Atlasītajai mapei jābūt derīgai Cryptomator glabātavai.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub glabātava
contactHubVaultOwner.message=Šī glabātava tika izveidota ar Cryptomator Hub
contactHubVaultOwner.description=Lūgums vērsties pie glabātavas īpašnieka, lai atjaunotu trūkstošo datni. Glabātavas sagatavi var lejupielādēt no Cryptomator Hub.
##Dialog Title
recover.recoverVaultConfig.title=Atkopt glabātavas konfigurāciju
recover.recoverMasterkey.title=Atkopt galveno atslēgu
## OnBoarding
recover.onBoarding.chooseMethod=Jāizvēlas atkopes veids:
recover.onBoarding.useRecoveryKey=Izmantot atkopes atslēgu
recover.onBoarding.usePassword=Izmantot paroli
recover.onBoarding.intro=Jāpārliecinās, ka ir pārbaudīts šis:
recover.onBoarding.pleaseConfirm=Pirms turpināšanas lūgums apstiprināt, ka:
recover.onBoarding.otherwisePleaseConfirm=Pretējā gadījumā lūgums apstiprināt, ka:
recover.onBoarding.allMissing.intro=Ja šo glabātavu pārvalda Cryptomator Hub, tā ir jāatjauno glabātavas īpašniekam.
recover.onBoarding.intro.ensure=Visas datnes ir pilnībā sinhronizētas.
recover.onBoarding.affirmation=Es izlasīju un saprotu šīs prasības
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=Tev ir atkopes atslēga, un Tu zini, vai tika izmantoti lietpratēju iestatījumi.
recover.onBoarding.intro.password=Tev ir glabātavas atslēga, un Tu zini, vai tika izmantoti lietpratēju iestatījumi.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=Tev ir glabātavas atkopes atslēga.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=Šai vērtībai ir jāatbilst tai, kas tika izmantota pirms atkopes, lai nodrošinatu saderību ar iepriekš šifrētajiem datiem.
# Convert Vault
convertVault.title=Pārveidot glabātavu
@@ -135,6 +135,7 @@ main.vaultlist.contextMenu.lock=Заклучи
### Unlocked
main.vaultDetail.lockBtn=Заклучи
### Missing
### Missing Vault Config
### Needs Migration
### Error
@@ -155,6 +156,25 @@ vaultOptions.mount.mountPoint.directoryPickerButton=Избор…
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
@@ -87,6 +87,7 @@
### Locked
### Unlocked
### Missing
### Missing Vault Config
### Needs Migration
### Error
@@ -105,6 +106,25 @@
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
@@ -419,6 +419,7 @@ main.vaultDetail.missing.info=Cryptomator kunne ikke finne et hvelv på denne s
main.vaultDetail.missing.recheck=Kontroller igjen
main.vaultDetail.missing.remove=Fjern fra hvelvlisten…
main.vaultDetail.missing.changeLocation=Endre hvelvplassering…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Oppgrader hvelv
main.vaultDetail.migratePrompt=Hvelvet ditt må oppgraderes til et nytt format før du kan få tilgang til det
@@ -498,6 +499,26 @@ recoveryKey.recover.resetBtn=Nullstill
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Passordnullstillingen vellykket
recoveryKey.recover.resetSuccess.description=Du kan låse opp hvelvet med det nye passordet.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub hvelv
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Konverter hvelvet
+59 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Voel je vrij om dit bestand te verwijderen.
## Existing
addvaultwizard.existing.title=Bestaande kluis toevoegen
addvaultwizard.existing.instruction=Kies het "vault.cryptomator"-bestand van uw bestaande kluis. Indien er enkel een bestand genaamd "masterkey.cryptomator" anwezig is, kies deze dan in de plaats.
addvaultwizard.existing.restore=Herstel…
addvaultwizard.existing.chooseBtn=Kies…
addvaultwizard.existing.filePickerTitle=Kies kluisbestand
addvaultwizard.existing.filePickerMimeDesc=Cryptomator kluis
@@ -127,6 +128,7 @@ unlock.unlockBtn=Ontgrendel
## Select
unlock.chooseMasterkey.message=Masterkey-bestand niet gevonden
unlock.chooseMasterkey.description=Kon het sleutelbestand voor deze kluis niet vinden op de gewenste locatie. Kies het sleutelbestand handmatig.
unlock.chooseMasterkey.restoreInstead=Herstel inplaats daarvan het hoofdsleutelbestand
unlock.chooseMasterkey.filePickerTitle=Selecteer het Masterkey-bestand
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Nu Ontgrendelen
main.vaultlist.contextMenu.vaultoptions=Laat kluisinstellingen zien
main.vaultlist.contextMenu.reveal=Toon Schijf
main.vaultlist.contextMenu.share=Delen…
main.vaultlist.addVaultBtn.menuItemNew=Nieuwe Kluis Aanmaken...
main.vaultlist.addVaultBtn.menuItemExisting=Open Bestaande Kluis...
main.vaultlist.addVaultBtn.menuItemNew=Maak een nieuwe kluis aan…
main.vaultlist.addVaultBtn.menuItemExisting=Open bestaande kluis
main.vaultlist.addVaultBtn.menuItemRecover=Herstel bestaande kluis…
main.vaultlist.showEventsButton.tooltip=Afspraakweergave openen
##Notificaition
main.notification.updateAvailable=Update beschikbaar.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=Cryptomator kon op dit pad geen kluis vinden.
main.vaultDetail.missing.recheck=Controleer nog eens
main.vaultDetail.missing.remove=Verwijderen van kluislijst…
main.vaultDetail.missing.changeLocation=Verander de locatie van de kluis…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=Het configuratiebestand van de kluis ontbreekt.
main.vaultDetail.missingVaultConfig.restore=Herstel het configuratiebestand van de kluis
### Needs Migration
main.vaultDetail.migrateButton=Kluis upgraden
main.vaultDetail.migratePrompt=Uw kluis moet worden bijgewerkt naar een nieuw formaat, voordat u deze kunt openen
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=Converteren naar Wachtwoord-gebaseerde kluis
recoveryKey.display.title=Toon herstelsleutel
recoveryKey.create.message=Wachtwoord vereist
recoveryKey.create.description=Voer uw wachtwoord in om de herstelsleutel voor "%s" te tonen:
recoveryKey.recover.description=Voer het wachtwoord voor "%s" in om de kluis te herstellen.
recoveryKey.display.description=De volgende herstelsleutel kan worden gebruikt om "%s" te herstellen:
recoveryKey.display.StorageHints=Bewaar het op een veilige plek, bv:\n • Bewaar het in een wachtwoordmanager\n • Sla het op op een USB-stick\n • Print het op papier
## Reset Password
@@ -509,9 +516,59 @@ recoveryKey.recover.invalidKey=Deze herstelsleutel is niet geldig
recoveryKey.printout.heading=Cryptomator herstelsleutel\n"%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Resetten
recoveryKey.recover.recoverBtn=Herstellen
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Wachtwoord resetten geslaagd
recoveryKey.recover.resetSuccess.description=Je kunt je kluis ontgrendelen met het nieuwe wachtwoord.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetVaultConfigSuccess.message=Kluis configuratie hersteld
recoveryKey.recover.resetMasterkeyFileSuccess.message=Masterkey-bestand hersteld
recoveryKey.recover.resetMasterkeyFileSuccess.description=Je kunt je kluis nu ontgrendelen met je wachtwoord.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Kluis toegevoegd
recover.existing.message=De kluis is met succes toegevoegd
recover.existing.description=Uw kluis "%s" is toegevoegd aan de lijst van kluizen. Er was geen herstelproces nodig.
##Vault Already Exists - Dialog
recover.alreadyExists.title=Kluis bestaat al
recover.alreadyExists.message=Deze kluis is al toegevoegd
recover.alreadyExists.description=Je kluis "%s" is al aanwezig in je kluis lijst en is daarom niet meer toegevoegd.
##Invalid Selection - Dialog
recover.invalidSelection.title=Ongeldige selectie
recover.invalidSelection.message=Jouw selectie is geen kluis
recover.invalidSelection.description=De geselecteerde map moet een geldige Cryptomator kluis zijn.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub Kluis
contactHubVaultOwner.message=Deze kluis is gemaakt met Cryptomator Hub
contactHubVaultOwner.description=Neem contact op met de eigenaar van de kluis om het ontbrekende bestand te herstellen. Ze kunnen de kluissjabloon downloaden van Cryptomator Hub.
##Dialog Title
recover.recoverVaultConfig.title=Herstel het configuratiebestand van de kluis
recover.recoverMasterkey.title=Masterkey herstellen
## OnBoarding
recover.onBoarding.chooseMethod=Kies een herstelmethode:
recover.onBoarding.useRecoveryKey=Herstelsleutel gebruiken
recover.onBoarding.usePassword=Wachtwoord gebruiken
recover.onBoarding.intro=Zorg ervoor dat je het volgende controleert:
recover.onBoarding.pleaseConfirm=Vooraleer verder te gaan, bevestig dat:
recover.onBoarding.otherwisePleaseConfirm=Anders bevestig dat:
recover.onBoarding.allMissing.intro=Als deze kluis wordt beheerd door Cryptomator Hub, moet de eigenaar van de kluis deze voor je herstellen.
recover.onBoarding.intro.ensure=Alle bestanden zijn volledig gesynchroniseerd.
recover.onBoarding.affirmation=Ik heb deze vereisten gelezen en begrepen
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=Je beschikt over de herstelsleutel en weet of de geavanceerde instellingen zijn gebruikt.
recover.onBoarding.intro.password=Je beschikt over de herstelsleutel en weet of de geavanceerde instellingen zijn gebruikt.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=Je hebt de kluis herstelsleutel.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=Deze waarde moet overeenkomen met de waarde voor het herstel om de compatibiliteit met eerder versleutelde gegevens te waarborgen.
# Convert Vault
convertVault.title=Kluis converteren
@@ -202,6 +202,7 @@ main.vaultDetail.throughput.mbps=%.1f MiB/s
### Missing
main.vaultDetail.missing.info=Cryptomator kunne ikkje finna ein kvelv på denne søkastien.
main.vaultDetail.missing.recheck=Kontroller igjen
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Oppgrader kvelv
main.vaultDetail.migratePrompt=Kvelven din må oppgraderast til eit nytt format før du kan få tilgang til det
@@ -254,6 +255,25 @@ recoveryKey.display.StorageHints=Ta vare på han ein veldig sikker stad, t.d. ve
recoveryKey.printout.heading=Cryptomator-gjenopprettingsnøkkel\n"%s"\n
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
@@ -87,6 +87,7 @@
### Locked
### Unlocked
### Missing
### Missing Vault Config
### Needs Migration
### Error
@@ -105,6 +106,25 @@
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
+21 -2
View File
@@ -329,8 +329,6 @@ main.vaultlist.contextMenu.unlockNow=ਹੁਣੇ ਅਣ-ਲਾਕ ਕਰੋ
main.vaultlist.contextMenu.vaultoptions=ਵਾਲਟ ਚੋਣਾਂ ਨੂੰ ਵੇਖਾਓ
main.vaultlist.contextMenu.reveal=ਡਰਾਇਵ ਦਿਖਾਓ
main.vaultlist.contextMenu.share=…ਸਾਂਝਾ ਕਰੋ
main.vaultlist.addVaultBtn.menuItemNew=...ਨਵਾਂ ਵਾਲਟ ਬਣਾਓ
main.vaultlist.addVaultBtn.menuItemExisting=...ਮੌਜੂਦਾ ਵਾਲਟ ਨੂੰ ਖੋਲ੍ਹੋ
##Notificaition
main.notification.updateAvailable=ਅੱਪਡੇਟ ਮੌਜੂਦ ਹੈ।
main.notification.support=Cryptomator ਲਈ ਸਹਿਯੋਗ।
@@ -362,6 +360,7 @@ main.vaultDetail.missing.info=Cryptomator ਇਸ ਮਾਗਰ ਉੱਤੇ ਵ
main.vaultDetail.missing.recheck=ਮੁੜ-ਜਾਂਚੋ
main.vaultDetail.missing.remove=ਵਾਲਟ ਸੂਚੀ ਤੋਂ ਹਟਾਓ…
main.vaultDetail.missing.changeLocation=ਵਾਲਟ ਟਿਕਾਣੇ ਨੂੰ ਬਦਲੋ…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=ਵਾਲਟ ਅੱਪਗਰੇਡ ਕਰੋ
main.vaultDetail.migratePrompt=ਤੁਹਾਡੇ ਵਾਲਟ ਨੂੰ ਵਰਤੇ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਨਵੇਂ ਫਾਰਮੈਟ ਲਈ ਅੱਪਗਰੇਡ ਕਰਨ ਦੀ ਲੋੜ ਹੈ
@@ -439,6 +438,26 @@ recoveryKey.recover.resetBtn=ਰੀਸੈੱਟ ਕਰੋ
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=ਪਾਸਵਰਡ ਨੂੰ ਕਾਮਯਾਬੀ ਨਾਲ ਮੁੜ-ਸੈੱਟ ਕੀਤਾ ਗਿਆ
recoveryKey.recover.resetSuccess.description=ਤੁਸੀਂ ਆਪਣੇ ਵਾਲਟ ਨੂੰ ਨਵੇਂ ਪਾਸਵਰਡ ਨਾਲ ਖੋਲ੍ਹ ਸਕਦੇ ਹੋ
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub ਵਾਲਟ
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=ਵਾਲਟ ਨੂੰ ਬਦਲੋ
+21 -2
View File
@@ -394,8 +394,6 @@ main.vaultlist.contextMenu.unlockNow=Odblokuj teraz
main.vaultlist.contextMenu.vaultoptions=Pokaż opcje sejfu
main.vaultlist.contextMenu.reveal=Otwórz lokalizację
main.vaultlist.contextMenu.share=Udostępnij…
main.vaultlist.addVaultBtn.menuItemNew=Utwórz Nowy Sejf...
main.vaultlist.addVaultBtn.menuItemExisting=Otwórz Istniejący Sejf...
main.vaultlist.showEventsButton.tooltip=Otwórz widok wydarzeń
##Notificaition
main.notification.updateAvailable=Dostępna aktualizacja.
@@ -433,6 +431,7 @@ main.vaultDetail.missing.info=Cryptomator nie mógł znaleźć sejfu w tej lokal
main.vaultDetail.missing.recheck=Ponów próbę
main.vaultDetail.missing.remove=Usuń z listy sejfów…
main.vaultDetail.missing.changeLocation=Zmień lokalizację sejfu…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Aktualizuj sejf
main.vaultDetail.migratePrompt=Twój sejf musi zostać zaktualizowany do nowego formatu, zanim będziesz mógł go używać
@@ -512,6 +511,26 @@ recoveryKey.recover.resetBtn=Resetuj
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Hasło zostało zresetowane
recoveryKey.recover.resetSuccess.description=Możesz odblokować sejf przy użyciu nowego hasła.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub sejfów
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Konwertuj sejf
+57 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Sinta-se livre para remover este ficheiro.
## Existing
addvaultwizard.existing.title=Adicionar cofre existente
addvaultwizard.existing.instruction=Escolha o ficheiro "vault.cryptomator" do seu cofre. Se encontrar unicamente o ficheiro "masterkey.cryptomator", selecione-o.
addvaultwizard.existing.restore=Restaurar…
addvaultwizard.existing.chooseBtn=Escolher…
addvaultwizard.existing.filePickerTitle=Selecionar o ficheiro do cofre
addvaultwizard.existing.filePickerMimeDesc=Cofre Cryptomator
@@ -127,6 +128,7 @@ unlock.unlockBtn=Desbloquear
## Select
unlock.chooseMasterkey.message=Chave Mestra não encontrada
unlock.chooseMasterkey.description=Não foi possível encontrar o ficheiro masterkey no local predefinido para este cofre. Por favor, escolha o ficheiro chave manualmente.
unlock.chooseMasterkey.restoreInstead=Restaure o ficheiro da chave mestra
unlock.chooseMasterkey.filePickerTitle=Selecionar ficheiro MasterKey
unlock.chooseMasterkey.filePickerMimeDesc=Chave Mestra Cryptomator
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Desbloquear agora
main.vaultlist.contextMenu.vaultoptions=Mostrar opções do Cofre
main.vaultlist.contextMenu.reveal=Revelar unidade
main.vaultlist.contextMenu.share=Partilhar…
main.vaultlist.addVaultBtn.menuItemNew=Criar novo cofre...
main.vaultlist.addVaultBtn.menuItemExisting=Abrir cofre existente...
main.vaultlist.addVaultBtn.menuItemNew=Criar novo cofre
main.vaultlist.addVaultBtn.menuItemExisting=Abrir cofre existente
main.vaultlist.addVaultBtn.menuItemRecover=Recuperar cofre existente…
main.vaultlist.showEventsButton.tooltip=Abrir visualização do evento
##Notificaition
main.notification.updateAvailable=A atualização está disponível.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=O Cryptomator não conseguiu encontrar um cofre ne
main.vaultDetail.missing.recheck=Verificar novamente
main.vaultDetail.missing.remove=Remover da Lista de Cofres…
main.vaultDetail.missing.changeLocation=Mudar localização do Cofre…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=A configuração do cofre está em falta.
main.vaultDetail.missingVaultConfig.restore=Restaurar configuração do cofre
### Needs Migration
main.vaultDetail.migrateButton=Atualizar Cofre
main.vaultDetail.migratePrompt=O cofre precisa de ser atualizado para um novo formato, antes que possa acessá-lo
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=Converter para cofre baseado em palavras-passe
recoveryKey.display.title=Mostrar chave de recuperação
recoveryKey.create.message=Palavra-passe necessária
recoveryKey.create.description=Inserir a palavra passe de "%s" para mostrar a chave de recuperação.
recoveryKey.recover.description=Introduza a palavra-passe de "%s" para recuperar a configuração do cofre.
recoveryKey.display.description=Esta chave de recuperação pode ser usada para restaurar acesso a "%s":
recoveryKey.display.StorageHints=Guarde-a num lugar muito seguro, por exemplo:\n • Armazená-la usando um gerenciador de senhas\n • Guarde-a numa pen USB\n • Imprima-a em papel
## Reset Password
@@ -509,9 +516,57 @@ recoveryKey.recover.invalidKey=Esta chave de recupreação não está certa
recoveryKey.printout.heading=A chave de recuperação do Cryptomator \n"%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Repor
recoveryKey.recover.recoverBtn=Recuperar
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Palavra-passe redefinida com sucesso
recoveryKey.recover.resetSuccess.description=Você pode desbloquear o seu cofre com a nova senha.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetMasterkeyFileSuccess.description=Agora pode desbloquear o seu cofre com a sua palavra-passe.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Cofre adicionado
recover.existing.message=O cofre foi adicionado com sucesso
recover.existing.description=O seu cofre "%s" foi adicionado à lista de cofres. Nenhum processo de recuperação foi necessário.
##Vault Already Exists - Dialog
recover.alreadyExists.title=O cofre já existe
recover.alreadyExists.message=Este cofre já foi adicionado
recover.alreadyExists.description=O seu cofre "%s" já está presente na lista de cofres, por isso, não foi adicionado novamente.
##Invalid Selection - Dialog
recover.invalidSelection.title=Seleção inválida
recover.invalidSelection.message=A sua seleção não é um cofre
recover.invalidSelection.description=A pasta selecionada precisa ser um cofre válido do Cryptomator.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Cofre do Hub
contactHubVaultOwner.message=Este cofre foi criado com o Cryptomator Hub
contactHubVaultOwner.description=Contacte o proprietário do cofre para restaurar o ficheiro perdido. Pode descarregar o modelo do cofre no Cryptomator Hub.
##Dialog Title
recover.recoverVaultConfig.title=Recuperar configuração do cofre
recover.recoverMasterkey.title=Recuperar chave mestra
## OnBoarding
recover.onBoarding.chooseMethod=Escolher método de recuperação:
recover.onBoarding.useRecoveryKey=Usar chave de recuperação
recover.onBoarding.usePassword=Usar palavra-passe
recover.onBoarding.intro=Certifique-se de verificar o seguinte:
recover.onBoarding.pleaseConfirm=Antes de prosseguir, por favor confirme que:
recover.onBoarding.otherwisePleaseConfirm=Caso contrário, confirme que:
recover.onBoarding.allMissing.intro=Se este cofre for gerido pelo Cryptomator Hub, o proprietário do cofre deverá restaurá-lo para si.
recover.onBoarding.intro.ensure=Todos os ficheiros estão totalmente sincronizados.
recover.onBoarding.affirmation=Li e compreendi estes requisitos
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=Tem a chave de recuperação e sabe se foram utilizadas as definições de especialista.
recover.onBoarding.intro.password=Tem a palavra-passe do cofre e sabe se foram usadas configurações de especialista.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=Você tem a chave de recuperação do cofre.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=Este valor deve corresponder ao utilizado antes da recuperação para garantir a compatibilidade com os dados previamente encriptados.
# Convert Vault
convertVault.title=Converter cofre
@@ -394,8 +394,6 @@ main.vaultlist.contextMenu.unlockNow=Desbloquear Agora
main.vaultlist.contextMenu.vaultoptions=Exibir Opções de Cofre
main.vaultlist.contextMenu.reveal=Revelar Volume
main.vaultlist.contextMenu.share=Compartilhar…
main.vaultlist.addVaultBtn.menuItemNew=Novo Cofre...
main.vaultlist.addVaultBtn.menuItemExisting=Abrir Cofre Existente...
main.vaultlist.showEventsButton.tooltip=Abrir visualização de evento
##Notificaition
main.notification.updateAvailable=Atualização disponível.
@@ -433,6 +431,7 @@ main.vaultDetail.missing.info=O Cryptomator não encontrou um cofre neste caminh
main.vaultDetail.missing.recheck=Verificar novamente
main.vaultDetail.missing.remove=Remover da lista de cofres…
main.vaultDetail.missing.changeLocation=Alterar Localização do Cofre…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Atualizar Cofre
main.vaultDetail.migratePrompt=Seu cofre precisa ser atualizado para um novo formato antes de poder acessá-lo
@@ -512,6 +511,26 @@ recoveryKey.recover.resetBtn=Redefinir
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Senha redefinida com sucesso
recoveryKey.recover.resetSuccess.description=Você pode desbloquear o seu cofre com a nova senha.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Cofre do Hub
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Converter Cofre
+21 -2
View File
@@ -390,8 +390,6 @@ main.vaultlist.contextMenu.unlockNow=Deblochează acum
main.vaultlist.contextMenu.vaultoptions=Arată opțiunile seifului
main.vaultlist.contextMenu.reveal=Dezvăluie unitatea
main.vaultlist.contextMenu.share=Distribuie…
main.vaultlist.addVaultBtn.menuItemNew=Creare seif nou...
main.vaultlist.addVaultBtn.menuItemExisting=Deschide un seif existent...
##Notificaition
main.notification.updateAvailable=O nouă versiune este valabilă.
main.notification.support=Susține Cryptomator.
@@ -425,6 +423,7 @@ main.vaultDetail.missing.info=Cryptomator nu a putut găsi un seif pe această c
main.vaultDetail.missing.recheck=Verifică din nou
main.vaultDetail.missing.remove=Eliminați din lista de seifuri…
main.vaultDetail.missing.changeLocation=Schimbați locația seifului…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Îmbunătățește seiful
main.vaultDetail.migratePrompt=Înainte de a-l putea accesa, seiful dumneavoastră trebuie actualizat la format nou
@@ -504,6 +503,26 @@ 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.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Seif Hub
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Transformă seiful
+57 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Этот файл можно удалить
## Existing
addvaultwizard.existing.title=Добавить имеющееся хранилище
addvaultwizard.existing.instruction=Выберите файл "vault.cryptomator" существующего хранилища. Если имеется только файл "masterkey.cryptomator", выберите его.
addvaultwizard.existing.restore=Восстановить…
addvaultwizard.existing.chooseBtn=Выбрать…
addvaultwizard.existing.filePickerTitle=Выберите файл хранилища
addvaultwizard.existing.filePickerMimeDesc=Хранилище Cryptomator
@@ -127,6 +128,7 @@ unlock.unlockBtn=Разблокировать
## Select
unlock.chooseMasterkey.message=Файл Masterkey не найден
unlock.chooseMasterkey.description=Не удалось найти файл Masterkey для хранилища "%s". Выберите ключевой файл вручную.
unlock.chooseMasterkey.restoreInstead=Восстановить файл Masterkey
unlock.chooseMasterkey.filePickerTitle=Выберите файл MasterKey
unlock.chooseMasterkey.filePickerMimeDesc=Мастер-ключ Cryptomator
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Разблокировать
main.vaultlist.contextMenu.vaultoptions=Параметры хранилища
main.vaultlist.contextMenu.reveal=Показать диск
main.vaultlist.contextMenu.share=Поделиться…
main.vaultlist.addVaultBtn.menuItemNew=Создать хранилище...
main.vaultlist.addVaultBtn.menuItemExisting=Открыть имеющееся хранилище...
main.vaultlist.addVaultBtn.menuItemNew=Создать хранилище
main.vaultlist.addVaultBtn.menuItemExisting=Открыть имеющееся хранилище
main.vaultlist.addVaultBtn.menuItemRecover=Восстановить имеющееся хранилище…
main.vaultlist.showEventsButton.tooltip=Открыть просмотр события
##Notificaition
main.notification.updateAvailable=Есть обновление.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=Cryptomator не смог найти хранил
main.vaultDetail.missing.recheck=Перепроверить
main.vaultDetail.missing.remove=Удалить из списка хранилищ…
main.vaultDetail.missing.changeLocation=Изменить расположение хранилища…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=Отсутствует конфигурация хранилища.
main.vaultDetail.missingVaultConfig.restore=Восстановить настройки хранилища
### Needs Migration
main.vaultDetail.migrateButton=Обновить хранилище
main.vaultDetail.migratePrompt=Чтобы получить доступ к хранилищу, его нужно преобразовать в новый формат
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=Преобразовать в хранилище с
recoveryKey.display.title=Показать ключ восстановления
recoveryKey.create.message=Требуется пароль
recoveryKey.create.description=Введите пароль для "%s", чтобы показать его ключ восстановления.
recoveryKey.recover.description=Введите пароль для "%s", чтобы восстановить конфигурацию хранилища.
recoveryKey.display.description=Ключ для восстановления доступа к "%s":
recoveryKey.display.StorageHints=Храните его в надёжном месте, например:\n • в диспетчере паролей\n • на флеш-накопителе USB\n • распечатанным на бумаге
## Reset Password
@@ -509,9 +516,57 @@ recoveryKey.recover.invalidKey=Этот ключ восстановления н
recoveryKey.printout.heading=Ключ восстановления Cryptomator\n"%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Сброс
recoveryKey.recover.recoverBtn=Восстановить
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Пароль успешно сброшен
recoveryKey.recover.resetSuccess.description=Вы можете разблокировать хранилище новым паролем.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetMasterkeyFileSuccess.description=Теперь вы можете разблокировать хранилище с помощью пароля.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Хранилище добавлено
recover.existing.message=Хранилище успешно добавлено
recover.existing.description=Хранилище "%s" добавлено в список. Восстановление не потребовалось.
##Vault Already Exists - Dialog
recover.alreadyExists.title=Хранилище уже существует
recover.alreadyExists.message=Это хранилище уже добавлено
recover.alreadyExists.description=Хранилище "%s" уже есть в списке и поэтому не было добавлено снова.
##Invalid Selection - Dialog
recover.invalidSelection.title=Неверный выбор
recover.invalidSelection.message=Выбрано не хранилище
recover.invalidSelection.description=Выбранная папка должна быть корректным хранилищем Cryptomator.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Хаб-хранилище
contactHubVaultOwner.message=Это хранилище было создано с помощью хаба Cryptomator
contactHubVaultOwner.description=Свяжитесь с владельцем хранилища для восстановления отсутствующего файла. Шаблон хранилища можно загрузить из хаба Cryptomator.
##Dialog Title
recover.recoverVaultConfig.title=Восстановление конфигурации хранилища
recover.recoverMasterkey.title=Восстановить Masterkey
## OnBoarding
recover.onBoarding.chooseMethod=Выберите метод восстановления:
recover.onBoarding.useRecoveryKey=Использовать ключ восстановления
recover.onBoarding.usePassword=Использовать пароль
recover.onBoarding.intro=Проверьте следующее:
recover.onBoarding.pleaseConfirm=Прежде чем продолжить, подтвердите, что:
recover.onBoarding.otherwisePleaseConfirm=В противном случае подтвердите, что:
recover.onBoarding.allMissing.intro=Если это хранилище управляется хабом Cryptomator, владелец хранилища должен восстановить его для вас.
recover.onBoarding.intro.ensure=Все файлы полностью синхронизированы.
recover.onBoarding.affirmation=Требования прочитаны и понятны
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=У вас есть ключ восстановления и вы знаете, что были использованы экспертные настройки.
recover.onBoarding.intro.password=У вас есть пароль хранилища и вы знаете, что были использованы экспертные настройки.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=У вас есть ключ восстановления хранилища.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=Это значение должно соответствовать значению перед восстановлением, чтобы гарантировать совместимость с ранее зашифрованными данными.
# Convert Vault
convertVault.title=Преобразовать хранилище
@@ -104,6 +104,7 @@ hub.registerSuccess.unlockBtn=අගුළුහරින්න
### Locked
### Unlocked
### Missing
### Missing Vault Config
### Needs Migration
### Error
@@ -122,6 +123,25 @@ hub.registerSuccess.unlockBtn=අගුළුහරින්න
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
+30 -1
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Tento súbor môžete kedykoľvek odstráni
## Existing
addvaultwizard.existing.title=Pridať existujúci trezor
addvaultwizard.existing.instruction=Zvoľte "vault.cryptomator" súbor Vášho existujúceho trezora. Ak existuje iba súbor s menom "masterkey.cryptomator", vyberte ho namiesto.
addvaultwizard.existing.restore=Obnoviť…
addvaultwizard.existing.chooseBtn=Vybrať…
addvaultwizard.existing.filePickerTitle=Zvoľte súbor trezora
addvaultwizard.existing.filePickerMimeDesc=Trezor Cryptomátora
@@ -388,7 +389,6 @@ main.vaultlist.contextMenu.vaultoptions=Ukáž možnosti trezora
main.vaultlist.contextMenu.reveal=Odkry disk
main.vaultlist.contextMenu.share=Zdieľať…
main.vaultlist.addVaultBtn.menuItemNew=Vytvoriť Nový trezor…
main.vaultlist.addVaultBtn.menuItemExisting=Otvoriť Existujúci trezor...
main.vaultlist.showEventsButton.tooltip=Otvoriť zobrazenie udalosti
##Notificaition
main.notification.updateAvailable=Aktualizácia je k dispozícii.
@@ -426,6 +426,7 @@ main.vaultDetail.missing.info=Cryptomator nevie nájsť trezor na tejto ceste.
main.vaultDetail.missing.recheck=Prekontrolovať
main.vaultDetail.missing.remove=Odstrániť zo zoznamu trezora…
main.vaultDetail.missing.changeLocation=Zmeniť umiestnenie trezora…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Aktualizácia trezora
main.vaultDetail.migratePrompt=Váš trezor vyžaduje aktualizáciu na nový formát predtým ako ho použijete
@@ -501,9 +502,37 @@ recoveryKey.recover.invalidKey=Toto je neplatný kľúč obnovy
recoveryKey.printout.heading=Kľúč obnovy Cryptomator-a\n "%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Resetovať
recoveryKey.recover.recoverBtn=Obnov
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Heslo úspešne zresetované
recoveryKey.recover.resetSuccess.description=Môžte odomknúť trezor s novým heslom.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetVaultConfigSuccess.message=Konfigurácia peňaženky obnovená
recoveryKey.recover.resetMasterkeyFileSuccess.message=Hlavný kľuč obnovený
recoveryKey.recover.resetMasterkeyFileSuccess.description=Teraz môžte odomknúť Váš trezor s heslom.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
recover.alreadyExists.title=Trezor už existuje
##Invalid Selection - Dialog
recover.invalidSelection.title=Neplatný výber
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub trezora
##Dialog Title
## OnBoarding
recover.onBoarding.usePassword=Použite heslo
recover.onBoarding.intro.ensure=Všetky súbory sú plne synchronizované.
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Konvertovať trezor
@@ -143,6 +143,7 @@ main.vaultDetail.share=Deli…
main.vaultDetail.lockBtn=Zakleni
main.vaultDetail.locateEncryptedFileBtn=Poišči šifrirano datoteko
### Missing
### Missing Vault Config
### Needs Migration
### Error
@@ -166,6 +167,26 @@ recoveryKey.recover.wrongKey=Ta obnovitveni ključ se ujema z drugim trezorjem
recoveryKey.recover.invalidKey=Obnovitveni ključ ni pravilen
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hub trezor
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
@@ -245,6 +245,7 @@ main.vaultDetail.missing.info=Cryptomator није пронашао сеф на
main.vaultDetail.missing.recheck=Провери поново
main.vaultDetail.missing.remove=Удаљи са листе сефова…
main.vaultDetail.missing.changeLocation=Промени локацију сефа…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Nadogradi sef
main.vaultDetail.migratePrompt=Да бисте приступили вашем сефу, он мора бити надограђен на нови формат
@@ -298,6 +299,25 @@ recoveryKey.recover.correctKey=Ово је исправан резервни к
recoveryKey.printout.heading=Cryptomator Резервни Кључ\n"%s"\n
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
@@ -204,6 +204,7 @@ main.vaultDetail.unlockNowBtn=Otključaj sada
main.vaultDetail.revealBtn=Otvori disk
main.vaultDetail.lockBtn=Zaključaj
### Missing
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Nadogradi sef
### Error
@@ -228,6 +229,25 @@ vaultOptions.masterkey.changePasswordBtn=Promena lozinke
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
+41 -2
View File
@@ -96,6 +96,7 @@ addvault.new.readme.accessLocation.4=Du kan ta bort denna fil.
## Existing
addvaultwizard.existing.title=Öppna befintligt valv
addvaultwizard.existing.instruction=Välj filen "vault.cryptomator" i ditt befintliga valv. Om det endast finns en fil som heter "masterkey.cryptomator", välj den istället.
addvaultwizard.existing.restore=Återställ…
addvaultwizard.existing.chooseBtn=Välj…
addvaultwizard.existing.filePickerTitle=Välj valvfil
addvaultwizard.existing.filePickerMimeDesc=Cryptomator valv
@@ -127,6 +128,7 @@ unlock.unlockBtn=Lås upp
## Select
unlock.chooseMasterkey.message=Filen med huvudnyckeln hittades inte
unlock.chooseMasterkey.description=Kunde inte hitta Masterkey-filen för detta valv på förväntad plats. Välj filen manuellt.
unlock.chooseMasterkey.restoreInstead=Återställ huvudnyckeln istället
unlock.chooseMasterkey.filePickerTitle=Välj Masterkey-fil
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator huvudnyckel
## Success
@@ -394,8 +396,9 @@ main.vaultlist.contextMenu.unlockNow=Lås upp nu
main.vaultlist.contextMenu.vaultoptions=Visa inställningar för valv
main.vaultlist.contextMenu.reveal=Visa enhet
main.vaultlist.contextMenu.share=Dela…
main.vaultlist.addVaultBtn.menuItemNew=Skapa nytt valv...
main.vaultlist.addVaultBtn.menuItemExisting=Öppna befintligt valv...
main.vaultlist.addVaultBtn.menuItemNew=Skapa nytt valv
main.vaultlist.addVaultBtn.menuItemExisting=Öppna befintligt valv
main.vaultlist.addVaultBtn.menuItemRecover=Återställ befintligt valv…
main.vaultlist.showEventsButton.tooltip=Öppna händelsevy
##Notificaition
main.notification.updateAvailable=Uppdatering tillgänglig.
@@ -433,6 +436,9 @@ main.vaultDetail.missing.info=Cryptomator kunde inte hitta någt valv i denna s
main.vaultDetail.missing.recheck=Kontrollera igen
main.vaultDetail.missing.remove=Ta bort från listan…
main.vaultDetail.missing.changeLocation=Ändra valvets plats…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=Valvkonfigurationen saknas.
main.vaultDetail.missingVaultConfig.restore=Återställ valvkonfiguration
### Needs Migration
main.vaultDetail.migrateButton=Uppgradera valv
main.vaultDetail.migratePrompt=Ditt valv behöver uppgraderas till ett nytt format innan du kan använda det
@@ -497,6 +503,7 @@ vaultOptions.hub.convertBtn=Konvertera till Lösenordsbaserat Valv
recoveryKey.display.title=Visa återställningsnyckel
recoveryKey.create.message=Lösenord krävs
recoveryKey.create.description=Ange ditt lösenord för att visa återställningsnyckeln för "%s":
recoveryKey.recover.description=Ange lösenordet för "%s" för att återställa valvkonfigurationen.
recoveryKey.display.description=Använd denna återställningsnyckel för att återställa åtkomst till "%s":
recoveryKey.display.StorageHints=Spara den på en säker plats, t.ex:\n • I en lösenordshanterare\n • På ett USB-minne (förvara säkert) \n • Skriv ut på ett papper (förvara säkert)
## Reset Password
@@ -509,9 +516,41 @@ recoveryKey.recover.invalidKey=Denna återställningsnyckel är ogiltig
recoveryKey.printout.heading=Cryptomator återställningsnyckel\n"%s"\n
### Reset Password
recoveryKey.recover.resetBtn=Återställ
recoveryKey.recover.recoverBtn=Återställ
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Lösenord återställt
recoveryKey.recover.resetSuccess.description=Du kan låsa upp valvet med det nya lösenordet.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetMasterkeyFileSuccess.description=Nu kan du låsa upp valvet med lösenordet.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Valvet tillagt
recover.existing.message=Valvet har lagts till
recover.existing.description=Ditt valv "%s" har lagts till i valvlistan. Ingen återhämtningsprocess var nödvändig.
##Vault Already Exists - Dialog
recover.alreadyExists.title=Valvet finns redan
recover.alreadyExists.message=Detta valv har redan lagts till
recover.alreadyExists.description=Ditt valv "%s" finns redan i din valvlista och lades därför inte till igen.
##Invalid Selection - Dialog
recover.invalidSelection.title=Ogiltigt val
recover.invalidSelection.message=Ditt val är inte ett valv
recover.invalidSelection.description=Den valda mappen måste vara ett giltigt Cryptomatorvalv.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Hubb valv
contactHubVaultOwner.message=Detta valv skapades med Cryptomator Hub
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
convertVault.title=Konvertera valv
@@ -365,6 +365,7 @@ main.vaultDetail.missing.info=Cryptomator haikuweza kupata kuba katika njia hii.
main.vaultDetail.missing.recheck=Kagua upya
main.vaultDetail.missing.remove=Ondoa kutoka kwenye Orodha ya Kuba…
main.vaultDetail.missing.changeLocation=Badilisha Mahali pa Kuba…
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=Pandisha daraja Kuba
main.vaultDetail.migratePrompt=Kuba yako inahitaji kuboreshwa hadi umbizo jipya, kabla ya kuipata
@@ -435,6 +436,25 @@ recoveryKey.recover.resetBtn=Weka upya
### Recovery Key Password Reset Success
recoveryKey.recover.resetSuccess.message=Kuweka upya nenosiri kumefaulu
recoveryKey.recover.resetSuccess.description=Unaweza kufungua chumba chako kwa kutumia neno la siri jipya.
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault
@@ -243,6 +243,7 @@ main.vaultDetail.unlockNowBtn=இப்போது திறக்கவும
main.vaultDetail.revealBtn=இயக்ககத்தை வெளிப்படுத்து
main.vaultDetail.lockBtn=பூட்டு
### Missing
### Missing Vault Config
### Needs Migration
main.vaultDetail.migrateButton=பெட்டகத்தை மேம்படுத்து
### Error
@@ -280,6 +281,25 @@ vaultOptions.masterkey.changePasswordBtn=கடவுச்சொல்லை
### Enter Recovery Key
### Reset Password
### Recovery Key Password Reset Success
### Recovery Key Vault Config Reset Success
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
##Vault Already Exists - Dialog
##Invalid Selection - Dialog
## Contact Hub Vault Owner - Dialog
##Dialog Title
## OnBoarding
###Vault Config Missing
###Masterkey Missing
## Expert Settings
# Convert Vault

Some files were not shown because too many files have changed in this diff Show More