diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 521edeedd..26111c518 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -52,7 +52,6 @@ jobs: java-version: ${{ env.JAVA_VERSION }} check-latest: true cache: 'maven' - - name: Download OpenJFX jmods id: download-jmods run: | @@ -194,3 +193,68 @@ jobs: cryptomator-*.AppImage cryptomator-*.zsync cryptomator-*.asc + + create-aur-bin-pr: + name: Create PR for aur-bin repo + needs: [build, get-version] + runs-on: ubuntu-latest + if: github.event_name == 'release' + steps: + - name: Download AppImages + uses: actions/download-artifact@v4 + with: + path: downloads/ + merge-multiple: true + - name: Compute sha256 hash of AppImages + id: checksums + run: | + X64_SHA256=$(sha256sum downloads/cryptomator-*-x86_64.AppImage | cut -d ' ' -f1) + echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT" + AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1) + echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT" + - uses: actions/checkout@v4 + with: + repository: 'cryptomator/aur-bin' + token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install makepkg pacman-package-manager + - name: Checkout release branch + run: | + git checkout -b release/${{ needs.get-version.outputs.semVerStr }} + - name: Update build file + run: | + sed -i -e 's|^pkgver=.*$|pkgver=${{ needs.get-version.outputs.semVerStr }}|' PKGBUILD + sed -i -e 's|^pkgrel=.*$|pkgrel=1|' PKGBUILD + sed -i -e "s|^sha256sums_x86_64=.*$|sha256sums_x86_64=('${{ steps.checksums.outputs.x64-sha256sum }}'|" PKGBUILD + sed -i -e "s|^sha256sums_aarch64=.*$|sha256sums_aarch64=('${{ steps.checksums.outputs.aarch64-sha256sum}}'|" PKGBUILD + makepkg --printsrcinfo > .SRCINFO + - name: Commit and push + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" + git config push.autoSetupRemote true + git stage . + git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}" + git push + - name: Create pull request + id: create-pr + run: | + printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update build instructions\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md + URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md) + echo "PR_URL=$URL" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} + - name: Slack Notification + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_USERNAME: 'Cryptobot' + SLACK_ICON: false + SLACK_ICON_EMOJI: ':bot:' + SLACK_CHANNEL: 'cryptomator-desktop' + SLACK_TITLE: "AUR-bin release PR for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created." + SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.PR_URL }}|PR> on how to proceed." + SLACK_FOOTER: false + MSG_MINIMAL: true diff --git a/.github/workflows/aur.yml b/.github/workflows/aur.yml new file mode 100644 index 000000000..e290be872 --- /dev/null +++ b/.github/workflows/aur.yml @@ -0,0 +1,93 @@ +name: Create PR for AUR + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: 'Release tag' + required: true + +jobs: + get-version: + uses: ./.github/workflows/get-version.yml + with: + version: ${{ inputs.tag }} + tarball: + name: Determines tarball url and compute checksum + runs-on: ubuntu-latest + needs: [get-version] + if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable' + outputs: + url: ${{ steps.url.outputs.url}} + sha256: ${{ steps.sha256.outputs.sha256}} + steps: + - name: Determine tarball url + id: url + run: | + URL=""; + if [[ -n "${{ inputs.tag }}" ]]; then + URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ inputs.tag }}.tar.gz" + else + URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz" + fi + echo "url=${URL}" >> "$GITHUB_OUTPUT" + - name: Download source tarball and compute checksum + id: sha256 + run: | + curl --silent --fail-with-body -L -H "Accept: application/vnd.github+json" ${{ steps.url.outputs.url }} --output cryptomator.tar.gz + TARBALL_SHA256=$(sha256sum cryptomator.tar.gz | cut -d ' ' -f1) + echo "sha256=${TARBALL_SHA256}" >> "$GITHUB_OUTPUT" + aur: + name: Create PR for AUR + runs-on: ubuntu-latest + needs: [tarball, get-version] + env: + AUR_PR_URL: tbd + steps: + - uses: actions/checkout@v4 + with: + repository: 'cryptomator/aur' + token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install makepkg pacman-package-manager + - name: Checkout release branch + run: | + git checkout -b release/${{ needs.get-version.outputs.semVerStr }} + - name: Update build file + run: | + sed -i -e 's|^pkgver=.*$|pkgver=${{ needs.get-version.outputs.semVerStr }}|' PKGBUILD + sed -i -e 's|^pkgrel=.*$|pkgrel=1|' PKGBUILD + sed -i -e "s|^sha256sums=.*$|sha256sums=('${{ needs.tarball.outputs.sha256 }}'|" PKGBUILD + makepkg --printsrcinfo > .SRCINFO + - name: Commit and push + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" + git config push.autoSetupRemote true + git stage . + git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}" + git push + - name: Create pull request + run: | + printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update build instructions\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md + PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md) + echo "AUR_PR_URL=$PR_URL" >> "$GITHUB_ENV" + env: + GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} + - name: Slack Notification + uses: rtCamp/action-slack-notify@v2 + if: github.event_name == 'release' + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_USERNAME: 'Cryptobot' + SLACK_ICON: false + SLACK_ICON_EMOJI: ':bot:' + SLACK_CHANNEL: 'cryptomator-desktop' + SLACK_TITLE: "AUR release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created." + SLACK_MESSAGE: "See <${{ env.AUR_PR_URL }}|PR> on how to proceed." + SLACK_FOOTER: false + MSG_MINIMAL: true \ No newline at end of file diff --git a/.github/workflows/flathub.yml b/.github/workflows/flathub.yml index da8f6c77f..85e2cb435 100644 --- a/.github/workflows/flathub.yml +++ b/.github/workflows/flathub.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v4 with: repository: 'flathub/org.cryptomator.Cryptomator' - token: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }} + token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - name: Checkout release branch run: | git checkout -b release/${{ needs.get-version.outputs.semVerStr }} @@ -72,7 +72,7 @@ jobs: PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md) echo "FLATHUB_PR_URL=$PR_URL" >> "$GITHUB_ENV" env: - GH_TOKEN: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }} + GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - name: Slack Notification uses: rtCamp/action-slack-notify@v2 if: github.event_name == 'release' diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 476e409e3..0beb75544 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -16,7 +16,7 @@ jobs: run: | gh repo sync cryptomator/winget-pkgs -b master --force env: - GH_TOKEN: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }} + GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - name: Submit package uses: vedantmgoyal2009/winget-releaser@main with: @@ -24,4 +24,4 @@ jobs: version: ${{ inputs.tag }} release-tag: ${{ inputs.tag }} installers-regex: '-x64\.msi$' - token: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }} \ No newline at end of file + token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} \ No newline at end of file diff --git a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml index b088836f7..e986431a8 100644 --- a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml +++ b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml @@ -83,6 +83,9 @@ + + https://github.com/cryptomator/cryptomator/releases/1.17.1 + https://github.com/cryptomator/cryptomator/releases/1.17.0 diff --git a/pom.xml b/pom.xml index 62d1eb2d3..cb8d62ba0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cryptomator cryptomator - 1.17.0 + 1.17.1 Cryptomator Desktop App diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java index b382cd1ac..a711e6536 100644 --- a/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/src/main/java/org/cryptomator/common/settings/Settings.java @@ -25,6 +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; @@ -75,6 +77,7 @@ public class Settings { public final BooleanProperty checkForUpdates; public final ObjectProperty lastUpdateCheckReminder; public final ObjectProperty lastSuccessfulUpdateCheck; + public final ObjectProperty previouslyUsedVaultDirectory; private Consumer saveCmd; @@ -114,6 +117,7 @@ public class Settings { this.checkForUpdates = new SimpleBooleanProperty(this, "checkForUpdates", json.checkForUpdatesEnabled); 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.directories.addAll(json.directories.stream().map(VaultSettings::new).toList()); @@ -143,6 +147,7 @@ public class Settings { checkForUpdates.addListener(this::somethingChanged); lastUpdateCheckReminder.addListener(this::somethingChanged); lastSuccessfulUpdateCheck.addListener(this::somethingChanged); + previouslyUsedVaultDirectory.addListener(this::somethingChanged); } @SuppressWarnings("deprecation") @@ -204,6 +209,7 @@ public class Settings { json.checkForUpdatesEnabled = checkForUpdates.get(); json.lastReminderForUpdateCheck = lastUpdateCheckReminder.get(); json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get(); + json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get(); return json; } diff --git a/src/main/java/org/cryptomator/common/settings/SettingsJson.java b/src/main/java/org/cryptomator/common/settings/SettingsJson.java index 15f5f2790..feb8a0bf2 100644 --- a/src/main/java/org/cryptomator/common/settings/SettingsJson.java +++ b/src/main/java/org/cryptomator/common/settings/SettingsJson.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import java.nio.file.Path; import java.time.Instant; import java.util.List; @@ -92,4 +93,7 @@ class SettingsJson { @JsonProperty("quickAccessService") String quickAccessService = Settings.DEFAULT_QUICKACCESS_SERVICE; + + @JsonProperty("previouslyUsedVaultDirectory") + Path previouslyUsedVaultDirectory; } diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index 6dacfcb8a..ce1b2433c 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -9,13 +9,13 @@ package org.cryptomator.common.vaults; import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.Constants; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.DirStructure; import org.cryptomator.cryptofs.migration.Migrators; import org.cryptomator.integrations.mount.MountService; +import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -125,7 +125,7 @@ public class VaultListManager { vaultSettings.lastKnownKeyLoader.set(keyIdScheme); } } else if (vaultState == NEEDS_MIGRATION) { - vaultSettings.lastKnownKeyLoader.set(Constants.DEFAULT_KEY_ID.toString()); + vaultSettings.lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME); } return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault(); } catch (IOException e) { diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultExpertSettingsController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultExpertSettingsController.java index a35bcdebe..9ca557366 100644 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultExpertSettingsController.java +++ b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultExpertSettingsController.java @@ -26,7 +26,7 @@ public class CreateNewVaultExpertSettingsController implements FxController { public static final int MAX_SHORTENING_THRESHOLD = 220; public static final int MIN_SHORTENING_THRESHOLD = 36; - private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/security/architecture/#name-shortening"; + private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/security/vault/#name-shortening"; private final Stage window; private final Lazy application; diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java index c8b7bee22..78a2771df 100644 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java +++ b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java @@ -4,6 +4,7 @@ import dagger.Lazy; import org.cryptomator.common.ObservableUtil; import org.cryptomator.common.locationpresets.LocationPreset; import org.cryptomator.common.locationpresets.LocationPresetsProvider; +import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; @@ -38,6 +39,7 @@ import javafx.stage.WindowEvent; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; @@ -64,6 +66,7 @@ public class CreateNewVaultLocationController implements FxController { private final BooleanProperty loadingPresetLocations = new SimpleBooleanProperty(false); private final ObservableList radioButtons; private final ObservableList sortedRadioButtons; + private final Settings settings; private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH; @@ -82,6 +85,7 @@ public class CreateNewVaultLocationController implements FxController { @FxmlScene(FxmlFile.ADDVAULT_NEW_EXPERT_SETTINGS) Lazy chooseExpertSettingsScene, // ObjectProperty vaultPath, // @Named("vaultName") StringProperty vaultName, // + Settings settings, // ExecutorService backgroundExecutor, ResourceBundle resourceBundle) { this.window = window; this.chooseNameScene = chooseNameScene; @@ -96,6 +100,18 @@ public class CreateNewVaultLocationController implements FxController { this.usePresetPath = new SimpleBooleanProperty(); this.radioButtons = FXCollections.observableArrayList(); this.sortedRadioButtons = radioButtons.sorted(this::compareLocationPresets); + this.settings = settings; + + Path previouslyUsedDirectory = settings.previouslyUsedVaultDirectory.get(); + if (previouslyUsedDirectory != null) { + try { + if (Files.exists(previouslyUsedDirectory) && Files.isDirectory(previouslyUsedDirectory) && isActuallyWritable(previouslyUsedDirectory)) { + this.customVaultPath = previouslyUsedDirectory; + } + } catch (InvalidPathException | NullPointerException e) { + LOG.warn("Invalid previously used vault directory path: {}", previouslyUsedDirectory, e); + } + } } private VaultPathStatus validatePath(Path p) throws NullPointerException { @@ -196,6 +212,12 @@ public class CreateNewVaultLocationController implements FxController { @FXML public void next() { if (validVaultPath.getValue()) { + if (this.getVaultPath() != null) { + Path parentPath = this.getVaultPath().getParent(); + if (parentPath != null) { + this.settings.previouslyUsedVaultDirectory.setValue(parentPath); + } + } window.setScene(chooseExpertSettingsScene.get()); } } diff --git a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java index b9af0f4a3..eb6ecbfa3 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java +++ b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingStrategy.java @@ -46,15 +46,16 @@ public interface KeyLoadingStrategy extends MasterkeyLoader { /** * Determines whether the provided key loader scheme corresponds to a Masterkey File Vault. *

- * This method checks if the {@code keyLoader} parameter matches the known Masterkey File Vault scheme + * This method checks if the {@code keyLoader} parameter starts with the known Masterkey File Vault scheme * {@link MasterkeyFileLoadingStrategy#SCHEME}. + * This allows identifying not only exact matches but also variants or extended schemes based on the Masterkey scheme. *

* * @param keyLoader A string representing the key loader scheme to be checked. - * @return {@code true} if the given key loader scheme represents a Masterkey File Vault; {@code false} otherwise. + * @return {@code true} if the given key loader scheme starts with the Masterkey File Vault scheme; {@code false} otherwise. */ static boolean isMasterkeyFileVault(String keyLoader) { - return MasterkeyFileLoadingStrategy.SCHEME.equals(keyLoader); + return keyLoader.startsWith(MasterkeyFileLoadingStrategy.SCHEME); } /** diff --git a/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java b/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java index 859c9e38d..93fb298ed 100644 --- a/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java +++ b/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java @@ -19,7 +19,7 @@ public class ShareVaultController implements FxController { private static final String SCHEME_PREFIX = "hub+"; private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/"; - private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/en/latest/security/best-practices/#sharing-of-vaults"; + private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/security/best-practices/#sharing-of-vaults"; private final Stage window; private final Lazy application; diff --git a/src/main/resources/i18n/strings_ar.properties b/src/main/resources/i18n/strings_ar.properties index 4e4b34095..f69f4b67c 100644 --- a/src/main/resources/i18n/strings_ar.properties +++ b/src/main/resources/i18n/strings_ar.properties @@ -62,9 +62,9 @@ addvaultwizard.new.locationIsOk=الموقع المناسب للمخزن الخ addvaultwizard.new.invalidName=اسم المخزن غير صالح addvaultwizard.new.validName=اسم المخزن صالح addvaultwizard.new.validCharacters.message=قد يحتوي اسم المخزن على الأحرف التالية: -addvaultwizard.new.validCharacters.chars=أحرف الكلمات (أمثلة: a, ж, 수) -addvaultwizard.new.validCharacters.numbers=الأعداد -addvaultwizard.new.validCharacters.dashes=الشرطة (%s) أو الشرطة السفلية (%s) +addvaultwizard.new.validCharacters.chars=أحرف لكن دون علامات (#, $, !, @...) +addvaultwizard.new.validCharacters.numbers=الأرقام +addvaultwizard.new.validCharacters.dashes=ناقص (%s) أو شِرْطَةٌ سفلية (%s) ### Expert Settings addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=تمكين إعدادات الخبراء addvaultwizard.new.expertSettings.shorteningThreshold.invalid=أدخل قيمة بين 36 و 220 (الافتراضي 220) @@ -319,7 +319,7 @@ preferences.volume.feature.readOnly=تحميل للقراءة فقط ## Updates preferences.updates=تحديثات preferences.updates.currentVersion=الإصدار الحالي: %s -preferences.updates.autoUpdateCheck=تحقق من التحديثات اوتوماتيكيا +preferences.updates.autoUpdateCheck=التحقق من وجود تحديثات تلقائياً preferences.updates.checkNowBtn=تحقق الان preferences.updates.updateAvailable=التحديث إلى الإصدار %s متاح. preferences.updates.lastUpdateCheck=آخر فحص: %s @@ -395,6 +395,7 @@ main.vaultlist.contextMenu.vaultoptions=إظهار خيارات المخزن main.vaultlist.contextMenu.reveal=اظهار القرص main.vaultlist.addVaultBtn.menuItemNew=إنشاء مخزن جديد... main.vaultlist.addVaultBtn.menuItemExisting=افتح مخزن موجود... +main.vaultlist.showEventsButton.tooltip=عرض الإشعارات ##Notificaition main.notification.updateAvailable=هناك تحديث متاح. main.notification.support=دعم Cryptomator. @@ -423,7 +424,9 @@ main.vaultDetail.stats=إحصائيات الخزنة main.vaultDetail.locateEncryptedFileBtn=تحديد موقع الملف المشفر main.vaultDetail.locateEncryptedFileBtn.tooltip=اختر ملف من خزانتك لتحديد مكان نظيره المشفر main.vaultDetail.encryptedPathsCopied=تم نسخ مسارات الملفات إلى الحافظة! +main.vaultDetail.locateEncrypted.filePickerTitle=اختر الملَف من الخزنة main.vaultDetail.decryptName.buttonLabel=فك تشفير اسم الملف +main.vaultDetail.decryptName.tooltip=اختر ملَف مشفر لفك تشفير اسمه ### Missing main.vaultDetail.missing.info=لم يتمكن Cryptomator من العثور على خزنة في هذا المسار. main.vaultDetail.missing.recheck=إعادة الفحص @@ -473,7 +476,7 @@ vaultOptions.mount.mountPoint.custom=استخدام المجلد المختار vaultOptions.mount.mountPoint.directoryPickerButton=اختر… vaultOptions.mount.mountPoint.directoryPickerTitle=إختر مجلد vaultOptions.mount.volumeType.default=الافتراضي (%s) -vaultOptions.mount.volumeType.restartRequired=لاستخدام هذا النوع ‮من وحدة التخزين يحتاج Cryptomator إلى إعادة تشغيله. +vaultOptions.mount.volumeType.restartRequired=لاستخدام هذا النوع من وحدة التخزين يحتاج Cryptomator إلى إعادة تشغيل. vaultOptions.mount.volume.tcp.port=منفذ TCP vaultOptions.mount.volume.type=‮نوع وحدة التخزين ## Master Key @@ -580,7 +583,40 @@ shareVault.hub.instruction.2=2. امنح الوصول لعضو الفريق في shareVault.hub.openHub=زيارة Cryptomator Hub # Decrypt File Names +decryptNames.title=فك تشفير اسم الملَف +decryptNames.filePicker.title=اختر ملَف مشفر +decryptNames.filePicker.extensionDescription=ملف مشفر +decryptNames.copyTable.tooltip=نسخ الجدول +decryptNames.clearTable.tooltip=مسح الجدول +decryptNames.copyHint=نسخ محتوى الخلية مع %s +decryptNames.dropZone.message=إسقاط الملفات أو انقر لتحديد +decryptNames.dropZone.error.vaultInternalFiles=مخزن الملفات الداخلية مع عدم تحديد اسم قابل للتشفير +decryptNames.dropZone.error.foreignFiles=الملفات لا تنتمي إلى مخزن "%s" +decryptNames.dropZone.error.noDirIdBackup=مسار الملفات المحددة لا يحتوي على ملَف dirId.c9r +decryptNames.dropZone.error.generic=فشل فك تشفير أسماء الملفات # Event View +eventView.title=أحداث +eventView.filter.allVaults=الكل +eventView.clearListButton.tooltip=تفريغ القائمة ## event list entries +eventView.entry.vaultLocked.description=فتح "%s" للحصول على التفاصيل +eventView.entry.conflictResolved.message=تم حل التضارب +eventView.entry.conflictResolved.showDecrypted=إظهار الملف غير المشفر +eventView.entry.conflictResolved.copyDecrypted=نسخ المسار غير المشفر +eventView.entry.conflict.message=فشل حل التضارب +eventView.entry.conflict.showDecrypted=إظهار الملَف غير المشفر الأصلي +eventView.entry.conflict.copyDecrypted=نسخ المسار غير المشفر والأصلي +eventView.entry.conflict.showEncrypted=إظهار ملف متضارب ومشفر +eventView.entry.conflict.copyEncrypted=نسخ مسار التشفير المتعارض +eventView.entry.decryptionFailed.message=فشل فك التشفير +eventView.entry.decryptionFailed.showEncrypted=عرض ملَف المشفر +eventView.entry.decryptionFailed.copyEncrypted=نسخ مسار المشفر +eventView.entry.brokenDirFile.message=رابط الدليل المكسور +eventView.entry.brokenDirFile.showEncrypted=إظهار الرابط المكسور، المشفر +eventView.entry.brokenDirFile.copyEncrypted=نسخ مسار الرابط المكسور +eventView.entry.brokenFileNode.message=عقدة ملفات النظام التافلة +eventView.entry.brokenFileNode.showEncrypted=عرض العقدة المشفّرة التافلة +eventView.entry.brokenFileNode.copyEncrypted=نسخ مسار العقدة المشفّرة التافلة +eventView.entry.brokenFileNode.copyDecrypted=نسخ المسار غير المشفر diff --git a/src/main/resources/i18n/strings_pt.properties b/src/main/resources/i18n/strings_pt.properties index e123071b0..bb048b2cf 100644 --- a/src/main/resources/i18n/strings_pt.properties +++ b/src/main/resources/i18n/strings_pt.properties @@ -424,9 +424,9 @@ main.vaultDetail.stats=Estatísticas do Cofre main.vaultDetail.locateEncryptedFileBtn=Localizar Ficheiro Encriptado main.vaultDetail.locateEncryptedFileBtn.tooltip=Escolha um ficheiro do seu cofre para localizar a sua contraparte encriptada main.vaultDetail.encryptedPathsCopied=Caminhos copiados para a área de transferência! -main.vaultDetail.locateEncrypted.filePickerTitle=Selecionar ficheiro dentro do cofre -main.vaultDetail.decryptName.buttonLabel=Desencriptar nome do ficheiro -main.vaultDetail.decryptName.tooltip=Escolha um ficheiro de cofre encriptado para desencriptar o seu nome +main.vaultDetail.locateEncrypted.filePickerTitle=Selecione o ficheiro no cofre +main.vaultDetail.decryptName.buttonLabel=Descriptografar o nome do ficheiro +main.vaultDetail.decryptName.tooltip=Escolha um ficheiro encriptado do cofre para desencriptar o seu nome ### Missing main.vaultDetail.missing.info=O Cryptomator não conseguiu encontrar um cofre neste diretório. main.vaultDetail.missing.recheck=Verificar novamente @@ -583,17 +583,17 @@ shareVault.hub.instruction.2=2. Conceder acesso ao membro da equipe no Hub Crypt shareVault.hub.openHub=Abrir Hub do Cryptomator # Decrypt File Names -decryptNames.title=Desencriptar nomes de ficheiros +decryptNames.title=Desencriptar os nomes dos ficheiros decryptNames.filePicker.title=Selecione o ficheiro encriptado -decryptNames.filePicker.extensionDescription=Ficheiro encriptado do Cryptomator -decryptNames.copyTable.tooltip=Copiar tabela -decryptNames.clearTable.tooltip=Limpar tabela -decryptNames.copyHint=Copiar conteúdo da célula com %s -decryptNames.dropZone.message=Solte os ficheiros ou clique para selecionar -decryptNames.dropZone.error.vaultInternalFiles=Ficheiros internos do cofre sem nome decifrável selecionado -decryptNames.dropZone.error.foreignFiles=Ficheiros não pertencem ao cofre "%s" +decryptNames.filePicker.extensionDescription=Ficheiro encriptado pelo Criptomator +decryptNames.copyTable.tooltip=Copiar a tabela +decryptNames.clearTable.tooltip=Limpar a tabela +decryptNames.copyHint=Copiar o conteúdo da célula com %s +decryptNames.dropZone.message=Largue os ficheiros ou clique para selecionar +decryptNames.dropZone.error.vaultInternalFiles=Ficheiros internos do coftre sem nome desencriptável selecionado +decryptNames.dropZone.error.foreignFiles=Os ficheiros não pertencem ao cofre "%s" decryptNames.dropZone.error.noDirIdBackup=O diretório dos ficheiros selecionados não contém o ficheiro dirId.c9r -decryptNames.dropZone.error.generic=Falha ao desencriptar nomes de ficheiros +decryptNames.dropZone.error.generic=Falha ao desencriptar os nomes dos ficheiros # Event View @@ -603,20 +603,20 @@ eventView.clearListButton.tooltip=Limpar lista ## event list entries eventView.entry.vaultLocked.description=Desbloquear "%s" para detalhes eventView.entry.conflictResolved.message=Conflito resolvido -eventView.entry.conflictResolved.showDecrypted=Mostrar ficheiro desencriptado -eventView.entry.conflictResolved.copyDecrypted=Copiar caminho desencriptado -eventView.entry.conflict.message=Resolução de conflito falhou -eventView.entry.conflict.showDecrypted=Mostrar ficheiro original desencriptado -eventView.entry.conflict.copyDecrypted=Copie caminho original desencriptado -eventView.entry.conflict.showEncrypted=Mostrar ficheiro encriptado conflitante -eventView.entry.conflict.copyEncrypted=Copiar caminho encriptado conflituante -eventView.entry.decryptionFailed.message=Falha na desencriptação +eventView.entry.conflictResolved.showDecrypted=Mostrar o ficheiro desencriptado +eventView.entry.conflictResolved.copyDecrypted=Copiar o caminho desencriptado +eventView.entry.conflict.message=A resolução do conflito falhou +eventView.entry.conflict.showDecrypted=Mostrar o ficheiro original desencriptado +eventView.entry.conflict.copyDecrypted=Copie o caminho original desencriptado +eventView.entry.conflict.showEncrypted=Mostrar o ficheiro encriptado em conflito +eventView.entry.conflict.copyEncrypted=Copiar o caminho encriptado em conflito +eventView.entry.decryptionFailed.message=A desencriptação falhou eventView.entry.decryptionFailed.showEncrypted=Mostrar ficheiro encriptado -eventView.entry.decryptionFailed.copyEncrypted=Copiar caminho de encriptação +eventView.entry.decryptionFailed.copyEncrypted=Copiar o caminho encriptado eventView.entry.brokenDirFile.message=Link de diretório quebrado -eventView.entry.brokenDirFile.showEncrypted=Mostrar link quebrado e encriptado +eventView.entry.brokenDirFile.showEncrypted=Mostrar o link quebrado e encriptado eventView.entry.brokenDirFile.copyEncrypted=Copiar caminho do link quebrado eventView.entry.brokenFileNode.message=Nó do sistema de ficheiros avariado -eventView.entry.brokenFileNode.showEncrypted=Mostrar nó encriptado quebrado +eventView.entry.brokenFileNode.showEncrypted=Mostrar nó encriptado e danificado eventView.entry.brokenFileNode.copyEncrypted=Copiar o caminho do nó encriptado e danificado -eventView.entry.brokenFileNode.copyDecrypted=Copiar caminho desencriptado +eventView.entry.brokenFileNode.copyDecrypted=Copiar o caminho desencriptado