diff --git a/.eslintrc.json b/.eslintrc.json index 671e7b2fab..61bebbf483 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -246,6 +246,22 @@ } ] } + }, + { + "files": ["**/*.ts"], + "excludedFiles": ["**/platform/**/*.ts"], + "rules": { + "no-restricted-imports": [ + "error", + { + "patterns": [ + "**/platform/**/internal", // General internal pattern + // All features that have been converted to barrel files + "**/platform/messaging/**" + ] + } + ] + } } ] } diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 585a888ae1..23f4bd35f1 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -160,9 +160,9 @@ jobs: run: npm run dist working-directory: browser-source/apps/browser - # - name: Build Manifest v3 - # run: npm run dist:mv3 - # working-directory: browser-source/apps/browser + - name: Build Manifest v3 + run: npm run dist:mv3 + working-directory: browser-source/apps/browser - name: Gulp run: gulp ci @@ -189,12 +189,12 @@ jobs: path: browser-source/apps/browser/dist/dist-chrome.zip if-no-files-found: error - # - name: Upload Chrome MV3 artifact - # uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 - # with: - # name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip - # path: browser-source/apps/browser/dist/dist-chrome-mv3.zip - # if-no-files-found: error + - name: Upload Chrome MV3 artifact (DO NOT USE FOR PROD) + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + with: + name: DO-NOT-USE-FOR-PROD-dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip + path: browser-source/apps/browser/dist/dist-chrome-mv3.zip + if-no-files-found: error - name: Upload Firefox artifact uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index 769e700588..6a5d9f1405 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -128,29 +128,90 @@ jobs: - name: Success Code run: exit 0 - get-branch-or-tag-sha: - name: Get Branch or Tag SHA + artifact-check: + name: Check if Web artifact is present runs-on: ubuntu-22.04 + needs: setup + env: + _ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment-artifact }} outputs: - branch-or-tag-sha: ${{ steps.get-branch-or-tag-sha.outputs.sha }} + artifact-build-commit: ${{ steps.set-artifact-commit.outputs.commit }} steps: - - name: Checkout Branch - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: 'Download latest cloud asset using GitHub Run ID: ${{ inputs.build-web-run-id }}' + if: ${{ inputs.build-web-run-id }} + uses: bitwarden/gh-actions/download-artifacts@main + id: download-latest-artifacts-run-id + continue-on-error: true with: - ref: ${{ inputs.branch-or-tag }} - fetch-depth: 0 + workflow: build-web.yml + path: apps/web + workflow_conclusion: success + run_id: ${{ inputs.build-web-run-id }} + artifacts: ${{ env._ENVIRONMENT_ARTIFACT }} - - name: Get Branch or Tag SHA - id: get-branch-or-tag-sha + - name: 'Download latest cloud asset from branch/tag: ${{ inputs.branch-or-tag }}' + if: ${{ !inputs.build-web-run-id }} + uses: bitwarden/gh-actions/download-artifacts@main + id: download-latest-artifacts + continue-on-error: true + with: + workflow: build-web.yml + path: apps/web + workflow_conclusion: success + branch: ${{ inputs.branch-or-tag }} + artifacts: ${{ env._ENVIRONMENT_ARTIFACT }} + + - name: Login to Azure + if: ${{ steps.download-latest-artifacts.outcome == 'failure' }} + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve secrets for Build trigger + if: ${{ steps.download-latest-artifacts.outcome == 'failure' }} + id: retrieve-secret + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: "bitwarden-ci" + secrets: "github-pat-bitwarden-devops-bot-repo-scope" + + - name: 'Trigger build web for missing branch/tag ${{ inputs.branch-or-tag }}' + if: ${{ steps.download-latest-artifacts.outcome == 'failure' }} + uses: convictional/trigger-workflow-and-wait@f69fa9eedd3c62a599220f4d5745230e237904be # v1.6.5 + id: trigger-build-web + with: + owner: bitwarden + repo: clients + github_token: ${{ steps.retrieve-secret.outputs.github-pat-bitwarden-devops-bot-repo-scope }} + workflow_file_name: build-web.yml + ref: ${{ inputs.branch-or-tag }} + wait_interval: 100 + + - name: Set artifact build commit + id: set-artifact-commit + env: + GH_TOKEN: ${{ github.token }} run: | - echo "sha=$(git rev-parse origin/${{ inputs.branch-or-tag }})" >> $GITHUB_OUTPUT + # If run-id was used, get the commit from the download-latest-artifacts-run-id step + if [ "${{ inputs.build-web-run-id }}" ]; then + echo "commit=${{ steps.download-latest-artifacts-run-id.outputs.artifact-build-commit }}" >> $GITHUB_OUTPUT + + elif [ "${{ steps.download-latest-artifacts.outcome }}" == "failure" ]; then + # If the download-latest-artifacts step failed, query the GH API to get the commit SHA of the artifact that was just built with trigger-build-web. + commit=$(gh api /repos/bitwarden/clients/actions/runs/${{ steps.trigger-build-web.outputs.workflow_id }}/artifacts --jq '.artifacts[0].workflow_run.head_sha') + echo "commit=$commit" >> $GITHUB_OUTPUT + + else + # Set the commit to the output of step download-latest-artifacts. + echo "commit=${{ steps.download-latest-artifacts.outputs.artifact-build-commit }}" >> $GITHUB_OUTPUT + fi notify-start: name: Notify Slack with start message needs: - approval - setup - - get-branch-or-tag-sha + - artifact-check runs-on: ubuntu-22.04 if: ${{ always() && contains( inputs.environment , 'QA' ) }} outputs: @@ -165,66 +226,10 @@ jobs: tag: ${{ inputs.branch-or-tag }} slack-channel: team-eng-qa-devops event: 'start' - commit-sha: ${{ needs.get-branch-or-tag-sha.outputs.branch-or-tag-sha }} + commit-sha: ${{ needs.artifact-check.outputs.artifact-build-commit }} url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }} AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - artifact-check: - name: Check if Web artifact is present - runs-on: ubuntu-22.04 - needs: setup - env: - _ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment-artifact }} - steps: - - name: 'Download latest cloud asset using GitHub Run ID: ${{ inputs.build-web-run-id }}' - if: ${{ inputs.build-web-run-id }} - uses: bitwarden/gh-actions/download-artifacts@main - id: download-latest-artifacts - continue-on-error: true - with: - workflow: build-web.yml - path: apps/web - workflow_conclusion: success - run_id: ${{ inputs.build-web-run-id }} - artifacts: ${{ env._ENVIRONMENT_ARTIFACT }} - - - name: 'Download latest cloud asset from branch/tag: ${{ inputs.branch-or-tag }}' - if: ${{ !inputs.build-web-run-id }} - uses: bitwarden/gh-actions/download-artifacts@main - id: download-artifacts - continue-on-error: true - with: - workflow: build-web.yml - path: apps/web - workflow_conclusion: success - branch: ${{ inputs.branch-or-tag }} - artifacts: ${{ env._ENVIRONMENT_ARTIFACT }} - - - name: Login to Azure - if: ${{ steps.download-artifacts.outcome == 'failure' }} - uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 - with: - creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - - - name: Retrieve secrets for Build trigger - if: ${{ steps.download-artifacts.outcome == 'failure' }} - id: retrieve-secret - uses: bitwarden/gh-actions/get-keyvault-secrets@main - with: - keyvault: "bitwarden-ci" - secrets: "github-pat-bitwarden-devops-bot-repo-scope" - - - name: 'Trigger build web for missing branch/tag ${{ inputs.branch-or-tag }}' - if: ${{ steps.download-artifacts.outcome == 'failure' }} - uses: convictional/trigger-workflow-and-wait@f69fa9eedd3c62a599220f4d5745230e237904be # v1.6.5 - with: - owner: bitwarden - repo: clients - github_token: ${{ steps.retrieve-secret.outputs.github-pat-bitwarden-devops-bot-repo-scope }} - workflow_file_name: build-web.yml - ref: ${{ inputs.branch-or-tag }} - wait_interval: 100 - azure-deploy: name: Deploy Web Vault to ${{ inputs.environment }} Storage Account needs: @@ -248,6 +253,7 @@ jobs: environment: ${{ env._ENVIRONMENT_NAME }} task: 'deploy' description: 'Deployment from branch/tag: ${{ inputs.branch-or-tag }}' + ref: ${{ needs.artifact-check.outputs.artifact-build-commit }} - name: Login to Azure uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 @@ -349,10 +355,10 @@ jobs: runs-on: ubuntu-22.04 if: ${{ always() && contains( inputs.environment , 'QA' ) }} needs: + - setup - notify-start - azure-deploy - - setup - - get-branch-or-tag-sha + - artifact-check steps: - uses: bitwarden/gh-actions/report-deployment-status-to-slack@main with: @@ -362,6 +368,6 @@ jobs: slack-channel: ${{ needs.notify-start.outputs.channel_id }} event: ${{ needs.azure-deploy.result }} url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }} - commit-sha: ${{ needs.get-branch-or-tag-sha.outputs.branch-or-tag-sha }} + commit-sha: ${{ needs.artifact-check.outputs.artifact-build-commit }} update-ts: ${{ needs.notify-start.outputs.ts }} AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 7b17b114d6..e08894be0b 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - مدير كلمات مرور مجاني", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "مدير كلمات مرور مجاني وآمن لجميع أجهزتك.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "تغيير كلمة المرور الرئيسية" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "عبارة بصمة الإصبع", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "أُضيف المجلد" }, - "changeMasterPass": { - "message": "تغيير كلمة المرور الرئيسية" - }, - "changeMasterPasswordConfirmation": { - "message": "يمكنك تغيير كلمة المرور الرئيسية من خزنة الويب في bitwarden.com. هل تريد زيارة الموقع الآن؟" - }, "twoStepLoginConfirmation": { "message": "تسجيل الدخول بخطوتين يجعل حسابك أكثر أمنا من خلال مطالبتك بالتحقق من تسجيل الدخول باستخدام جهاز آخر مثل مفتاح الأمان، تطبيق المصادقة، الرسائل القصيرة، المكالمة الهاتفية، أو البريد الإلكتروني. يمكن تمكين تسجيل الدخول بخطوتين على خزنة الويب bitwarden.com. هل تريد زيارة الموقع الآن؟" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index e12fb0dc88..1e5062d8c6 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Ödənişsiz Parol Meneceri", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bütün cihazlarınız üçün güvənli və ödənişsiz bir parol meneceri.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Ana parolu dəyişdir" }, + "continueToWebApp": { + "message": "Veb tətbiqlə davam edilsin?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Ana parolunuzu Bitwarden veb tətbiqində dəyişdirə bilərsiniz." + }, "fingerprintPhrase": { "message": "Barmaq izi ifadəsi", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Qovluq əlavə edildi" }, - "changeMasterPass": { - "message": "Ana parolu dəyişdir" - }, - "changeMasterPasswordConfirmation": { - "message": "Ana parolunuzu bitwarden.com veb anbarında dəyişdirə bilərsiniz. İndi saytı ziyarət etmək istəyirsiniz?" - }, "twoStepLoginConfirmation": { "message": "İki addımlı giriş, güvənlik açarı, kimlik doğrulayıcı tətbiq, SMS, telefon zəngi və ya e-poçt kimi digər cihazlarla girişinizi doğrulamanızı tələb edərək hesabınızı daha da güvənli edir. İki addımlı giriş, bitwarden.com veb anbarında qurula bilər. Veb saytı indi ziyarət etmək istəyirsiniz?" }, @@ -1045,7 +1045,7 @@ "message": "Bildiriş server URL-si" }, "iconsUrl": { - "message": "Nişan server URL-si" + "message": "İkon server URL-si" }, "environmentSaved": { "message": "Mühit URL-ləri saxlanıldı." @@ -1072,7 +1072,7 @@ "description": "Overlay appearance select option for showing the field on focus of the input element" }, "autofillOverlayVisibilityOnButtonClick": { - "message": "Avto-doldurma nişanı seçiləndə", + "message": "Avto-doldurma ikonu seçiləndə", "description": "Overlay appearance select option for showing the field on click of the overlay icon" }, "enableAutoFillOnPageLoad": { @@ -1109,7 +1109,7 @@ "message": "Anbarı açılan pəncərədə aç" }, "commandOpenSidebar": { - "message": "Anbar yan sətirdə aç" + "message": "Anbarı yan çubuqda aç" }, "commandAutofillDesc": { "message": "Hazırkı veb sayt üçün son istifadə edilən giriş məlumatlarını avto-doldur" @@ -1162,7 +1162,7 @@ "message": "Bu brauzer bu açılan pəncərədə U2F tələblərini emal edə bilmir. U2F istifadə edərək giriş etmək üçün bu açılan pəncərəni yeni bir pəncərədə açmaq istəyirsiniz?" }, "enableFavicon": { - "message": "Veb sayt nişanlarını göstər" + "message": "Veb sayt ikonlarını göstər" }, "faviconDesc": { "message": "Hər girişin yanında tanına bilən təsvir göstər." @@ -1724,7 +1724,7 @@ "message": "İcazə tələb xətası" }, "nativeMessaginPermissionSidebarDesc": { - "message": "Bu əməliyyatı kənar çubuqda icra edilə bilməz. Lütfən açılan pəncərədə yenidən sınayın." + "message": "Bu əməliyyat yan çubuqda icra edilə bilməz. Lütfən açılan pəncərədə yenidən sınayın." }, "personalOwnershipSubmitError": { "message": "Müəssisə Siyasətinə görə, elementləri şəxsi anbarınızda saxlamağınız məhdudlaşdırılıb. Sahiblik seçimini təşkilat olaraq dəyişdirin və mövcud kolleksiyalar arasından seçim edin." @@ -1924,10 +1924,10 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { - "message": "Bir fayl seçmək üçün (mümkünsə) kənar çubuqdakı uzantını açın və ya bu bannerə klikləyərək yeni bir pəncərədə açın." + "message": "Bir fayl seçmək üçün (mümkünsə) yan çubuqdakı uzantını açın və ya bu bannerə klikləyərək yeni bir pəncərədə açın." }, "sendFirefoxFileWarning": { - "message": "Firefox istifadə edərək bir fayl seçmək üçün kənar çubuqdakı uzantını açın və ya bu bannerə klikləyərək yeni bir pəncərədə açın." + "message": "Firefox istifadə edərək bir fayl seçmək üçün yan çubuqdakı uzantını açın və ya bu bannerə klikləyərək yeni bir pəncərədə açın." }, "sendSafariFileWarning": { "message": "Safari istifadə edərək bir fayl seçmək üçün bu bannerə klikləyərək yeni bir pəncərədə açın." @@ -3000,13 +3000,36 @@ "message": "Kimlik məlumatlarını saxlama xətası. Detallar üçün konsolu yoxlayın.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Uğurlu" + }, "removePasskey": { "message": "Parolu sil" }, "passkeyRemoved": { "message": "Parol silindi" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Bildiriş: Təyin edilməyən təşkilat elementləri artıq Bütün Anbarlar görünüşündə görünən olmayacaq və yalnız Admin Konsolu vasitəsilə əlçatan olacaq." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Bildiriş: 16 May 2024-cü il tarixindən etibarən, təyin edilməyən təşkilat elementləri Bütün Anbarlar görünüşündə görünən olmayacaq və yalnız Admin Konsolu vasitəsilə əlçatan olacaq." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Bu elementləri görünən etmək üçün", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "bir kolleksiyaya təyin edin.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Konsolu" + }, + "errorAssigningTargetCollection": { + "message": "Hədəf kolleksiyaya təyin etmə xətası." + }, + "errorAssigningTargetFolder": { + "message": "Hədəf qovluğa təyin etmə xətası." } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 26ecf49cc7..91ff397b3a 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden – бясплатны менеджар пароляў", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Бяспечны і бясплатны менеджар пароляў для ўсіх вашых прылад.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Змяніць асноўны пароль" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Фраза адбітка пальца", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Папка дададзена" }, - "changeMasterPass": { - "message": "Змяніць асноўны пароль" - }, - "changeMasterPasswordConfirmation": { - "message": "Вы можаце змяніць свой асноўны пароль у вэб-сховішчы на bitwarden.com. Перайсці на вэб-сайт зараз?" - }, "twoStepLoginConfirmation": { "message": "Двухэтапны ўваход робіць ваш уліковы запіс больш бяспечным, патрабуючы пацвярджэнне ўваходу на іншай прыладзе з выкарыстаннем ключа бяспекі, праграмы аўтэнтыфікацыі, SMS, тэлефоннага званка або электроннай пошты. Двухэтапны ўваход уключаецца на bitwarden.com. Перайсці на вэб-сайт, каб зрабіць гэта?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 2ddaf647d8..33be2608b4 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -3,11 +3,11 @@ "message": "Битуорден (Bitwarden)" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Безопасно и безплатно управление за всичките ви устройства.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Промяна на главната парола" }, + "continueToWebApp": { + "message": "Продължаване към уеб приложението?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Може да промените главната си парола в уеб приложението на Битуорден." + }, "fingerprintPhrase": { "message": "Уникална фраза", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Добавена папка" }, - "changeMasterPass": { - "message": "Промяна на главната парола" - }, - "changeMasterPasswordConfirmation": { - "message": "Главната парола на трезор може да се промени чрез сайта bitwarden.com. Искате ли да го посетите?" - }, "twoStepLoginConfirmation": { "message": "Двустепенното вписване защитава регистрацията ви, като ви кара да потвърдите влизането си чрез устройство-ключ, приложение за удостоверение, мобилно съобщение, телефонно обаждане или електронна поща. Двустепенното вписване може да се включи чрез сайта bitwarden.com. Искате ли да го посетите?" }, @@ -3000,13 +3000,36 @@ "message": "Грешка при запазването на идентификационните данни. Вижте конзолата за подробности.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Успех" + }, "removePasskey": { "message": "Премахване на секретния ключ" }, "passkeyRemoved": { "message": "Секретният ключ е премахнат" }, - "unassignedItemsBanner": { - "message": "Забележка: неразпределените елементи на организацията вече не се виждат в изгледа с „Всички трезори“, а са достъпни само през Административната конзола. Добавете тези елементи към някоя колекция в Административната конзола, за да станат видими." + "unassignedItemsBannerNotice": { + "message": "Известие: неразпределените елементи в организацията вече няма да се виждат в изгледа с всички трезори, а са достъпни само през Административната конзола." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Известие: след 16 май 2024, неразпределените елементи в организацията вече няма да се виждат в изгледа с всички трезори, а ще бъдат достъпни само през Административната конзола." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Добавете тези елементи към колекция в", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "за да ги направите видими.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Административна конзола" + }, + "errorAssigningTargetCollection": { + "message": "Грешка при задаването на целева колекция." + }, + "errorAssigningTargetFolder": { + "message": "Грешка при задаването на целева папка." } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 4bdf811b3b..a12308648a 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "আপনার সমস্ত ডিভাইসের জন্য একটি সুরক্ষিত এবং বিনামূল্যের পাসওয়ার্ড ব্যবস্থাপক।", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "মূল পাসওয়ার্ড পরিবর্তন" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "ফিঙ্গারপ্রিন্ট ফ্রেজ", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "ফোল্ডার জোড়া হয়েছে" }, - "changeMasterPass": { - "message": "মূল পাসওয়ার্ড পরিবর্তন" - }, - "changeMasterPasswordConfirmation": { - "message": "আপনি bitwarden.com ওয়েব ভল্ট থেকে মূল পাসওয়ার্ডটি পরিবর্তন করতে পারেন। আপনি কি এখনই ওয়েবসাইটটি দেখতে চান?" - }, "twoStepLoginConfirmation": { "message": "দ্বি-পদক্ষেপ লগইন অন্য ডিভাইসে আপনার লগইনটি যাচাই করার জন্য সিকিউরিটি কী, প্রমাণীকরণকারী অ্যাপ্লিকেশন, এসএমএস, ফোন কল বা ই-মেইল ব্যাবহারের মাধ্যমে আপনার অ্যাকাউন্টকে আরও সুরক্ষিত করে। bitwarden.com ওয়েব ভল্টে দ্বি-পদক্ষেপের লগইন সক্ষম করা যাবে। আপনি কি এখনই ওয়েবসাইটটি দেখতে চান?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index a7f157011e..7f406fabee 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 147a64233c..7c8bd63aea 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Administrador de contrasenyes segur i gratuït per a tots els vostres dispositius.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Canvia la contrasenya mestra" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Frase d'empremta digital", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Carpeta afegida" }, - "changeMasterPass": { - "message": "Canvia la contrasenya mestra" - }, - "changeMasterPasswordConfirmation": { - "message": "Podeu canviar la contrasenya mestra a la caixa forta web de bitwarden.com. Voleu visitar el lloc web ara?" - }, "twoStepLoginConfirmation": { "message": "L'inici de sessió en dues passes fa que el vostre compte siga més segur, ja que obliga a verificar el vostre inici de sessió amb un altre dispositiu, com ara una clau de seguretat, una aplicació autenticadora, un SMS, una trucada telefònica o un correu electrònic. Es pot habilitar l'inici de sessió en dues passes a la caixa forta web de bitwarden.com. Voleu visitar el lloc web ara?" }, @@ -3000,13 +3000,36 @@ "message": "S'ha produït un error en guardar les credencials. Consulteu la consola per obtenir més informació.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Suprimeix la clau de pas" }, "passkeyRemoved": { "message": "Clau de pas suprimida" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 2b6a8d4f0b..bd3c6882df 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden – Bezplatný správce hesel", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bezpečný a bezplatný správce hesel pro všechna Vaše zařízení.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Změnit hlavní heslo" }, + "continueToWebApp": { + "message": "Pokračovat do webové aplikace?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Hlavní heslo můžete změnit ve webové aplikaci Bitwardenu." + }, "fingerprintPhrase": { "message": "Fráze otisku prstu", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Složka byla přidána" }, - "changeMasterPass": { - "message": "Změnit hlavní heslo" - }, - "changeMasterPasswordConfirmation": { - "message": "Hlavní heslo si můžete změnit na webové stránce bitwarden.com. Chcete tuto stránku nyní otevřít?" - }, "twoStepLoginConfirmation": { "message": "Dvoufázové přihlášení činí Váš účet mnohem bezpečnějším díky nutnosti po každém úspěšném přihlášení zadat ověřovací kód získaný z bezpečnostního klíče, aplikace, SMS, telefonního hovoru nebo e-mailu. Dvoufázové přihlášení lze aktivovat na webové stránce bitwarden.com. Chcete tuto stránku nyní otevřít?" }, @@ -3000,13 +3000,36 @@ "message": "Chyba při ukládání přihlašovacích údajů. Podrobnosti naleznete v konzoli.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Úspěch" + }, "removePasskey": { "message": "Odebrat přístupový klíč" }, "passkeyRemoved": { "message": "Přístupový klíč byl odebrán" }, - "unassignedItemsBanner": { - "message": "Upozornění: Nepřiřazené položky organizace již nejsou viditelné ve Vašem zobrazení všech trezorů a jsou nyní přístupné jen v konzoli správce. Přiřaďte tyto položky do kolekce z konzole pro správce, aby byly viditelné." + "unassignedItemsBannerNotice": { + "message": "Upozornění: Nepřiřazené položky organizace již nejsou viditelné ve vašem zobrazení všech trezorů a jsou nyní přístupné pouze v konzoli správce." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Upozornění: 16. květba 2024 již nebudou nepřiřazené položky organizace viditelné ve vašem zobrazení všech trezorů a budou přístupné pouze v konzoli správce." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Přiřadit tyto položky ke kolekci z", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "aby byly viditelné.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Konzole správce" + }, + "errorAssigningTargetCollection": { + "message": "Chyba při přiřazování cílové kolekce." + }, + "errorAssigningTargetFolder": { + "message": "Chyba při přiřazování cílové složky." } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 79867bf7bf..c718c1d876 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Rheolydd cyfineiriau am ddim", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Rheolydd cyfrineiriau diogel a rhad ac am ddim ar gyfer eich holl ddyfeisiau.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Newid y prif gyfrinair" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Ymadrodd unigryw", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Ffolder wedi'i hychwanegu" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 26e08741b2..777c3b484f 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Gratis adgangskodemanager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "En sikker og gratis adgangskodemanager til alle dine enheder.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Skift hovedadgangskode" }, + "continueToWebApp": { + "message": "Fortsæt til web-app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Hovedadgangskoden kan ændres via Bitwarden web-appen." + }, "fingerprintPhrase": { "message": "Fingeraftrykssætning", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Mappe tilføjet" }, - "changeMasterPass": { - "message": "Skift hovedadgangskode" - }, - "changeMasterPasswordConfirmation": { - "message": "Du kan ændre din hovedadgangskode i bitwarden.com web-boksen. Vil du besøge hjemmesiden nu?" - }, "twoStepLoginConfirmation": { "message": "To-trins login gør din konto mere sikker ved at kræve, at du verificerer dit login med en anden enhed, såsom en sikkerhedsnøgle, autentificeringsapp, SMS, telefonopkald eller e-mail. To-trins login kan aktiveres i bitwarden.com web-boksen. Vil du besøge hjemmesiden nu?" }, @@ -3000,13 +3000,36 @@ "message": "Fejl under import. Tjek konsollen for detaljer.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Gennemført" + }, "removePasskey": { "message": "Fjern adgangsnøgle" }, "passkeyRemoved": { "message": "Adgangsnøgle fjernet" }, - "unassignedItemsBanner": { - "message": "Bemærk: Utildelte organisationsemner er ikke længere synlige i Alle Bokse-visningen og er kun tilgængelige via Adminkonsollen. Føj disse emner til en samling fra Adminkonsollen for at gøre dem synlige." + "unassignedItemsBannerNotice": { + "message": "Bemærk: Utildelte organisationsemner er ikke længere synlige i Alle Bokse-visningen, men er kun tilgængelige via Admin-konsol." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Bemærk: Pr. 16. maj 2024 er utildelte organisationsemner er ikke længere synlige i Alle Bokse-visningen, men er kun tilgængelige via Admin-konsol." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Tildel disse emner til en samling via", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "for at gøre dem synlige.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin-konsol" + }, + "errorAssigningTargetCollection": { + "message": "Fejl ved tildeling af målsamling." + }, + "errorAssigningTargetFolder": { + "message": "Fejl ved tildeling af målmappe." } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index b542fbfad7..8f2a59af1e 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Kostenloser Passwortmanager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Ein sicherer und kostenloser Passwortmanager für all deine Geräte.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Master-Passwort ändern" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerabdruck-Phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Ordner hinzugefügt" }, - "changeMasterPass": { - "message": "Master-Passwort ändern" - }, - "changeMasterPasswordConfirmation": { - "message": "Du kannst dein Master-Passwort im Bitwarden.com Web-Tresor ändern. Möchtest du die Seite jetzt öffnen?" - }, "twoStepLoginConfirmation": { "message": "Mit der Zwei-Faktor-Authentifizierung wird dein Konto zusätzlich abgesichert, da jede Anmeldung mit einem anderen Gerät wie einem Sicherheitsschlüssel, einer Authentifizierungs-App, einer SMS, einem Anruf oder einer E-Mail verifiziert werden muss. Die Zwei-Faktor-Authentifizierung kann im bitwarden.com Web-Tresor aktiviert werden. Möchtest du die Website jetzt öffnen?" }, @@ -3000,13 +3000,36 @@ "message": "Fehler beim Speichern der Zugangsdaten. Details in der Konsole.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { - "message": "Passkey löschen" + "message": "Passkey entfernen" }, "passkeyRemoved": { - "message": "Passkey gelöscht" + "message": "Passkey entfernt" }, - "unassignedItemsBanner": { - "message": "Hinweis: Nicht zugeordnete Organisationseinträge sind nicht mehr in der Ansicht aller Tresore sichtbar und nur über die Administrator-Konsole zugänglich. Weise diese Einträge einer Sammlung aus der Administrator-Konsole zu, um sie sichtbar zu machen." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Fehler beim Zuweisen der Ziel-Sammlung." + }, + "errorAssigningTargetFolder": { + "message": "Fehler beim Zuweisen des Ziel-Ordners." } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 7bbc615dae..8c65e61e53 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Δωρεάν Διαχειριστής Κωδικών", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Ένας ασφαλής και δωρεάν διαχειριστής κωδικών, για όλες σας τις συσκευές.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Αλλαγή Κύριου Κωδικού" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Φράση Δακτυλικών Αποτυπωμάτων", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Προστέθηκε φάκελος" }, - "changeMasterPass": { - "message": "Αλλαγή Κύριου Κωδικού" - }, - "changeMasterPasswordConfirmation": { - "message": "Μπορείτε να αλλάξετε τον κύριο κωδικό στο bitwarden.com. Θέλετε να επισκεφθείτε την ιστοσελίδα τώρα;" - }, "twoStepLoginConfirmation": { "message": "Η σύνδεση σε δύο βήματα καθιστά πιο ασφαλή τον λογαριασμό σας, απαιτώντας να επαληθεύσετε τα στοιχεία σας με μια άλλη συσκευή, όπως κλειδί ασφαλείας, εφαρμογή επαλήθευσης, μήνυμα SMS, τηλεφωνική κλήση ή email. Μπορείτε να ενεργοποιήσετε τη σύνδεση σε δύο βήματα στο bitwarden.com. Θέλετε να επισκεφθείτε την ιστοσελίδα τώρα;" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 4108db3996..7e6e333689 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,16 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." }, - "unassignedItemsBannerSelfHost": { - "message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 1c24106cdc..e4d90adf1a 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organisation items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organisation items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organisation items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index f1a4979766..7cc17240d2 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Added folder" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be enabled on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 31200031f6..3e488bce4c 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bitwarden es un gestor de contraseñas seguro y gratuito para todos tus dispositivos.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Cambiar contraseña maestra" }, + "continueToWebApp": { + "message": "¿Continuar a la aplicación web?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Puedes cambiar la contraseña maestra en la aplicación web de Bitwarden." + }, "fingerprintPhrase": { "message": "Frase de huella digital", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Carpeta añadida" }, - "changeMasterPass": { - "message": "Cambiar contraseña maestra" - }, - "changeMasterPasswordConfirmation": { - "message": "Puedes cambiar tu contraseña maestra en la caja fuerte web de bitwarden.com. ¿Quieres visitar ahora el sitio web?" - }, "twoStepLoginConfirmation": { "message": "La autenticación en dos pasos hace que tu cuenta sea mucho más segura, requiriendo que introduzcas un código de seguridad de una aplicación de autenticación cada vez que accedes. La autenticación en dos pasos puede ser habilitada en la caja fuerte web de bitwarden.com. ¿Quieres visitar ahora el sitio web?" }, @@ -3000,13 +3000,36 @@ "message": "Se produjo un error al guardar las credenciales. Revise la consola para obtener detalles.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { - "message": "Remove passkey" + "message": "Eliminar passkey" }, "passkeyRemoved": { - "message": "Passkey removed" + "message": "Passkey eliminada" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 1f98d15758..785a3e4986 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Tasuta paroolihaldur", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Turvaline ja tasuta paroolihaldur kõikidele sinu seadmetele.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Muuda ülemparooli" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Sõrmejälje fraas", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Kaust on lisatud" }, - "changeMasterPass": { - "message": "Muuda ülemparooli" - }, - "changeMasterPasswordConfirmation": { - "message": "Saad oma ülemparooli muuta bitwarden.com veebihoidlas. Soovid seda kohe teha?" - }, "twoStepLoginConfirmation": { "message": "Kaheastmeline kinnitamine aitab konto turvalisust tõsta. Lisaks paroolile pead kontole ligipääsemiseks kinnitama sisselogimise päringu SMS-ga, telefonikõnega, autentimise rakendusega või e-postiga. Kaheastmelist kinnitust saab sisse lülitada bitwarden.com veebihoidlas. Soovid seda kohe avada?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { - "message": "Remove passkey" + "message": "Eemalda pääsuvõti" }, "passkeyRemoved": { - "message": "Passkey removed" + "message": "Pääsuvõti on eemaldatud" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 49086462d9..9a07b9d9ae 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Pasahitz kudeatzailea", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bitwarden, zure gailu guztietarako pasahitzen kudeatzaile seguru eta doakoa da.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Aldatu pasahitz nagusia" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Hatz-marka digitalaren esaldia", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Karpeta gehituta" }, - "changeMasterPass": { - "message": "Aldatu pasahitz nagusia" - }, - "changeMasterPasswordConfirmation": { - "message": "Zure pasahitz nagusia alda dezakezu bitwarden.com webgunean. Orain joan nahi duzu webgunera?" - }, "twoStepLoginConfirmation": { "message": "Bi urratseko saio hasiera dela eta, zure kontua seguruagoa da, beste aplikazio/gailu batekin saioa hastea eskatzen baitizu; adibidez, segurtasun-gako, autentifikazio-aplikazio, SMS, telefono dei edo email bidez. Bi urratseko saio hasiera bitwarden.com webgunean aktibatu daiteke. Orain joan nahi duzu webgunera?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 0c44026cec..c68dc43ef4 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - مدیریت کلمه عبور رایگان", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "یک مدیریت کننده کلمه عبور رایگان برای تمامی دستگاه‌هایتان.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "تغییر کلمه عبور اصلی" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "عبارت اثر انگشت", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "پوشه اضافه شد" }, - "changeMasterPass": { - "message": "تغییر کلمه عبور اصلی" - }, - "changeMasterPasswordConfirmation": { - "message": "شما می‌توانید کلمه عبور اصلی خود را در bitwarden.com تغییر دهید. آیا می‌خواهید از سایت بازدید کنید؟" - }, "twoStepLoginConfirmation": { "message": "ورود دو مرحله ای باعث می‌شود که حساب کاربری شما با استفاده از یک دستگاه دیگر مانند کلید امنیتی، برنامه احراز هویت، پیامک، تماس تلفنی و یا ایمیل، اعتبار خود را با ایمنی بیشتر اثبات کند. ورود دو مرحله ای می تواند در bitwarden.com فعال شود. آیا می‌خواهید از سایت بازدید کنید؟" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index ead3d43236..2cdb6a2379 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden – Ilmainen salasanahallinta", + "message": "Bitwarden – Salasanahallinta", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Turvallinen ja ilmainen salasanahallinta kaikille laitteillesi.", + "message": "Kotona, töissä tai reissussa, Bitwarden suojaa helposti kaikki salasanasi, avainkoodisi ja arkaluonteiset tietosi.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Vaihda pääsalasana" }, + "continueToWebApp": { + "message": "Avataanko verkkosovellus?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Voit vaihtaa pääsalasanasi Bitwardenin verkkosovelluksessa." + }, "fingerprintPhrase": { "message": "Tunnistelauseke", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Kansio lisätty" }, - "changeMasterPass": { - "message": "Vaihda pääsalasana" - }, - "changeMasterPasswordConfirmation": { - "message": "Voit vaihtaa pääsalasanasi bitwarden.com-verkkoholvissa. Haluatko käydä sivustolla nyt?" - }, "twoStepLoginConfirmation": { "message": "Kaksivaiheinen kirjautuminen parantaa tilisi suojausta vaatimalla kirjautumisen vahvistuksen salasanan lisäksi todennuslaitteen, ‑sovelluksen, tekstiviestin, puhelun tai sähköpostin avulla. Voit ottaa kaksivaiheisen kirjautumisen käyttöön bitwarden.com‑verkkoholvissa. Haluatko avata sen nyt?" }, @@ -3000,13 +3000,36 @@ "message": "Virhe tallennettaessa käyttäjätietoja. Näet isätietoja hallinnasta.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Onnistui" + }, "removePasskey": { "message": "Poista suojausavain" }, "passkeyRemoved": { "message": "Suojausavain poistettiin" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Huomioi: Määrittämättömät organisaatiokohteet eivät enää näy \"Kaikki holvit\" -näkymässä, vaan ne ovat käytettävissä vain Hallintapaneelista." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Huomioi: Alkaen 16. toukokuuta 2024, määrittämättömät organisaatiokohteet eivät enää näy \"Kaikki holvit\" -näkymässä, vaan ne ovat käytettävissä vain Hallintapaneelista." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Määritä nämä kohteet kokoelmaan", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": ", jotta ne näkyvät.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Hallintapaneelista" + }, + "errorAssigningTargetCollection": { + "message": "Virhe määritettäessä kohdekokoelmaa." + }, + "errorAssigningTargetFolder": { + "message": "Virhe määritettäessä kohdekansiota." } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index e9bc8e2c75..0dfb4a39c9 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Libreng Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Isang ligtas at libreng password manager para sa lahat ng iyong mga aparato.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Baguhin ang Master Password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Hulmabig ng Hilik ng Dako", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Idinagdag na folder" }, - "changeMasterPass": { - "message": "Palitan ang master password" - }, - "changeMasterPasswordConfirmation": { - "message": "Maaari mong palitan ang iyong master password sa bitwarden.com web vault. Gusto mo bang bisitahin ang website ngayon?" - }, "twoStepLoginConfirmation": { "message": "Ang two-step login ay nagpapagaan sa iyong account sa pamamagitan ng pag-verify sa iyong login sa isa pang device tulad ng security key, authenticator app, SMS, tawag sa telepono o email. Ang two-step login ay maaaring magawa sa bitwarden.com web vault. Gusto mo bang bisitahin ang website ngayon?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 452a2b363b..742e31ee58 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Gestion des mots de passe", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Un gestionnaire de mots de passe sécurisé et gratuit pour tous vos appareils.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Changer le mot de passe principal" }, + "continueToWebApp": { + "message": "Poursuivre vers l'application web ?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Vous pouvez modifier votre mot de passe principal sur l'application web de Bitwarden." + }, "fingerprintPhrase": { "message": "Phrase d'empreinte", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -525,13 +531,13 @@ "message": "Impossible de scanner le QR code à partir de la page web actuelle" }, "totpCaptureSuccess": { - "message": "Clé de l'Authentificateur ajoutée" + "message": "Clé Authenticator ajoutée" }, "totpCapture": { "message": "Scanner le QR code de l'authentificateur à partir de la page web actuelle" }, "copyTOTP": { - "message": "Copier la clé de l'Authentificateur (TOTP)" + "message": "Copier la clé Authenticator (TOTP)" }, "loggedOut": { "message": "Déconnecté" @@ -557,12 +563,6 @@ "addedFolder": { "message": "Dossier ajouté" }, - "changeMasterPass": { - "message": "Changer le mot de passe principal" - }, - "changeMasterPasswordConfirmation": { - "message": "Vous pouvez changer votre mot de passe principal depuis le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" - }, "twoStepLoginConfirmation": { "message": "L'authentification à deux facteurs rend votre compte plus sûr en vous demandant de vérifier votre connexion avec un autre dispositif tel qu'une clé de sécurité, une application d'authentification, un SMS, un appel téléphonique ou un courriel. L'authentification à deux facteurs peut être configurée dans le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" }, @@ -659,7 +659,7 @@ "message": "Liste les éléments des cartes de paiement sur la Page d'onglet pour faciliter la saisie automatique." }, "showIdentitiesCurrentTab": { - "message": "Afficher les identités sur la page d'onglet" + "message": "Afficher les identités sur la Page d'onglet" }, "showIdentitiesCurrentTabDesc": { "message": "Liste les éléments d'identité sur la Page d'onglet pour faciliter la saisie automatique." @@ -802,7 +802,7 @@ "message": "En savoir plus" }, "authenticatorKeyTotp": { - "message": "Clé de l'Authentificateur (TOTP)" + "message": "Clé Authenticator (TOTP)" }, "verificationCodeTotp": { "message": "Code de vérification (TOTP)" @@ -3000,13 +3000,36 @@ "message": "Erreur lors de l'enregistrement des identifiants. Consultez la console pour plus de détails.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Retirer la clé d'identification (passkey)" }, "passkeyRemoved": { "message": "Clé d'identification (passkey) retirée" }, - "unassignedItemsBanner": { - "message": "Notice : les éléments d'organisation non assignés ne sont plus visibles dans la vue Tous les coffres et sont uniquement accessibles via la console d'administration. Assignez ces éléments à une collection à partir de la console d'administration pour les rendre visibles." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index dc91c1e6a9..b4c151eeb0 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -3,39 +3,39 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { "message": "Log in or create a new account to access your secure vault." }, "createAccount": { - "message": "Create account" + "message": "Crea unha conta" }, "login": { - "message": "Log in" + "message": "Iniciar sesión" }, "enterpriseSingleSignOn": { "message": "Enterprise single sign-on" }, "cancel": { - "message": "Cancel" + "message": "Cancelar" }, "close": { - "message": "Close" + "message": "Pechar" }, "submit": { "message": "Submit" }, "emailAddress": { - "message": "Email address" + "message": "Enderezo de correo electrónico" }, "masterPass": { - "message": "Master password" + "message": "Contrasinal mestre" }, "masterPassDesc": { "message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it." @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 7a05907228..61482da54a 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - מנהל סיסמאות חינמי", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "מנהל סיסמאות חינמי ומאובטח עבור כל המכשירים שלך.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "החלף סיסמה ראשית" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "סיסמת טביעת אצבע", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "נוספה תיקייה" }, - "changeMasterPass": { - "message": "החלף סיסמה ראשית" - }, - "changeMasterPasswordConfirmation": { - "message": "באפשרותך לשנות את הסיסמה הראשית שלך דרך הכספת באתר bitwarden.com. האם ברצונך לפתוח את האתר כעת?" - }, "twoStepLoginConfirmation": { "message": "התחברות בשני-שלבים הופכת את החשבון שלך למאובטח יותר בכך שאתה נדרש לוודא בכל כניסה בעזרת מכשיר אחר כדוגמת מפתח אבטחה, תוכנת אימות, SMS, שיחת טלפון, או אימייל. ניתן להפעיל את \"התחברות בשני-שלבים\" בכספת שבאתר bitwarden.com. האם ברצונך לפתוח את האתר כעת?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 99daecc5f8..b76405eed8 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -3,11 +3,11 @@ "message": "bitwarden" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "bitwarden is a secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change Master Password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint Phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "जोड़ा गया फ़ोल्डर" }, - "changeMasterPass": { - "message": "Change Master Password" - }, - "changeMasterPasswordConfirmation": { - "message": "आप वेब वॉल्ट bitwarden.com पर अपना मास्टर पासवर्ड बदल सकते हैं।क्या आप अब वेबसाइट पर जाना चाहते हैं?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to enter a security code from an authenticator app whenever you log in. Two-step login can be enabled on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index ec4509dbd4..2dc500bc1e 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - besplatni upravitelj lozinki", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bitwarden je siguran i besplatan upravitelj lozinki za sve tvoje uređaje.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Promjeni glavnu lozinku" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Jedinstvena fraza", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Mapa dodana" }, - "changeMasterPass": { - "message": "Promjeni glavnu lozinku" - }, - "changeMasterPasswordConfirmation": { - "message": "Svoju glavnu lozinku možeš promijeniti na web trezoru. Želiš li sada posjetiti bitwarden.com?" - }, "twoStepLoginConfirmation": { "message": "Prijava dvostrukom autentifikacijom čini tvoj račun još sigurnijim tako što će zahtijevati da potvrdiš prijavu putem drugog uređaja pomoću sigurnosnog koda, autentifikatorske aplikacije, SMS-om, pozivom ili e-poštom. Prijavu dvostrukom autentifikacijom možeš omogućiti na web trezoru. Želiš li sada posjetiti bitwarden.com?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 5c10efc3ae..e47f2cda1f 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Ingyenes jelszókezelő", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Egy biztonságos és ingyenes jelszókezelő az összes eszközre.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Mesterjelszó módosítása" }, + "continueToWebApp": { + "message": "Tovább a webes alkalmazáshoz?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "A mesterjelszó a Bitwarden webalkalmazásban módosítható." + }, "fingerprintPhrase": { "message": "Ujjlenyomat kifejezés", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "A mappa hozzáadásra került." }, - "changeMasterPass": { - "message": "Mesterjelszó módosítása" - }, - "changeMasterPasswordConfirmation": { - "message": "Mesterjelszavadat a bitwarden.com webes széfén tudod megváltoztatni. Szeretnéd meglátogatni a most a weboldalt?" - }, "twoStepLoginConfirmation": { "message": "A kétlépcsős bejelentkezés biztonságosabbá teszi a fiókot azáltal, hogy ellenőrizni kell a bejelentkezést egy másik olyan eszközzel mint például biztonsági kulcs, hitelesítő alkalmazás, SMS, telefon hívás vagy email. A kétlépcsős bejelentkezést a bitwarden.com webes széfben lehet engedélyezni. Felkeressük a webhelyet most?" }, @@ -3000,13 +3000,36 @@ "message": "Hiba történt a hitelesítések mentésekor. A részletekért ellenőrizzük a konzolt.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Sikeres" + }, "removePasskey": { "message": "Jelszó eltávolítása" }, "passkeyRemoved": { "message": "A jelszó eltávolításra került." }, - "unassignedItemsBanner": { - "message": "Megjegyzés: A nem hozzá nem rendelt szervezeti elemek már nem láthatók az Összes széf nézetben és csak az Adminisztrátori konzolon keresztül érhetők el. Rendeljük ezeket az elemeket egy gyűjteményhez az Adminisztrátor konzolból, hogy láthatóvá tegyük azokat." + "unassignedItemsBannerNotice": { + "message": "Megjegyzés: A nem hozzárendelt szervezeti elemek már nem láthatók az Összes széf nézetben a különböző eszközökön és csak az Adminisztrátori konzolon keresztül érhetők el." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Megjegyzés: 2024. május 16-tól a nem hozzárendelt szervezeti elemek többé nem lesznek láthatók az Összes széf nézetben a különböző eszközökön és csak az Adminisztrátori konzolon keresztül lesznek elérhetők." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Rendeljük hozzá ezeket az elemeket a gyűjteményhez", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "a láthatósághoz.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Adminisztrátori konzol" + }, + "errorAssigningTargetCollection": { + "message": "Hiba történt a célgyűjtemény hozzárendelése során." + }, + "errorAssigningTargetFolder": { + "message": "Hiba történt a célmappa hozzárendelése során." } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 810d603047..d4399d8e15 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Pengelola Sandi Gratis", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bitwarden adalah sebuah pengelola sandi yang aman dan gratis untuk semua perangkat Anda.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Ubah Kata Sandi Utama" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Frasa Sidik Jari", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Tambah Folder" }, - "changeMasterPass": { - "message": "Ubah Kata Sandi Utama" - }, - "changeMasterPasswordConfirmation": { - "message": "Anda dapat mengubah kata sandi utama Anda di brankas web bitwarden.com. Anda ingin mengunjungi situs web sekarang?" - }, "twoStepLoginConfirmation": { "message": "Info masuk dua langkah membuat akun Anda lebih aman dengan mengharuskan Anda memverifikasi info masuk Anda dengan peranti lain seperti kode keamanan, aplikasi autentikasi, SMK, panggilan telepon, atau email. Info masuk dua langkah dapat diaktifkan di brankas web bitwarden.com. Anda ingin mengunjungi situs web sekarang?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 86a6bf054e..93ae682190 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Password Manager Gratis", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Un password manager sicuro e gratis per tutti i tuoi dispositivi.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Cambia password principale" }, + "continueToWebApp": { + "message": "Passa al sito web?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Puoi modificare la tua password principale sul sito web di Bitwarden." + }, "fingerprintPhrase": { "message": "Frase impronta", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Cartella aggiunta" }, - "changeMasterPass": { - "message": "Cambia password principale" - }, - "changeMasterPasswordConfirmation": { - "message": "Puoi cambiare la tua password principale sulla cassaforte online di bitwarden.com. Vuoi visitare ora il sito?" - }, "twoStepLoginConfirmation": { "message": "La verifica in due passaggi rende il tuo account più sicuro richiedendoti di verificare il tuo login usando un altro dispositivo come una chiave di sicurezza, app di autenticazione, SMS, telefonata, o email. Può essere abilitata nella cassaforte web su bitwarden.com. Vuoi visitare il sito?" }, @@ -3000,13 +3000,36 @@ "message": "Errore durante il salvataggio delle credenziali. Controlla la console per più dettagli.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Successo" + }, "removePasskey": { "message": "Rimuovi passkey" }, "passkeyRemoved": { "message": "Passkey rimossa" }, - "unassignedItemsBanner": { - "message": "Avviso: gli elementi dell'organizzazione non assegnati non sono più visibili nella visualizzazione Tutte le Cassaforti su tutti i dispositivi e sono ora accessibili solo tramite la Console di amministrazione. Assegna questi elementi ad una raccolta dalla Console di amministrazione per renderli visibili." + "unassignedItemsBannerNotice": { + "message": "Avviso: gli elementi dell'organizzazione non assegnati non sono più visibili nella visualizzazione Tutte le Cassaforti su tutti i dispositivi e sono ora accessibili solo tramite la Console di amministrazione." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Avviso: dal 16 maggio 2024, gli elementi dell'organizzazione non assegnati non saranno più visibili nella visualizzazione Tutte le Cassaforti su tutti i dispositivi e saranno accessibili solo tramite la Console di amministrazione." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assegna questi elementi ad una raccolta dalla", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "per renderli visibili.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Console di amministrazione" + }, + "errorAssigningTargetCollection": { + "message": "Errore nell'assegnazione della raccolta di destinazione." + }, + "errorAssigningTargetFolder": { + "message": "Errore nell'assegnazione della cartella di destinazione." } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index eb78a7205f..52ff21727a 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - 無料パスワードマネージャー", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bitwarden はあらゆる端末で使える、安全な無料パスワードマネージャーです。", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "マスターパスワードの変更" }, + "continueToWebApp": { + "message": "ウェブアプリに進みますか?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Bitwarden ウェブアプリでマスターパスワードを変更できます。" + }, "fingerprintPhrase": { "message": "パスフレーズ", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "フォルダを追加しました" }, - "changeMasterPass": { - "message": "マスターパスワードの変更" - }, - "changeMasterPasswordConfirmation": { - "message": "マスターパスワードは bitwarden.com ウェブ保管庫で変更できます。ウェブサイトを開きますか?" - }, "twoStepLoginConfirmation": { "message": "2段階認証を使うと、ログイン時にセキュリティキーや認証アプリ、SMS、電話やメールでの認証を必要にすることでアカウントをさらに安全に出来ます。2段階認証は bitwarden.com ウェブ保管庫で有効化できます。ウェブサイトを開きますか?" }, @@ -3000,13 +3000,36 @@ "message": "資格情報の保存中にエラーが発生しました。詳細はコンソールを確認してください。", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "成功" + }, "removePasskey": { "message": "パスキーを削除" }, "passkeyRemoved": { "message": "パスキーを削除しました" }, - "unassignedItemsBanner": { - "message": "注意: 割り当てられていない組織項目は、すべての保管庫のビューでは表示されなくなり、管理コンソールからのみアクセスできます。 管理コンソールからコレクションにこれらのアイテムを割り当てると、表示するようにできます。" + "unassignedItemsBannerNotice": { + "message": "注意: 割り当てられていない組織アイテムは、すべての保管庫ビューでは表示されなくなり、管理コンソールからのみアクセスできるようになります。" + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "お知らせ:2024年5月16日に、 割り当てられていない組織アイテムは、すべての保管庫ビューに表示されなくなり、管理コンソールからのみアクセス可能になります。" + }, + "unassignedItemsBannerCTAPartOne": { + "message": "これらのアイテムのコレクションへの割り当てを", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "で実行すると表示できるようになります。", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "管理コンソール" + }, + "errorAssigningTargetCollection": { + "message": "ターゲットコレクションの割り当てに失敗しました。" + }, + "errorAssigningTargetFolder": { + "message": "ターゲットフォルダーの割り当てに失敗しました。" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 66dae59f4c..2c18502eca 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index dc91c1e6a9..5d1b024c60 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 68fe45999d..047270808e 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -3,11 +3,11 @@ "message": "ಬಿಟ್ವಾರ್ಡೆನ್" }, "extName": { - "message": "ಬಿಟ್‌ವಾರ್ಡೆನ್ - ಉಚಿತ ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕ", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "ನಿಮ್ಮ ಎಲ್ಲಾ ಸಾಧನಗಳಿಗೆ ಸುರಕ್ಷಿತ ಮತ್ತು ಉಚಿತ ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕ.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "ಮಾಸ್ಟರ್ ಪಾಸ್ವರ್ಡ್ ಬದಲಾಯಿಸಿ" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಫ್ರೇಸ್", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "ಫೋಲ್ಡರ್ ಸೇರಿಸಿ" }, - "changeMasterPass": { - "message": "ಮಾಸ್ಟರ್ ಪಾಸ್ವರ್ಡ್ ಬದಲಾಯಿಸಿ" - }, - "changeMasterPasswordConfirmation": { - "message": "ನಿಮ್ಮ ಮಾಸ್ಟರ್ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ನೀವು bitwarden.com ವೆಬ್ ವಾಲ್ಟ್‌ನಲ್ಲಿ ಬದಲಾಯಿಸಬಹುದು. ನೀವು ಈಗ ವೆಬ್‌ಸೈಟ್‌ಗೆ ಭೇಟಿ ನೀಡಲು ಬಯಸುವಿರಾ?" - }, "twoStepLoginConfirmation": { "message": "ಭದ್ರತಾ ಕೀ, ದೃಢೀಕರಣ ಅಪ್ಲಿಕೇಶನ್, ಎಸ್‌ಎಂಎಸ್, ಫೋನ್ ಕರೆ ಅಥವಾ ಇಮೇಲ್‌ನಂತಹ ಮತ್ತೊಂದು ಸಾಧನದೊಂದಿಗೆ ನಿಮ್ಮ ಲಾಗಿನ್ ಅನ್ನು ಪರಿಶೀಲಿಸುವ ಅಗತ್ಯವಿರುವ ಎರಡು ಹಂತದ ಲಾಗಿನ್ ನಿಮ್ಮ ಖಾತೆಯನ್ನು ಹೆಚ್ಚು ಸುರಕ್ಷಿತಗೊಳಿಸುತ್ತದೆ. ಬಿಟ್ವಾರ್ಡೆನ್.ಕಾಮ್ ವೆಬ್ ವಾಲ್ಟ್ನಲ್ಲಿ ಎರಡು-ಹಂತದ ಲಾಗಿನ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಬಹುದು. ನೀವು ಈಗ ವೆಬ್‌ಸೈಟ್‌ಗೆ ಭೇಟಿ ನೀಡಲು ಬಯಸುವಿರಾ?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 689e618929..4bc4302f8b 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - 무료 비밀번호 관리자", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "당신의 모든 기기에서 사용할 수 있는, 안전한 무료 비밀번호 관리자입니다.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "마스터 비밀번호 변경" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "지문 구절", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "폴더 추가함" }, - "changeMasterPass": { - "message": "마스터 비밀번호 변경" - }, - "changeMasterPasswordConfirmation": { - "message": "bitwarden.com 웹 보관함에서 마스터 비밀번호를 바꿀 수 있습니다. 지금 웹 사이트를 방문하시겠습니까?" - }, "twoStepLoginConfirmation": { "message": "2단계 인증은 보안 키, 인증 앱, SMS, 전화 통화 등의 다른 기기로 사용자의 로그인 시도를 검증하여 사용자의 계정을 더욱 안전하게 만듭니다. 2단계 인증은 bitwarden.com 웹 보관함에서 활성화할 수 있습니다. 지금 웹 사이트를 방문하시겠습니까?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index c99d128915..b1a2c857e0 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Saugi ir nemokama slaptažodžių tvarkyklė visiems įrenginiams.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Keisti pagrindinį slaptažodį" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Pirštų atspaudų frazė", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Katalogas pridėtas" }, - "changeMasterPass": { - "message": "Keisti pagrindinį slaptažodį" - }, - "changeMasterPasswordConfirmation": { - "message": "Pagrindinį slaptažodį galite pakeisti „bitwarden.com“ žiniatinklio saugykloje. Ar norite dabar apsilankyti svetainėje?" - }, "twoStepLoginConfirmation": { "message": "Prisijungus dviem veiksmais, jūsų paskyra tampa saugesnė, reikalaujant patvirtinti prisijungimą naudojant kitą įrenginį, pvz., saugos raktą, autentifikavimo programėlę, SMS, telefono skambutį ar el. paštą. Dviejų žingsnių prisijungimą galima įjungti „bitwarden.com“ interneto saugykloje. Ar norite dabar apsilankyti svetainėje?" }, @@ -3000,13 +3000,36 @@ "message": "Klaida išsaugant kredencialus. Išsamesnės informacijos patikrink konsolėje.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Pašalinti slaptaraktį" }, "passkeyRemoved": { "message": "Pašalintas slaptaraktis" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index c36315d10d..4055693486 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Drošs bezmaksas paroļu pārvaldnieks visām Tavām ierīcēm.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Mainīt galveno paroli" }, + "continueToWebApp": { + "message": "Pāriet uz tīmekļa lietotni?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Savu galveno paroli var mainīt Bitwarden tīmekļa lietotnē." + }, "fingerprintPhrase": { "message": "Atpazīšanas vārdkopa", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Pievienoja mapi" }, - "changeMasterPass": { - "message": "Mainīt galveno paroli" - }, - "changeMasterPasswordConfirmation": { - "message": "Galveno paroli ir iespējams mainīt bitwarden.com tīmekļa glabātavā. Vai tagad apmeklēt tīmekļvietni?" - }, "twoStepLoginConfirmation": { "message": "Divpakāpju pieteikšanās padara kontu krietni drošāku, pieprasot apstiprināt pieteikšanos ar tādu citu ierīču vai pakalpojumu starpniecību kā drošības atslēga, autentificētāja lietotne, īsziņa, tālruņa zvans vai e-pasts. Divpakāpju pieteikšanos var iespējot bitwarden.com tīmekļa glabātavā. Vai tagad apmeklēt tīmekļvietni?" }, @@ -3000,13 +3000,36 @@ "message": "Kļūda piekļuves informācijas saglabāšanā. Jāpārbauda, vai konsolē ir izvērstāka informācija.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Izdevās" + }, "removePasskey": { "message": "Noņemt piekļuves atslēgu" }, "passkeyRemoved": { "message": "Piekļuves atslēga noņemta" }, - "unassignedItemsBanner": { - "message": "Jāņem vērā: nepiešķirti apvienības vienumi vairs nav redzami skatā \"Visas glabātavas\" un ir sasniedzami tikai no pārvaldības konsoles, kur šie vienumi jāpiešķir krājumam, lai padarītu tos redzamus." + "unassignedItemsBannerNotice": { + "message": "Jāņem vērā: nepiešķirti apvienības vienumi vairs nav redzami skatā \"Visas glabātavas\" un ir pieejami tikai pārvaldības konsolē." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Jāņem vērā: no 2024. gada 16. maija nepiešķirti apvienības vienumi vairs nebūs redzami skatā \"Visas glabātavas\" un būs pieejami tikai pārvaldības konsolē." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Piešķirt šos vienumus krājumam", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "lai padarītu tos redzamus.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "pārvaldības konsolē," + }, + "errorAssigningTargetCollection": { + "message": "Kļūda mērķa krājuma piešķiršanā." + }, + "errorAssigningTargetFolder": { + "message": "Kļūda mērķa mapes piešķiršanā." } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 7205a54559..d9703137fe 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - സൗജന്യ പാസ്സ്‌വേഡ് മാനേജർ ", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങൾക്കും സുരക്ഷിതവും സൗജന്യവുമായ പാസ്‌വേഡ് മാനേജർ.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "പ്രാഥമിക പാസ്‌വേഡ് മാറ്റുക" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "ഫിംഗർപ്രിന്റ് ഫ്രേസ്‌", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "ചേർക്കപ്പെട്ട ഫോൾഡർ" }, - "changeMasterPass": { - "message": "പ്രാഥമിക പാസ്‌വേഡ് മാറ്റുക" - }, - "changeMasterPasswordConfirmation": { - "message": "തങ്ങൾക്കു ബിറ്റ് വാർഡൻ വെബ് വാൾട്ടിൽ പ്രാഥമിക പാസ്‌വേഡ് മാറ്റാൻ സാധിക്കും.വെബ്സൈറ്റ് ഇപ്പോൾ സന്ദർശിക്കാൻ ആഗ്രഹിക്കുന്നുവോ?" - }, "twoStepLoginConfirmation": { "message": "സുരക്ഷാ കീ, ഓതന്റിക്കേറ്റർ അപ്ലിക്കേഷൻ, SMS, ഫോൺ കോൾ അല്ലെങ്കിൽ ഇമെയിൽ പോലുള്ള മറ്റൊരു ഉപകരണം ഉപയോഗിച്ച് തങ്ങളുടെ ലോഗിൻ സ്ഥിരീകരിക്കാൻ ആവശ്യപ്പെടുന്നതിലൂടെ രണ്ട്-ഘട്ട ലോഗിൻ തങ്ങളുടെ അക്കൗണ്ടിനെ കൂടുതൽ സുരക്ഷിതമാക്കുന്നു. bitwarden.com വെബ് വാൾട്ടിൽ രണ്ട്-ഘട്ട ലോഗിൻ പ്രവർത്തനക്ഷമമാക്കാനാകും.തങ്ങള്ക്കു ഇപ്പോൾ വെബ്സൈറ്റ് സന്ദർശിക്കാൻ ആഗ്രഹമുണ്ടോ?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 918557feb8..f67f617d3b 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - विनामूल्य पासवर्ड व्यवस्थापक", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "तुमच्या सर्व उपकरणांसाठी एक सुरक्षित व विनामूल्य पासवर्ड व्यवस्थापक.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "मुख्य पासवर्ड बदला" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "अंगुलिमुद्रा वाक्यांश", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index dc91c1e6a9..5d1b024c60 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index d77aaa7d7c..649163a8dc 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden — Fri passordbehandling", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bitwarden er en sikker og fri passordbehandler for alle dine PCer og mobiler.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Endre hovedpassordet" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingeravtrykksfrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "La til en mappe" }, - "changeMasterPass": { - "message": "Endre hovedpassordet" - }, - "changeMasterPasswordConfirmation": { - "message": "Du kan endre superpassordet ditt på bitwarden.net-netthvelvet. Vil du besøke det nettstedet nå?" - }, "twoStepLoginConfirmation": { "message": "2-trinnsinnlogging gjør kontoen din mer sikker, ved å kreve at du verifiserer din innlogging med en annen enhet, f.eks. en autentiseringsapp, SMS, e-post, telefonsamtale, eller sikkerhetsnøkkel. 2-trinnsinnlogging kan aktiveres i netthvelvet ditt på bitwarden.com. Vil du besøke bitwarden.com nå?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index dc91c1e6a9..5d1b024c60 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index bbf16b345a..5a52b4f7ef 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Gratis wachtwoordbeheer", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Een veilige en gratis oplossing voor wachtwoordbeheer voor al je apparaten.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Hoofdwachtwoord wijzigen" }, + "continueToWebApp": { + "message": "Doorgaan naar web-app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Je kunt je hoofdwachtwoord wijzigen in de Bitwarden-webapp." + }, "fingerprintPhrase": { "message": "Vingerafdrukzin", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Map is toegevoegd" }, - "changeMasterPass": { - "message": "Hoofdwachtwoord wijzigen" - }, - "changeMasterPasswordConfirmation": { - "message": "Je kunt je hoofdwachtwoord wijzigen in de kluis op bitwarden.com. Wil je de website nu bezoeken?" - }, "twoStepLoginConfirmation": { "message": "Tweestapsaanmelding beschermt je account door je inlogpoging te bevestigen met een ander apparaat zoals een beveiligingscode, authenticatie-app, SMS, spraakoproep of e-mail. Je kunt Tweestapsaanmelding inschakelen in de webkluis op bitwarden.com. Wil je de website nu bezoeken?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Succes" + }, "removePasskey": { "message": "Passkey verwijderen" }, "passkeyRemoved": { "message": "Passkey verwijderd" }, - "unassignedItemsBanner": { - "message": "Let op: Niet-toegewezen organisatie-items zijn niet langer zichtbaar in de weergave van alle kluisjes en zijn alleen toegankelijk via de Admin Console. Om deze items zichtbaar te maken, moet je ze toewijzen aan een collectie via de Admin Console." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Fout bij toewijzen doelverzameling." + }, + "errorAssigningTargetFolder": { + "message": "Fout bij toewijzen doelmap." } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index dc91c1e6a9..5d1b024c60 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index dc91c1e6a9..5d1b024c60 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 98130de3be..e768a70d52 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - darmowy menedżer haseł", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bezpieczny i darmowy menedżer haseł dla wszystkich Twoich urządzeń.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Zmień hasło główne" }, + "continueToWebApp": { + "message": "Kontynuować do aplikacji internetowej?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Możesz zmienić swoje hasło główne w aplikacji internetowej Bitwarden." + }, "fingerprintPhrase": { "message": "Unikalny identyfikator konta", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder został dodany" }, - "changeMasterPass": { - "message": "Zmień hasło główne" - }, - "changeMasterPasswordConfirmation": { - "message": "Hasło główne możesz zmienić na stronie sejfu bitwarden.com. Czy chcesz przejść do tej strony?" - }, "twoStepLoginConfirmation": { "message": "Logowanie dwustopniowe sprawia, że konto jest bardziej bezpieczne poprzez wymuszenie potwierdzenia logowania z innego urządzenia, takiego jak z klucza bezpieczeństwa, aplikacji uwierzytelniającej, wiadomości SMS, telefonu lub adresu e-mail. Logowanie dwustopniowe możesz włączyć w sejfie internetowym bitwarden.com. Czy chcesz przejść do tej strony?" }, @@ -2709,7 +2709,7 @@ "message": "Otwórz rozszerzenie w nowym oknie, aby dokończyć logowanie." }, "popoutExtension": { - "message": "Popout extension" + "message": "Otwórz rozszerzenie w nowym oknie" }, "launchDuo": { "message": "Uruchom DUO" @@ -2822,7 +2822,7 @@ "message": "Wybierz dane logowania do których przypisać passkey" }, "passkeyItem": { - "message": "Passkey Item" + "message": "Element Passkey" }, "overwritePasskey": { "message": "Zastąpić passkey?" @@ -3000,13 +3000,36 @@ "message": "Błąd podczas zapisywania danych logowania. Sprawdź konsolę, aby uzyskać szczegóły.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Sukces" + }, "removePasskey": { "message": "Usuń passkey" }, "passkeyRemoved": { "message": "Passkey został usunięty" }, - "unassignedItemsBanner": { - "message": "Uwaga: Nieprzypisane elementy w organizacji nie są już widoczne w widoku Wszystkie sejfy i są dostępne tylko przez Konsolę Administracyjną. Przypisz te elementy do kolekcji z konsoli administracyjnej, aby były one widoczne." + "unassignedItemsBannerNotice": { + "message": "Uwaga: Nieprzypisane elementy organizacji nie są już widoczne w widoku Wszystkie sejfy i są teraz dostępne tylko przez Konsolę Administracyjną." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Uwaga: 16 maja 2024 r. nieprzypisana elementy w organizacji nie będą już widoczne w widoku Wszystkie sejfy i będą dostępne tylko przez Konsolę Administracyjną." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Przypisz te elementy do kolekcji z", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": ", aby były widoczne.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Konsola Administracyjna" + }, + "errorAssigningTargetCollection": { + "message": "Wystąpił błąd podczas przypisywania kolekcji." + }, + "errorAssigningTargetFolder": { + "message": "Wystąpił błąd podczas przypisywania folderu." } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 6126679902..c6e62fbd4f 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Um gerenciador de senhas seguro e gratuito para todos os seus dispositivos.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Alterar Senha Mestra" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Frase Biométrica", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Pasta adicionada" }, - "changeMasterPass": { - "message": "Alterar Senha Mestra" - }, - "changeMasterPasswordConfirmation": { - "message": "Você pode alterar a sua senha mestra no cofre web em bitwarden.com. Você deseja visitar o site agora?" - }, "twoStepLoginConfirmation": { "message": "O login de duas etapas torna a sua conta mais segura ao exigir que digite um código de segurança de um aplicativo de autenticação quando for iniciar a sessão. O login de duas etapas pode ser ativado no cofre web bitwarden.com. Deseja visitar o site agora?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index a904867d5f..06ba8eed26 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Gestor de Palavras-passe", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Um gestor de palavras-passe seguro e gratuito para todos os seus dispositivos.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Alterar palavra-passe mestra" }, + "continueToWebApp": { + "message": "Continuar para a aplicação Web?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Pode alterar a sua palavra-passe mestra na aplicação Web Bitwarden." + }, "fingerprintPhrase": { "message": "Frase de impressão digital", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Pasta adicionada" }, - "changeMasterPass": { - "message": "Alterar palavra-passe mestra" - }, - "changeMasterPasswordConfirmation": { - "message": "Pode alterar o seu endereço de e-mail no cofre do site bitwarden.com. Deseja visitar o site agora?" - }, "twoStepLoginConfirmation": { "message": "A verificação de dois passos torna a sua conta mais segura, exigindo que verifique o seu início de sessão com outro dispositivo, como uma chave de segurança, aplicação de autenticação, SMS, chamada telefónica ou e-mail. A verificação de dois passos pode ser configurada em bitwarden.com. Pretende visitar o site agora?" }, @@ -3000,13 +3000,36 @@ "message": "Erro ao guardar as credenciais. Verifique a consola para obter detalhes.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Com sucesso" + }, "removePasskey": { "message": "Remover chave de acesso" }, "passkeyRemoved": { "message": "Chave de acesso removida" }, - "unassignedItemsBanner": { - "message": "Aviso: Os itens da organização não atribuídos já não são visíveis na vista Todos os cofres e só são acessíveis através da consola de administração. Atribua estes itens a uma coleção a partir da Consola de administração para os tornar visíveis." + "unassignedItemsBannerNotice": { + "message": "Aviso: Os itens da organização não atribuídos já não são visíveis na vista Todos os cofres e só são acessíveis através da Consola de administração." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Aviso: A 16 de maio de 2024, os itens da organização não atribuídos deixarão de estar visíveis na vista Todos os cofres e só estarão acessíveis através da consola de administração." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Atribua estes itens a uma coleção a partir da", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "para os tornar visíveis.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Consola de administração" + }, + "errorAssigningTargetCollection": { + "message": "Erro ao atribuir a coleção de destino." + }, + "errorAssigningTargetFolder": { + "message": "Erro ao atribuir a pasta de destino." } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 3e554f4d68..b3e0a2066f 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Manager de parole gratuit", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Un manager de parole sigur și gratuit pentru toate dispozitivele dvs.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Schimbare parolă principală" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fraza amprentă", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Dosar adăugat" }, - "changeMasterPass": { - "message": "Schimbare parolă principală" - }, - "changeMasterPasswordConfirmation": { - "message": "Puteți modifica parola principală în seiful web bitwarden.com. Doriți să vizitați saitul acum?" - }, "twoStepLoginConfirmation": { "message": "Autentificarea în două etape vă face contul mai sigur, prin solicitarea unei verificări de autentificare cu un alt dispozitiv, cum ar fi o cheie de securitate, o aplicație de autentificare, un SMS, un apel telefonic sau un e-mail. Autentificarea în două etape poate fi configurată în seiful web bitwarden.com. Doriți să vizitați site-ul web acum?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index aed3d58b11..e594dbdce2 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - бесплатный менеджер паролей", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Защищенный и бесплатный менеджер паролей для всех ваших устройств.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Изменить мастер-пароль" }, + "continueToWebApp": { + "message": "Перейти к веб-приложению?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Изменить мастер-пароль можно в веб-приложении Bitwarden." + }, "fingerprintPhrase": { "message": "Фраза отпечатка", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Папка добавлена" }, - "changeMasterPass": { - "message": "Изменить мастер-пароль" - }, - "changeMasterPasswordConfirmation": { - "message": "Вы можете изменить свой мастер-пароль на bitwarden.com. Перейти на сайт сейчас?" - }, "twoStepLoginConfirmation": { "message": "Двухэтапная аутентификация делает аккаунт более защищенным, поскольку требуется подтверждение входа при помощи другого устройства, например, ключа безопасности, приложения-аутентификатора, SMS, телефонного звонка или электронной почты. Двухэтапная аутентификация включается на bitwarden.com. Перейти на сайт сейчас?" }, @@ -3000,13 +3000,36 @@ "message": "Ошибка сохранения учетных данных. Проверьте консоль для получения подробной информации.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Успешно" + }, "removePasskey": { "message": "Удалить passkey" }, "passkeyRemoved": { "message": "Passkey удален" }, - "unassignedItemsBanner": { - "message": "Обратите внимание: неприсвоенные элементы организации больше не видны в представлении \"Все хранилища\" и доступны только через консоль администратора. Назначьте эти элементы коллекции в консоли администратора, чтобы сделать их видимыми." + "unassignedItemsBannerNotice": { + "message": "Уведомление: Неприсвоенные элементы организации больше не видны в представлении \"Все хранилища\" и доступны только через консоль администратора." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Уведомление: с 16 мая 2024 года не назначенные элементы организации больше не будут видны в представлении \"Все хранилища\" и будут доступны только через консоль администратора." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Назначьте эти элементы в коллекцию из", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "чтобы сделать их видимыми.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "консоли администратора" + }, + "errorAssigningTargetCollection": { + "message": "Ошибка при назначении целевой коллекции." + }, + "errorAssigningTargetFolder": { + "message": "Ошибка при назначении целевой папки." } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 3dc18a97fb..05e2dc3edd 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -3,11 +3,11 @@ "message": "බිට්වාඩන්" }, "extName": { - "message": "බිට්වාඩන් - නොමිලේ මුරපදය කළමනාකරු", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "ඔබගේ සියලු උපාංග සඳහා ආරක්ෂිත සහ නොමිලේ මුරපද කළමණාකරුවෙකු.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "ප්රධාන මුරපදය වෙනස්" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "ඇඟිලි සලකුණු වාක්ය ඛණ්ඩය", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "එකතු කරන ලද ෆෝල්ඩරය" }, - "changeMasterPass": { - "message": "ප්රධාන මුරපදය වෙනස්" - }, - "changeMasterPasswordConfirmation": { - "message": "bitwarden.com වෙබ් සුරක්ෂිතාගාරයේ ඔබේ ප්රධාන මුරපදය වෙනස් කළ හැකිය. ඔබට දැන් වෙබ් අඩවියට පිවිසීමට අවශ්යද?" - }, "twoStepLoginConfirmation": { "message": "ආරක්ෂක යතුරක්, සත්යාපන යෙදුම, කෙටි පණිවුඩ, දුරකථන ඇමතුමක් හෝ විද්යුත් තැපෑල වැනි වෙනත් උපාංගයක් සමඟ ඔබේ පිවිසුම සත්යාපනය කිරීමට ඔබට අවශ්ය වීමෙන් ද්වි-පියවර පිවිසුම ඔබගේ ගිණුම වඩාත් සුරක්ෂිත කරයි. බිට්වොන්.com වෙබ් සුරක්ෂිතාගාරයේ ද්වි-පියවර පිවිසුම සක්රීය කළ හැකිය. ඔබට දැන් වෙබ් අඩවියට පිවිසීමට අවශ්යද?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index bd4f9f9796..eab1d105eb 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Bezplatný správca hesiel", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "bitwarden je bezpečný a bezplatný správca hesiel pre všetky vaše zariadenia.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Zmeniť hlavné heslo" }, + "continueToWebApp": { + "message": "Pokračovať vo webovej aplikácii?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Hlavné heslo si môžete zmeniť vo webovej aplikácii Bitwarden." + }, "fingerprintPhrase": { "message": "Fráza odtlačku", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Pridaný priečinok" }, - "changeMasterPass": { - "message": "Zmeniť hlavné heslo" - }, - "changeMasterPasswordConfirmation": { - "message": "Teraz si môžete zmeniť svoje hlavné heslo vo webovom trezore bitwarden.com. Chcete navštíviť túto stránku teraz?" - }, "twoStepLoginConfirmation": { "message": "Dvojstupňové prihlasovanie robí váš účet bezpečnejším vďaka vyžadovaniu bezpečnostného kódu z overovacej aplikácie vždy, keď sa prihlásite. Dvojstupňové prihlasovanie môžete povoliť vo webovom trezore bitwarden.com. Chcete navštíviť túto stránku teraz?" }, @@ -1754,7 +1754,7 @@ } }, "send": { - "message": "Odoslať", + "message": "Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "searchSends": { @@ -3000,13 +3000,36 @@ "message": "Chyba pri ukladaní prihlasovacích údajov. Viac informácii nájdete v konzole.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Úspech" + }, "removePasskey": { "message": "Odstrániť prístupový kľúč" }, "passkeyRemoved": { "message": "Prístupový kľúč bol odstránený" }, - "unassignedItemsBanner": { - "message": "Upozornenie: Nepriradené položky organizácie už nie sú viditeľné v zobrazení Všetky Trezory a sú prístupné len cez administrátorskú konzolu. Aby boli viditeľné, priraďte tieto položky do kolekcie z konzoly administrátora." + "unassignedItemsBannerNotice": { + "message": "Upozornenie: Nepriradené položky organizácie už nie sú viditeľné v zobrazení Všetky trezory a sú prístupné iba cez Správcovskú konzolu." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Upozornenie: 16. mája 2024 nepriradené položky organizácie už nebudú viditeľné v zobrazení Všetky trezory a budú prístupné iba cez Správcovskú konzolu." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Priradiť tieto položky do zbierky zo", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": ", aby boli viditeľné.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Správcovská konzola" + }, + "errorAssigningTargetCollection": { + "message": "Chyba pri priraďovaní cieľovej kolekcie." + }, + "errorAssigningTargetFolder": { + "message": "Chyba pri priraďovaní cieľového priečinka." } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 3e2690be30..935678efc8 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Brezplačni upravitelj gesel", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Varen in brezplačen upravitelj gesel za vse vaše naprave.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Spremeni glavno geslo" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Identifikacijsko geslo", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Mapa dodana" }, - "changeMasterPass": { - "message": "Spremeni glavno geslo" - }, - "changeMasterPasswordConfirmation": { - "message": "Svoje glavno geslo lahko spremenite v Bitwardnovem spletnem trezorju. Želite zdaj obiskati Bitwardnovo spletno stran?" - }, "twoStepLoginConfirmation": { "message": "Avtentikacija v dveh korakih dodatno varuje vaš račun, saj zahteva, da vsakokratno prijavo potrdite z drugo napravo, kot je varnostni ključ, aplikacija za preverjanje pristnosti, SMS, telefonski klic ali e-pošta. Avtentikacijo v dveh korakih lahko omogočite v spletnem trezorju bitwarden.com. Ali želite spletno stran obiskati sedaj?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index fef1a4eb8a..5819546800 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - бесплатни менаџер лозинки", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Сигурни и бесплатни менаџер лозинки за све ваше уређаје.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Промени главну лозинку" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Сигурносна Фраза Сефа", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Фасцикла додата" }, - "changeMasterPass": { - "message": "Промени главну лозинку" - }, - "changeMasterPasswordConfirmation": { - "message": "Можете променити главну лозинку у Вашем сефу на bitwarden.com. Да ли желите да посетите веб страницу сада?" - }, "twoStepLoginConfirmation": { "message": "Пријава у два корака чини ваш налог сигурнијим захтевом да верификујете своје податке помоћу другог уређаја, као што су безбедносни кључ, апликација, СМС-а, телефонски позив или имејл. Пријављивање у два корака може се омогућити на веб сефу. Да ли желите да посетите веб страницу сада?" }, @@ -3000,13 +3000,36 @@ "message": "Грешка при чувању акредитива. Проверите конзолу за детаље.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Уклонити приступачни кључ" }, "passkeyRemoved": { "message": "Приступачни кључ је уклоњен" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 0a27862c74..d96e86b8d3 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Gratis lösenordshanterare", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bitwarden är en säker och gratis lösenordshanterare för alla dina enheter.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Ändra huvudlösenord" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingeravtrycksfras", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Lade till mapp" }, - "changeMasterPass": { - "message": "Ändra huvudlösenord" - }, - "changeMasterPasswordConfirmation": { - "message": "Du kan ändra ditt huvudlösenord på bitwardens webbvalv. Vill du besöka webbplatsen nu?" - }, "twoStepLoginConfirmation": { "message": "Tvåstegsverifiering gör ditt konto säkrare genom att kräva att du verifierar din inloggning med en annan enhet, t.ex. en säkerhetsnyckel, autentiseringsapp, SMS, telefonsamtal eller e-post. Tvåstegsverifiering kan aktiveras i Bitwardens webbvalv. Vill du besöka webbplatsen nu?" }, @@ -2931,7 +2931,7 @@ "message": "active" }, "locked": { - "message": "locked" + "message": "låst" }, "unlocked": { "message": "unlocked" @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index dc91c1e6a9..5d1b024c60 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change master password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Folder added" }, - "changeMasterPass": { - "message": "Change master password" - }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index ecc346513d..794d0e6c22 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -3,11 +3,11 @@ "message": "bitwarden" }, "extName": { - "message": "bitwarden - Free Password Manager", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "bitwarden is a secure and free password manager for all of your devices.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Change Master Password" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Fingerprint Phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "เพิ่มโฟลเดอร์แล้ว" }, - "changeMasterPass": { - "message": "Change Master Password" - }, - "changeMasterPasswordConfirmation": { - "message": "คุณสามารถเปลี่ยนรหัสผ่านหลักได้ที่เว็บตู้เซฟ bitwarden.com คุณต้องการเปิดเว็บไซต์เลยหรือไม่?" - }, "twoStepLoginConfirmation": { "message": "Two-step login makes your account more secure by requiring you to enter a security code from an authenticator app whenever you log in. Two-step login can be enabled on the bitwarden.com web vault. Do you want to visit the website now?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index f6c00a58b8..8408253b86 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Ücretsiz Parola Yöneticisi", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Tüm cihazlarınız için güvenli ve ücretsiz bir parola yöneticisi.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Ana parolayı değiştir" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "Parmak izi ifadesi", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Klasör eklendi" }, - "changeMasterPass": { - "message": "Ana parolayı değiştir" - }, - "changeMasterPasswordConfirmation": { - "message": "Ana parolanızı bitwarden.com web kasası üzerinden değiştirebilirsiniz. Siteye gitmek ister misiniz?" - }, "twoStepLoginConfirmation": { "message": "İki aşamalı giriş, hesabınıza girererken işlemi bir güvenlik anahtarı, şifrematik uygulaması, SMS, telefon araması veya e-posta gibi ek bir yöntemle doğrulamanızı isteyerek hesabınızın güvenliğini artırır. İki aşamalı giriş özelliğini bitwarden.com web kasası üzerinden etkinleştirebilirsiniz. Şimdi siteye gitmek ister misiniz?" }, @@ -3000,13 +3000,36 @@ "message": "Kimlik bilgileri kaydedilirken hata oluştu. Ayrıntılar için konsolu kontrol edin.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 873cfad2c6..b590b92041 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bitwarden - це захищений і безкоштовний менеджер паролів для всіх ваших пристроїв.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Змінити головний пароль" }, + "continueToWebApp": { + "message": "Продовжити у вебпрограмі?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Ви можете змінити головний пароль у вебпрограмі Bitwarden." + }, "fingerprintPhrase": { "message": "Фраза відбитка", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "Теку додано" }, - "changeMasterPass": { - "message": "Змінити головний пароль" - }, - "changeMasterPasswordConfirmation": { - "message": "Ви можете змінити головний пароль в сховищі на bitwarden.com. Хочете перейти на вебсайт зараз?" - }, "twoStepLoginConfirmation": { "message": "Двоетапна перевірка дає змогу надійніше захистити ваш обліковий запис, вимагаючи підтвердження входу з використанням іншого пристрою, наприклад, за допомогою ключа безпеки, програми автентифікації, SMS, телефонного виклику, або е-пошти. Ви можете налаштувати двоетапну перевірку в сховищі на bitwarden.com. Хочете перейти на вебсайт зараз?" }, @@ -3000,13 +3000,36 @@ "message": "Помилка збереження облікових даних. Перегляньте подробиці в консолі.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Успішно" + }, "removePasskey": { "message": "Вилучити ключ доступу" }, "passkeyRemoved": { "message": "Ключ доступу вилучено" }, - "unassignedItemsBanner": { - "message": "Увага: непризначені елементи організації більше не видимі у поданні \"Усі сховища\" і доступні лише в консолі адміністратора. Щоб зробити їх видимими, призначте ці елементи збірці в консолі адміністратора." + "unassignedItemsBannerNotice": { + "message": "Примітка: непризначені елементи організації більше не видимі у поданні \"Усі сховища\" і доступні лише в консолі адміністратора." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Примітка: 16 травня 2024 року непризначені елементи організації більше не будуть видимі у поданні \"Усі сховища\" і будуть доступні лише через консоль адміністратора." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Призначте ці елементи збірці в", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "щоб зробити їх видимими.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "консолі адміністратора," + }, + "errorAssigningTargetCollection": { + "message": "Помилка призначення цільової збірки." + }, + "errorAssigningTargetFolder": { + "message": "Помилка призначення цільової теки." } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index d71fa3322f..ab1d0d515b 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Quản lý mật khẩu miễn phí", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Trình quản lý mật khẩu an toàn và miễn phí cho mọi thiết bị của bạn.", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "Thay đổi mật khẩu chính" }, + "continueToWebApp": { + "message": "Tiếp tục tới ứng dụng web?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Bạn có thể thay đổi mật khẩu chính của mình trên Bitwarden bản web." + }, "fingerprintPhrase": { "message": "Fingerprint Phrase", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -415,7 +421,7 @@ "message": "Khóa ngay" }, "lockAll": { - "message": "Lock all" + "message": "Khóa tất cả" }, "immediately": { "message": "Ngay lập tức" @@ -494,10 +500,10 @@ "message": "Tài khoản mới của bạn đã được tạo! Bạn có thể đăng nhập từ bây giờ." }, "youSuccessfullyLoggedIn": { - "message": "You successfully logged in" + "message": "Bạn đã đăng nhập thành công" }, "youMayCloseThisWindow": { - "message": "You may close this window" + "message": "Bạn có thể đóng cửa sổ này" }, "masterPassSent": { "message": "Chúng tôi đã gửi cho bạn email có chứa gợi ý mật khẩu chính của bạn." @@ -522,16 +528,16 @@ "message": "Không thể tự động điền mục đã chọn trên trang này. Hãy thực hiện sao chép và dán thông tin một cách thủ công." }, "totpCaptureError": { - "message": "Unable to scan QR code from the current webpage" + "message": "Không thể quét mã QR từ trang web hiện tại" }, "totpCaptureSuccess": { - "message": "Authenticator key added" + "message": "Đã thêm khóa xác thực" }, "totpCapture": { - "message": "Scan authenticator QR code from current webpage" + "message": "Quét mã QR xác thực từ trang web hiện tại" }, "copyTOTP": { - "message": "Copy Authenticator key (TOTP)" + "message": "Sao chép khóa Authenticator (TOTP)" }, "loggedOut": { "message": "Đã đăng xuất" @@ -557,12 +563,6 @@ "addedFolder": { "message": "Đã thêm thư mục" }, - "changeMasterPass": { - "message": "Thay đổi mật khẩu chính" - }, - "changeMasterPasswordConfirmation": { - "message": "Bạn có thể thay đổi mật khẩu chính trong trang web kho lưu trữ của Bitwarden. Bạn có muốn truy cập trang web ngay bây giờ không?" - }, "twoStepLoginConfirmation": { "message": "Xác thực hai lớp giúp cho tài khoản của bạn an toàn hơn bằng cách yêu cầu bạn xác minh thông tin đăng nhập của bạn bằng một thiết bị khác như khóa bảo mật, ứng dụng xác thực, SMS, cuộc gọi điện thoại hoặc email. Bạn có thể bật xác thực hai lớp trong kho bitwarden nền web. Bạn có muốn ghé thăm trang web bây giờ?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Lưu ý: Các mục tổ chức chưa được chỉ định sẽ không còn hiển thị trong chế độ xem Tất cả Vault và chỉ có thể truy cập được qua Bảng điều khiển dành cho quản trị viên." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Lưu ý: Vào ngày 16 tháng 5 năm 2024, các mục tổ chức chưa được chỉ định sẽ không còn hiển thị trong chế độ xem Tất cả Vault và sẽ chỉ có thể truy cập được qua Bảng điều khiển dành cho quản trị viên." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Gán các mục này vào một bộ sưu tập từ", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "để làm cho chúng hiển thị.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Bảng điều khiển dành cho quản trị viên" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 85fb5f6d2e..a2f856c31b 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - 免费密码管理器", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "安全且免费的跨平台密码管理器。", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "更改主密码" }, + "continueToWebApp": { + "message": "前往网页 App 吗?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "指纹短语", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "文件夹已添加" }, - "changeMasterPass": { - "message": "修改主密码" - }, - "changeMasterPasswordConfirmation": { - "message": "您可以在 bitwarden.com 网页版密码库修改主密码。您现在要访问这个网站吗?" - }, "twoStepLoginConfirmation": { "message": "两步登录要求您从其他设备(例如安全钥匙、验证器 App、短信、电话或者电子邮件)来验证您的登录,这能使您的账户更加安全。两步登录需要在 bitwarden.com 网页版密码库中设置。现在访问此网站吗?" }, @@ -3000,13 +3000,36 @@ "message": "保存凭据时出错。检查控制台以获取详细信息。", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "成功" + }, "removePasskey": { "message": "移除通行密钥" }, "passkeyRemoved": { "message": "通行密钥已移除" }, - "unassignedItemsBanner": { - "message": "注意:未分配的组织项目在「所有密码库」视图中不再可见,只能通过管理控制台访问。通过管理控制台将这些项目分配给集合以使其可见。" + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "管理控制台" + }, + "errorAssigningTargetCollection": { + "message": "分配目标集合时出错。" + }, + "errorAssigningTargetFolder": { + "message": "分配目标文件夹时出错。" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 66d9f7ce62..1ecfdfc50e 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -3,11 +3,11 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - 免費密碼管理工具", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Bitwarden 是一款安全、免費、跨平台的密碼管理工具。", + "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -172,6 +172,12 @@ "changeMasterPassword": { "message": "變更主密碼" }, + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." + }, "fingerprintPhrase": { "message": "指紋短語", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." @@ -557,12 +563,6 @@ "addedFolder": { "message": "資料夾已新增" }, - "changeMasterPass": { - "message": "變更主密碼" - }, - "changeMasterPasswordConfirmation": { - "message": "您可以在 bitwarden.com 網頁版密碼庫變更主密碼。現在要前往嗎?" - }, "twoStepLoginConfirmation": { "message": "兩步驟登入需要您從其他裝置(例如安全鑰匙、驗證器程式、SMS、手機或電子郵件)來驗證您的登入,這使您的帳戶更加安全。兩步驟登入可以在 bitwarden.com 網頁版密碼庫啟用。現在要前往嗎?" }, @@ -3000,13 +3000,36 @@ "message": "Error saving credentials. Check console for details.", "description": "Notification message for when saving credentials has failed." }, + "success": { + "message": "Success" + }, "removePasskey": { "message": "Remove passkey" }, "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." + }, + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/browser/src/auth/background/service-factories/auth-request-service.factory.ts b/apps/browser/src/auth/background/service-factories/auth-request-service.factory.ts index 295fedbadd..c18fd1a112 100644 --- a/apps/browser/src/auth/background/service-factories/auth-request-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/auth-request-service.factory.ts @@ -17,6 +17,10 @@ import { FactoryOptions, factory, } from "../../../platform/background/service-factories/factory-options"; +import { + stateProviderFactory, + StateProviderInitOptions, +} from "../../../platform/background/service-factories/state-provider.factory"; import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory"; import { @@ -31,7 +35,8 @@ export type AuthRequestServiceInitOptions = AuthRequestServiceFactoryOptions & AccountServiceInitOptions & MasterPasswordServiceInitOptions & CryptoServiceInitOptions & - ApiServiceInitOptions; + ApiServiceInitOptions & + StateProviderInitOptions; export function authRequestServiceFactory( cache: { authRequestService?: AuthRequestServiceAbstraction } & CachedServices, @@ -48,6 +53,7 @@ export function authRequestServiceFactory( await internalMasterPasswordServiceFactory(cache, opts), await cryptoServiceFactory(cache, opts), await apiServiceFactory(cache, opts), + await stateProviderFactory(cache, opts), ), ); } diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index 93750ece07..45f095aee9 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -720,7 +720,7 @@ describe("NotificationBackground", () => { ); tabSendMessageSpy = jest.spyOn(BrowserApi, "tabSendMessage").mockImplementation(); editItemSpy = jest.spyOn(notificationBackground as any, "editItem"); - setAddEditCipherInfoSpy = jest.spyOn(stateService, "setAddEditCipherInfo"); + setAddEditCipherInfoSpy = jest.spyOn(cipherService, "setAddEditCipherInfo"); openAddEditVaultItemPopoutSpy = jest.spyOn( notificationBackground as any, "openAddEditVaultItemPopout", diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index 74e6147505..9b65e4db0b 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -600,14 +600,14 @@ export default class NotificationBackground { } /** - * Sets the add/edit cipher info in the state service + * Sets the add/edit cipher info in the cipher service * and opens the add/edit vault item popout. * * @param cipherView - The cipher to edit * @param senderTab - The tab that the message was sent from */ private async editItem(cipherView: CipherView, senderTab: chrome.tabs.Tab) { - await this.stateService.setAddEditCipherInfo({ + await this.cipherService.setAddEditCipherInfo({ cipher: cipherView, collectionIds: cipherView.collectionIds, }); diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index 2599c1825e..e65397a62b 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -592,7 +592,7 @@ describe("OverlayBackground", () => { beforeEach(() => { sender = mock({ tab: { id: 1 } }); jest - .spyOn(overlayBackground["stateService"], "setAddEditCipherInfo") + .spyOn(overlayBackground["cipherService"], "setAddEditCipherInfo") .mockImplementation(); jest.spyOn(overlayBackground as any, "openAddEditVaultItemPopout").mockImplementation(); }); @@ -600,7 +600,7 @@ describe("OverlayBackground", () => { it("will not open the add edit popout window if the message does not have a login cipher provided", () => { sendExtensionRuntimeMessage({ command: "autofillOverlayAddNewVaultItem" }, sender); - expect(overlayBackground["stateService"].setAddEditCipherInfo).not.toHaveBeenCalled(); + expect(overlayBackground["cipherService"].setAddEditCipherInfo).not.toHaveBeenCalled(); expect(overlayBackground["openAddEditVaultItemPopout"]).not.toHaveBeenCalled(); }); @@ -621,7 +621,7 @@ describe("OverlayBackground", () => { ); await flushPromises(); - expect(overlayBackground["stateService"].setAddEditCipherInfo).toHaveBeenCalled(); + expect(overlayBackground["cipherService"].setAddEditCipherInfo).toHaveBeenCalled(); expect(BrowserApi.sendMessage).toHaveBeenCalledWith( "inlineAutofillMenuRefreshAddEditCipher", ); diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 50fb80ef1b..551263525e 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -636,7 +636,7 @@ class OverlayBackground implements OverlayBackgroundInterface { cipherView.type = CipherType.Login; cipherView.login = loginView; - await this.stateService.setAddEditCipherInfo({ + await this.cipherService.setAddEditCipherInfo({ cipher: cipherView, collectionIds: cipherView.collectionIds, }); diff --git a/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts b/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts index c948f7aa94..bee5da18b5 100644 --- a/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts +++ b/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts @@ -1,3 +1,7 @@ +import { + accountServiceFactory, + AccountServiceInitOptions, +} from "../../../auth/background/service-factories/account-service.factory"; import { UserVerificationServiceInitOptions, userVerificationServiceFactory, @@ -7,6 +11,10 @@ import { eventCollectionServiceFactory, } from "../../../background/service-factories/event-collection-service.factory"; import { billingAccountProfileStateServiceFactory } from "../../../platform/background/service-factories/billing-account-profile-state-service.factory"; +import { + browserScriptInjectorServiceFactory, + BrowserScriptInjectorServiceInitOptions, +} from "../../../platform/background/service-factories/browser-script-injector-service.factory"; import { CachedServices, factory, @@ -45,7 +53,9 @@ export type AutoFillServiceInitOptions = AutoFillServiceOptions & EventCollectionServiceInitOptions & LogServiceInitOptions & UserVerificationServiceInitOptions & - DomainSettingsServiceInitOptions; + DomainSettingsServiceInitOptions & + BrowserScriptInjectorServiceInitOptions & + AccountServiceInitOptions; export function autofillServiceFactory( cache: { autofillService?: AbstractAutoFillService } & CachedServices, @@ -65,6 +75,8 @@ export function autofillServiceFactory( await domainSettingsServiceFactory(cache, opts), await userVerificationServiceFactory(cache, opts), await billingAccountProfileStateServiceFactory(cache, opts), + await browserScriptInjectorServiceFactory(cache, opts), + await accountServiceFactory(cache, opts), ), ); } diff --git a/apps/browser/src/autofill/content/autofill-init.spec.ts b/apps/browser/src/autofill/content/autofill-init.spec.ts index b299ddccbf..8f1a8bf992 100644 --- a/apps/browser/src/autofill/content/autofill-init.spec.ts +++ b/apps/browser/src/autofill/content/autofill-init.spec.ts @@ -560,6 +560,17 @@ describe("AutofillInit", () => { }); describe("destroy", () => { + it("clears the timeout used to collect page details on load", () => { + jest.spyOn(window, "clearTimeout"); + + autofillInit.init(); + autofillInit.destroy(); + + expect(window.clearTimeout).toHaveBeenCalledWith( + autofillInit["collectPageDetailsOnLoadTimeout"], + ); + }); + it("removes the extension message listeners", () => { autofillInit.destroy(); diff --git a/apps/browser/src/autofill/content/autofill-init.ts b/apps/browser/src/autofill/content/autofill-init.ts index 2de35dee20..e78a1fb5ee 100644 --- a/apps/browser/src/autofill/content/autofill-init.ts +++ b/apps/browser/src/autofill/content/autofill-init.ts @@ -16,6 +16,7 @@ class AutofillInit implements AutofillInitInterface { private readonly domElementVisibilityService: DomElementVisibilityService; private readonly collectAutofillContentService: CollectAutofillContentService; private readonly insertAutofillContentService: InsertAutofillContentService; + private collectPageDetailsOnLoadTimeout: number | NodeJS.Timeout | undefined; private readonly extensionMessageHandlers: AutofillExtensionMessageHandlers = { collectPageDetails: ({ message }) => this.collectPageDetails(message), collectPageDetailsImmediately: ({ message }) => this.collectPageDetails(message, true), @@ -66,17 +67,19 @@ class AutofillInit implements AutofillInitInterface { * to act on the page. */ private collectPageDetailsOnLoad() { - const sendCollectDetailsMessage = () => - setTimeout( + const sendCollectDetailsMessage = () => { + this.clearCollectPageDetailsOnLoadTimeout(); + this.collectPageDetailsOnLoadTimeout = setTimeout( () => sendExtensionMessage("bgCollectPageDetails", { sender: "autofillInit" }), 250, ); + }; - if (document.readyState === "complete") { + if (globalThis.document.readyState === "complete") { sendCollectDetailsMessage(); } - window.addEventListener("load", sendCollectDetailsMessage); + globalThis.addEventListener("load", sendCollectDetailsMessage); } /** @@ -247,6 +250,15 @@ class AutofillInit implements AutofillInitInterface { this.autofillOverlayContentService.autofillOverlayVisibility = data?.autofillOverlayVisibility; } + /** + * Clears the send collect details message timeout. + */ + private clearCollectPageDetailsOnLoadTimeout() { + if (this.collectPageDetailsOnLoadTimeout) { + clearTimeout(this.collectPageDetailsOnLoadTimeout); + } + } + /** * Sets up the extension message listeners for the content script. */ @@ -288,6 +300,7 @@ class AutofillInit implements AutofillInitInterface { * listeners, timeouts, and object instances to prevent memory leaks. */ destroy() { + this.clearCollectPageDetailsOnLoadTimeout(); chrome.runtime.onMessage.removeListener(this.handleExtensionMessage); this.collectAutofillContentService.destroy(); this.autofillOverlayContentService?.destroy(); diff --git a/apps/browser/src/autofill/services/autofill.service.spec.ts b/apps/browser/src/autofill/services/autofill.service.spec.ts index 4db64f417d..d1fbf79bfa 100644 --- a/apps/browser/src/autofill/services/autofill.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill.service.spec.ts @@ -32,6 +32,7 @@ import { CipherService } from "@bitwarden/common/vault/services/cipher.service"; import { TotpService } from "@bitwarden/common/vault/services/totp.service"; import { BrowserApi } from "../../platform/browser/browser-api"; +import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service"; import { AutofillPort } from "../enums/autofill-port.enums"; import AutofillField from "../models/autofill-field"; import AutofillPageDetails from "../models/autofill-page-details"; @@ -67,6 +68,7 @@ describe("AutofillService", () => { const accountService: FakeAccountService = mockAccountServiceWith(mockUserId); const fakeStateProvider: FakeStateProvider = new FakeStateProvider(accountService); let domainSettingsService: DomainSettingsService; + let scriptInjectorService: BrowserScriptInjectorService; const totpService = mock(); const eventCollectionService = mock(); const logService = mock(); @@ -74,6 +76,7 @@ describe("AutofillService", () => { const billingAccountProfileStateService = mock(); beforeEach(() => { + scriptInjectorService = new BrowserScriptInjectorService(); autofillService = new AutofillService( cipherService, autofillSettingsService, @@ -83,6 +86,8 @@ describe("AutofillService", () => { domainSettingsService, userVerificationService, billingAccountProfileStateService, + scriptInjectorService, + accountService, ); domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider); @@ -250,6 +255,7 @@ describe("AutofillService", () => { expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith(tabMock.id, { file: "content/content-message-handler.js", + frameId: 0, ...defaultExecuteScriptOptions, }); }); diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 8b33d03419..8f85d65692 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -1,7 +1,9 @@ import { firstValueFrom } from "rxjs"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types"; @@ -20,6 +22,7 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FieldView } from "@bitwarden/common/vault/models/view/field.view"; import { BrowserApi } from "../../platform/browser/browser-api"; +import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service"; import { openVaultItemPasswordRepromptPopout } from "../../vault/popup/utils/vault-popout-window"; import { AutofillPort } from "../enums/autofill-port.enums"; import AutofillField from "../models/autofill-field"; @@ -55,6 +58,8 @@ export default class AutofillService implements AutofillServiceInterface { private domainSettingsService: DomainSettingsService, private userVerificationService: UserVerificationService, private billingAccountProfileStateService: BillingAccountProfileStateService, + private scriptInjectorService: ScriptInjectorService, + private accountService: AccountService, ) {} /** @@ -102,30 +107,46 @@ export default class AutofillService implements AutofillServiceInterface { frameId = 0, triggeringOnPageLoad = true, ): Promise { - const mainAutofillScript = (await this.getOverlayVisibility()) + // Autofill settings loaded from state can await the active account state indefinitely if + // not guarded by an active account check (e.g. the user is logged in) + const activeAccount = await firstValueFrom(this.accountService.activeAccount$); + + // These settings are not available until the user logs in + let overlayVisibility: InlineMenuVisibilitySetting = AutofillOverlayVisibility.Off; + let autoFillOnPageLoadIsEnabled = false; + + if (activeAccount) { + overlayVisibility = await this.getOverlayVisibility(); + } + const mainAutofillScript = overlayVisibility ? "bootstrap-autofill-overlay.js" : "bootstrap-autofill.js"; const injectedScripts = [mainAutofillScript]; - const autoFillOnPageLoadIsEnabled = await this.getAutofillOnPageLoad(); + if (activeAccount) { + autoFillOnPageLoadIsEnabled = await this.getAutofillOnPageLoad(); + } if (triggeringOnPageLoad && autoFillOnPageLoadIsEnabled) { injectedScripts.push("autofiller.js"); } else { - await BrowserApi.executeScriptInTab(tab.id, { - file: "content/content-message-handler.js", - runAt: "document_start", + await this.scriptInjectorService.inject({ + tabId: tab.id, + injectDetails: { file: "content/content-message-handler.js", runAt: "document_start" }, }); } injectedScripts.push("notificationBar.js", "contextMenuHandler.js"); for (const injectedScript of injectedScripts) { - await BrowserApi.executeScriptInTab(tab.id, { - file: `content/${injectedScript}`, - frameId, - runAt: "document_start", + await this.scriptInjectorService.inject({ + tabId: tab.id, + injectDetails: { + file: `content/${injectedScript}`, + runAt: "document_start", + frame: frameId, + }, }); } } diff --git a/apps/browser/src/autofill/spec/autofill-mocks.ts b/apps/browser/src/autofill/spec/autofill-mocks.ts index 708489c57e..9c957f6b1b 100644 --- a/apps/browser/src/autofill/spec/autofill-mocks.ts +++ b/apps/browser/src/autofill/spec/autofill-mocks.ts @@ -267,6 +267,7 @@ function createPortSpyMock(name: string) { disconnect: jest.fn(), sender: { tab: createChromeTabMock(), + url: "https://jest-testing-website.com", }, }); } diff --git a/apps/browser/src/autofill/spec/fido2-testing-utils.ts b/apps/browser/src/autofill/spec/fido2-testing-utils.ts new file mode 100644 index 0000000000..c9b39c16cc --- /dev/null +++ b/apps/browser/src/autofill/spec/fido2-testing-utils.ts @@ -0,0 +1,74 @@ +import { mock } from "jest-mock-extended"; + +import { + AssertCredentialResult, + CreateCredentialResult, +} from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction"; + +export function createCredentialCreationOptionsMock( + customFields: Partial = {}, +): CredentialCreationOptions { + return mock({ + publicKey: mock({ + authenticatorSelection: { authenticatorAttachment: "platform" }, + excludeCredentials: [{ id: new ArrayBuffer(32), type: "public-key" }], + pubKeyCredParams: [{ alg: -7, type: "public-key" }], + user: { id: new ArrayBuffer(32), name: "test", displayName: "test" }, + }), + ...customFields, + }); +} + +export function createCreateCredentialResultMock( + customFields: Partial = {}, +): CreateCredentialResult { + return mock({ + credentialId: "mock", + clientDataJSON: "mock", + attestationObject: "mock", + authData: "mock", + publicKey: "mock", + publicKeyAlgorithm: -7, + transports: ["internal"], + ...customFields, + }); +} + +export function createCredentialRequestOptionsMock( + customFields: Partial = {}, +): CredentialRequestOptions { + return mock({ + mediation: "optional", + publicKey: mock({ + allowCredentials: [{ id: new ArrayBuffer(32), type: "public-key" }], + }), + ...customFields, + }); +} + +export function createAssertCredentialResultMock( + customFields: Partial = {}, +): AssertCredentialResult { + return mock({ + credentialId: "mock", + clientDataJSON: "mock", + authenticatorData: "mock", + signature: "mock", + userHandle: "mock", + ...customFields, + }); +} + +export function setupMockedWebAuthnSupport() { + (globalThis as any).PublicKeyCredential = class PolyfillPublicKeyCredential { + static isUserVerifyingPlatformAuthenticatorAvailable = () => Promise.resolve(true); + }; + (globalThis as any).AuthenticatorAttestationResponse = + class PolyfillAuthenticatorAttestationResponse {}; + (globalThis as any).AuthenticatorAssertionResponse = + class PolyfillAuthenticatorAssertionResponse {}; + (globalThis as any).navigator.credentials = { + create: jest.fn().mockResolvedValue({}), + get: jest.fn().mockResolvedValue({}), + }; +} diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 4aecf8f585..0a9ad44962 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1,4 +1,4 @@ -import { firstValueFrom } from "rxjs"; +import { Subject, firstValueFrom, merge } from "rxjs"; import { PinCryptoServiceAbstraction, @@ -29,6 +29,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/services/policy/p import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; @@ -82,7 +83,6 @@ import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/co import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { AbstractMemoryStorageService, @@ -95,6 +95,9 @@ import { DefaultBiometricStateService, } from "@bitwarden/common/platform/biometrics/biometric-state.service"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; +import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; +// eslint-disable-next-line no-restricted-imports -- Used for dependency creation +import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { AppIdService } from "@bitwarden/common/platform/services/app-id.service"; import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; @@ -108,8 +111,8 @@ import { KeyGenerationService } from "@bitwarden/common/platform/services/key-ge import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; -import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { SystemService } from "@bitwarden/common/platform/services/system.service"; +import { UserKeyInitService } from "@bitwarden/common/platform/services/user-key-init.service"; import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; import { ActiveUserStateProvider, @@ -134,7 +137,6 @@ import { EventUploadService } from "@bitwarden/common/services/event/event-uploa import { NotificationsService } from "@bitwarden/common/services/notifications.service"; import { SearchService } from "@bitwarden/common/services/search.service"; import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service"; -import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/avatar.service"; import { PasswordGenerationService, PasswordGenerationServiceAbstraction, @@ -207,13 +209,15 @@ import { Account } from "../models/account"; import { BrowserApi } from "../platform/browser/browser-api"; import { flagEnabled } from "../platform/flags"; import { UpdateBadge } from "../platform/listeners/update-badge"; +/* eslint-disable no-restricted-imports */ +import { ChromeMessageSender } from "../platform/messaging/chrome-message.sender"; +/* eslint-enable no-restricted-imports */ import { BrowserStateService as StateServiceAbstraction } from "../platform/services/abstractions/browser-state.service"; import { BrowserCryptoService } from "../platform/services/browser-crypto.service"; import { BrowserEnvironmentService } from "../platform/services/browser-environment.service"; import BrowserLocalStorageService from "../platform/services/browser-local-storage.service"; import BrowserMemoryStorageService from "../platform/services/browser-memory-storage.service"; -import BrowserMessagingPrivateModeBackgroundService from "../platform/services/browser-messaging-private-mode-background.service"; -import BrowserMessagingService from "../platform/services/browser-messaging.service"; +import { BrowserScriptInjectorService } from "../platform/services/browser-script-injector.service"; import { DefaultBrowserStateService } from "../platform/services/default-browser-state.service"; import I18nService from "../platform/services/i18n.service"; import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service"; @@ -221,11 +225,14 @@ import { BackgroundPlatformUtilsService } from "../platform/services/platform-ut import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service"; import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider"; import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service"; +import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider"; +import { ForegroundMemoryStorageService } from "../platform/storage/foreground-memory-storage.service"; +import { fromChromeRuntimeMessaging } from "../platform/utils/from-chrome-runtime-messaging"; import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service"; import FilelessImporterBackground from "../tools/background/fileless-importer.background"; +import { Fido2Background as Fido2BackgroundAbstraction } from "../vault/fido2/background/abstractions/fido2.background"; +import { Fido2Background } from "../vault/fido2/background/fido2.background"; import { BrowserFido2UserInterfaceService } from "../vault/fido2/browser-fido2-user-interface.service"; -import { Fido2Service as Fido2ServiceAbstraction } from "../vault/services/abstractions/fido2.service"; -import Fido2Service from "../vault/services/fido2.service"; import { VaultFilterService } from "../vault/services/vault-filter.service"; import CommandsBackground from "./commands.background"; @@ -234,11 +241,13 @@ import { NativeMessagingBackground } from "./nativeMessaging.background"; import RuntimeBackground from "./runtime.background"; export default class MainBackground { - messagingService: MessagingServiceAbstraction; + messagingService: MessageSender; storageService: BrowserLocalStorageService; secureStorageService: AbstractStorageService; memoryStorageService: AbstractMemoryStorageService; memoryStorageForStateProviders: AbstractMemoryStorageService & ObservableStorageService; + largeObjectMemoryStorageForStateProviders: AbstractMemoryStorageService & + ObservableStorageService; i18nService: I18nServiceAbstraction; platformUtilsService: PlatformUtilsServiceAbstraction; logService: LogServiceAbstraction; @@ -316,7 +325,7 @@ export default class MainBackground { activeUserStateProvider: ActiveUserStateProvider; derivedStateProvider: DerivedStateProvider; stateProvider: StateProvider; - fido2Service: Fido2ServiceAbstraction; + fido2Background: Fido2BackgroundAbstraction; individualVaultExportService: IndividualVaultExportServiceAbstraction; organizationVaultExportService: OrganizationVaultExportServiceAbstraction; vaultSettingsService: VaultSettingsServiceAbstraction; @@ -324,6 +333,10 @@ export default class MainBackground { stateEventRunnerService: StateEventRunnerService; ssoLoginService: SsoLoginServiceAbstraction; billingAccountProfileStateService: BillingAccountProfileStateService; + // eslint-disable-next-line rxjs/no-exposed-subjects -- Needed to give access to services module + intraprocessMessagingSubject: Subject>; + userKeyInitService: UserKeyInitService; + scriptInjectorService: BrowserScriptInjectorService; onUpdatedRan: boolean; onReplacedRan: boolean; @@ -342,11 +355,11 @@ export default class MainBackground { private syncTimeout: any; private isSafari: boolean; private nativeMessagingBackground: NativeMessagingBackground; - popupOnlyContext: boolean; - - constructor(public isPrivateMode: boolean = false) { - this.popupOnlyContext = isPrivateMode || BrowserApi.isManifestVersion(3); + constructor( + public isPrivateMode: boolean = false, + public popupOnlyContext: boolean = false, + ) { // Services const lockedCallback = async (userId?: string) => { if (this.notificationsService != null) { @@ -365,22 +378,45 @@ export default class MainBackground { const logoutCallback = async (expired: boolean, userId?: UserId) => await this.logout(expired, userId); - this.messagingService = - this.isPrivateMode && BrowserApi.isManifestVersion(2) - ? new BrowserMessagingPrivateModeBackgroundService() - : new BrowserMessagingService(); this.logService = new ConsoleLogService(false); this.cryptoFunctionService = new WebCryptoFunctionService(self); this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService); this.storageService = new BrowserLocalStorageService(); + this.intraprocessMessagingSubject = new Subject>(); + + this.messagingService = MessageSender.combine( + new SubjectMessageSender(this.intraprocessMessagingSubject), + new ChromeMessageSender(this.logService), + ); + + const messageListener = new MessageListener( + merge( + this.intraprocessMessagingSubject.asObservable(), // For messages from the same context + fromChromeRuntimeMessaging(), // For messages from other contexts + ), + ); + + this.platformUtilsService = new BackgroundPlatformUtilsService( + this.messagingService, + (clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs), + async () => this.biometricUnlock(), + self, + ); + const mv3MemoryStorageCreator = (partitionName: string) => { + if (this.popupOnlyContext) { + return new ForegroundMemoryStorageService(partitionName); + } + // TODO: Consider using multithreaded encrypt service in popup only context return new LocalBackedSessionStorageService( + this.logService, new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false), this.keyGenerationService, new BrowserLocalStorageService(), new BrowserMemoryStorageService(), + this.platformUtilsService, partitionName, ); }; @@ -390,12 +426,16 @@ export default class MainBackground { ? mv3MemoryStorageCreator("stateService") : new MemoryStorageService(); this.memoryStorageForStateProviders = BrowserApi.isManifestVersion(3) - ? mv3MemoryStorageCreator("stateProviders") - : new BackgroundMemoryStorageService(); + ? new BrowserMemoryStorageService() // mv3 stores to storage.session + : new BackgroundMemoryStorageService(); // mv2 stores to memory + this.largeObjectMemoryStorageForStateProviders = BrowserApi.isManifestVersion(3) + ? mv3MemoryStorageCreator("stateProviders") // mv3 stores to local-backed session storage + : this.memoryStorageForStateProviders; // mv2 stores to the same location - const storageServiceProvider = new StorageServiceProvider( + const storageServiceProvider = new BrowserStorageServiceProvider( this.storageService, this.memoryStorageForStateProviders, + this.largeObjectMemoryStorageForStateProviders, ); this.globalStateProvider = new DefaultGlobalStateProvider(storageServiceProvider); @@ -432,9 +472,7 @@ export default class MainBackground { this.accountService, this.singleUserStateProvider, ); - this.derivedStateProvider = new BackgroundDerivedStateProvider( - this.memoryStorageForStateProviders, - ); + this.derivedStateProvider = new BackgroundDerivedStateProvider(storageServiceProvider); this.stateProvider = new DefaultStateProvider( this.activeUserStateProvider, this.singleUserStateProvider, @@ -449,12 +487,6 @@ export default class MainBackground { this.biometricStateService = new DefaultBiometricStateService(this.stateProvider); this.userNotificationSettingsService = new UserNotificationSettingsService(this.stateProvider); - this.platformUtilsService = new BackgroundPlatformUtilsService( - this.messagingService, - (clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs), - async () => this.biometricUnlock(), - self, - ); this.tokenService = new TokenService( this.singleUserStateProvider, @@ -556,21 +588,6 @@ export default class MainBackground { this.twoFactorService = new TwoFactorService(this.i18nService, this.platformUtilsService); - // eslint-disable-next-line - const that = this; - const backgroundMessagingService = new (class extends MessagingServiceAbstraction { - // AuthService should send the messages to the background not popup. - send = (subscriber: string, arg: any = {}) => { - if (BrowserApi.isManifestVersion(3)) { - that.messagingService.send(subscriber, arg); - return; - } - - const message = Object.assign({}, { command: subscriber }, arg); - void that.runtimeBackground.processMessage(message, that as any); - }; - })(); - this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider); this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); @@ -596,11 +613,12 @@ export default class MainBackground { this.masterPasswordService, this.cryptoService, this.apiService, + this.stateProvider, ); this.authService = new AuthService( this.accountService, - backgroundMessagingService, + this.messagingService, this.cryptoService, this.apiService, this.stateService, @@ -621,7 +639,7 @@ export default class MainBackground { this.tokenService, this.appIdService, this.platformUtilsService, - backgroundMessagingService, + this.messagingService, this.logService, this.keyConnectorService, this.environmentService, @@ -662,12 +680,12 @@ export default class MainBackground { this.encryptService, this.cipherFileUploadService, this.configService, + this.stateProvider, ); this.folderService = new FolderService( this.cryptoService, this.i18nService, this.cipherService, - this.stateService, this.stateProvider, ); this.folderApiService = new FolderApiService(this.folderService, this.apiService); @@ -774,6 +792,7 @@ export default class MainBackground { this.avatarService, logoutCallback, this.billingAccountProfileStateService, + this.tokenService, ); this.eventUploadService = new EventUploadService( this.apiService, @@ -790,6 +809,7 @@ export default class MainBackground { ); this.totpService = new TotpService(this.cryptoFunctionService, this.logService); + this.scriptInjectorService = new BrowserScriptInjectorService(); this.autofillService = new AutofillService( this.cipherService, this.autofillSettingsService, @@ -799,6 +819,8 @@ export default class MainBackground { this.domainSettingsService, this.userVerificationService, this.billingAccountProfileStateService, + this.scriptInjectorService, + this.accountService, ); this.auditService = new AuditService(this.cryptoFunctionService, this.apiService); @@ -844,10 +866,10 @@ export default class MainBackground { logoutCallback, this.stateService, this.authService, + this.authRequestService, this.messagingService, ); - this.fido2Service = new Fido2Service(); this.fido2UserInterfaceService = new BrowserFido2UserInterfaceService(this.authService); this.fido2AuthenticatorService = new Fido2AuthenticatorService( this.cipherService, @@ -887,82 +909,90 @@ export default class MainBackground { this.isSafari = this.platformUtilsService.isSafari(); // Background - this.runtimeBackground = new RuntimeBackground( - this, - this.autofillService, - this.platformUtilsService as BrowserPlatformUtilsService, - this.i18nService, - this.notificationsService, - this.stateService, - this.autofillSettingsService, - this.systemService, - this.environmentService, - this.messagingService, - this.logService, - this.configService, - this.fido2Service, - ); - this.nativeMessagingBackground = new NativeMessagingBackground( - this.accountService, - this.masterPasswordService, - this.cryptoService, - this.cryptoFunctionService, - this.runtimeBackground, - this.messagingService, - this.appIdService, - this.platformUtilsService, - this.stateService, - this.logService, - this.authService, - this.biometricStateService, - ); - this.commandsBackground = new CommandsBackground( - this, - this.passwordGenerationService, - this.platformUtilsService, - this.vaultTimeoutService, - this.authService, - ); - this.notificationBackground = new NotificationBackground( - this.autofillService, - this.cipherService, - this.authService, - this.policyService, - this.folderService, - this.stateService, - this.userNotificationSettingsService, - this.domainSettingsService, - this.environmentService, - this.logService, - themeStateService, - this.configService, - ); - this.overlayBackground = new OverlayBackground( - this.cipherService, - this.autofillService, - this.authService, - this.environmentService, - this.domainSettingsService, - this.stateService, - this.autofillSettingsService, - this.i18nService, - this.platformUtilsService, - themeStateService, - ); - this.filelessImporterBackground = new FilelessImporterBackground( - this.configService, - this.authService, - this.policyService, - this.notificationBackground, - this.importService, - this.syncService, - ); - this.tabsBackground = new TabsBackground( - this, - this.notificationBackground, - this.overlayBackground, - ); if (!this.popupOnlyContext) { + this.fido2Background = new Fido2Background( + this.logService, + this.fido2ClientService, + this.vaultSettingsService, + this.scriptInjectorService, + ); + this.runtimeBackground = new RuntimeBackground( + this, + this.autofillService, + this.platformUtilsService as BrowserPlatformUtilsService, + this.notificationsService, + this.stateService, + this.autofillSettingsService, + this.systemService, + this.environmentService, + this.messagingService, + this.logService, + this.configService, + this.fido2Background, + messageListener, + ); + this.nativeMessagingBackground = new NativeMessagingBackground( + this.accountService, + this.masterPasswordService, + this.cryptoService, + this.cryptoFunctionService, + this.runtimeBackground, + this.messagingService, + this.appIdService, + this.platformUtilsService, + this.stateService, + this.logService, + this.authService, + this.biometricStateService, + ); + this.commandsBackground = new CommandsBackground( + this, + this.passwordGenerationService, + this.platformUtilsService, + this.vaultTimeoutService, + this.authService, + ); + this.notificationBackground = new NotificationBackground( + this.autofillService, + this.cipherService, + this.authService, + this.policyService, + this.folderService, + this.stateService, + this.userNotificationSettingsService, + this.domainSettingsService, + this.environmentService, + this.logService, + themeStateService, + this.configService, + ); + this.overlayBackground = new OverlayBackground( + this.cipherService, + this.autofillService, + this.authService, + this.environmentService, + this.domainSettingsService, + this.stateService, + this.autofillSettingsService, + this.i18nService, + this.platformUtilsService, + themeStateService, + ); + this.filelessImporterBackground = new FilelessImporterBackground( + this.configService, + this.authService, + this.policyService, + this.notificationBackground, + this.importService, + this.syncService, + this.scriptInjectorService, + ); + this.tabsBackground = new TabsBackground( + this, + this.notificationBackground, + this.overlayBackground, + ); + const contextMenuClickedHandler = new ContextMenuClickedHandler( (options) => this.platformUtilsService.copyToClipboard(options.text), async (_tab) => { @@ -1004,11 +1034,6 @@ export default class MainBackground { this.notificationsService, this.accountService, ); - this.webRequestBackground = new WebRequestBackground( - this.platformUtilsService, - this.cipherService, - this.authService, - ); this.usernameGenerationService = new UsernameGenerationService( this.cryptoService, @@ -1030,34 +1055,51 @@ export default class MainBackground { this.authService, this.cipherService, ); + + if (BrowserApi.isManifestVersion(2)) { + this.webRequestBackground = new WebRequestBackground( + this.platformUtilsService, + this.cipherService, + this.authService, + ); + } } + + this.userKeyInitService = new UserKeyInitService( + this.accountService, + this.cryptoService, + this.logService, + ); } async bootstrap() { this.containerService.attachToGlobal(self); - await this.stateService.init(); + await this.stateService.init({ runMigrations: !this.isPrivateMode }); + + // This is here instead of in in the InitService b/c we don't plan for + // side effects to run in the Browser InitService. + this.userKeyInitService.listenForActiveUserChangesToSetUserKey(); - await this.vaultTimeoutService.init(true); await (this.i18nService as I18nService).init(); await (this.eventUploadService as EventUploadService).init(true); - await this.runtimeBackground.init(); - await this.notificationBackground.init(); - this.filelessImporterBackground.init(); - await this.commandsBackground.init(); - this.twoFactorService.init(); - await this.overlayBackground.init(); - - await this.tabsBackground.init(); if (!this.popupOnlyContext) { + await this.vaultTimeoutService.init(true); + this.fido2Background.init(); + await this.runtimeBackground.init(); + await this.notificationBackground.init(); + this.filelessImporterBackground.init(); + await this.commandsBackground.init(); + await this.overlayBackground.init(); + await this.tabsBackground.init(); this.contextMenusBackground?.init(); + await this.idleBackground.init(); + if (BrowserApi.isManifestVersion(2)) { + await this.webRequestBackground.init(); + } } - await this.idleBackground.init(); - await this.webRequestBackground.init(); - - await this.fido2Service.init(); if (this.platformUtilsService.isFirefox() && !this.isPrivateMode) { // Set Private Mode windows to the default icon - they do not share state with the background page @@ -1080,9 +1122,7 @@ export default class MainBackground { if (!this.isPrivateMode) { await this.refreshBadge(); } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.fullSync(true); + await this.fullSync(true); setTimeout(() => this.notificationsService.init(), 2500); resolve(); }, 500); @@ -1203,7 +1243,7 @@ export default class MainBackground { BrowserApi.sendMessage("updateBadge"); } await this.refreshBadge(); - await this.mainContextMenuHandler.noAccess(); + await this.mainContextMenuHandler?.noAccess(); // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises this.notificationsService.updateConnection(false); diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index faf2e6e2cc..5ac9961147 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -204,6 +204,8 @@ export class NativeMessagingBackground { this.privateKey = null; this.connected = false; + this.logService.error("NativeMessaging port disconnected because of error: " + error); + const reason = error != null ? "desktopIntegrationDisabled" : null; reject(new Error(reason)); }); @@ -397,7 +399,7 @@ export class NativeMessagingBackground { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.runtimeBackground.processMessage({ command: "unlocked" }, null); + this.runtimeBackground.processMessage({ command: "unlocked" }); } break; } diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index a88bc051d8..f457889e96 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -1,16 +1,16 @@ -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, mergeMap } from "rxjs"; import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { SystemService } from "@bitwarden/common/platform/abstractions/system.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { MessageListener } from "../../../../libs/common/src/platform/messaging"; import { closeUnlockPopout, openSsoAuthResultPopout, @@ -22,8 +22,7 @@ import { BrowserApi } from "../platform/browser/browser-api"; import { BrowserStateService } from "../platform/services/abstractions/browser-state.service"; import { BrowserEnvironmentService } from "../platform/services/browser-environment.service"; import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service"; -import { AbortManager } from "../vault/background/abort-manager"; -import { Fido2Service } from "../vault/services/abstractions/fido2.service"; +import { Fido2Background } from "../vault/fido2/background/abstractions/fido2.background"; import MainBackground from "./main.background"; @@ -32,13 +31,11 @@ export default class RuntimeBackground { private pageDetailsToAutoFill: any[] = []; private onInstalledReason: string = null; private lockedVaultPendingNotifications: LockedVaultPendingNotificationsData[] = []; - private abortManager = new AbortManager(); constructor( private main: MainBackground, private autofillService: AutofillService, private platformUtilsService: BrowserPlatformUtilsService, - private i18nService: I18nService, private notificationsService: NotificationsService, private stateService: BrowserStateService, private autofillSettingsService: AutofillSettingsServiceAbstraction, @@ -47,7 +44,8 @@ export default class RuntimeBackground { private messagingService: MessagingService, private logService: LogService, private configService: ConfigService, - private fido2Service: Fido2Service, + private fido2Background: Fido2Background, + private messageListener: MessageListener, ) { // onInstalled listener must be wired up before anything else, so we do it in the ctor chrome.runtime.onInstalled.addListener((details: any) => { @@ -64,100 +62,47 @@ export default class RuntimeBackground { const backgroundMessageListener = ( msg: any, sender: chrome.runtime.MessageSender, - sendResponse: any, + sendResponse: (response: any) => void, ) => { - const messagesWithResponse = [ - "checkFido2FeatureEnabled", - "fido2RegisterCredentialRequest", - "fido2GetCredentialRequest", - "biometricUnlock", - ]; + const messagesWithResponse = ["biometricUnlock"]; if (messagesWithResponse.includes(msg.command)) { - this.processMessage(msg, sender).then( + this.processMessageWithSender(msg, sender).then( (value) => sendResponse({ result: value }), (error) => sendResponse({ error: { ...error, message: error.message } }), ); return true; } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.processMessage(msg, sender); + void this.processMessageWithSender(msg, sender).catch((err) => + this.logService.error( + `Error while processing message in RuntimeBackground '${msg?.command}'. Error: ${err?.message ?? "Unknown Error"}`, + ), + ); return false; }; + this.messageListener.allMessages$ + .pipe( + mergeMap(async (message: any) => { + await this.processMessage(message); + }), + ) + .subscribe(); + + // For messages that require the full on message interface BrowserApi.messageListener("runtime.background", backgroundMessageListener); - if (this.main.popupOnlyContext) { - (self as any).bitwardenBackgroundMessageListener = backgroundMessageListener; - } } - async processMessage(msg: any, sender: chrome.runtime.MessageSender) { + // Messages that need the chrome sender and send back a response need to be registered in this method. + async processMessageWithSender(msg: any, sender: chrome.runtime.MessageSender) { switch (msg.command) { - case "loggedIn": - case "unlocked": { - let item: LockedVaultPendingNotificationsData; - - if (msg.command === "loggedIn") { - await this.sendBwInstalledMessageToVault(); - } - - if (this.lockedVaultPendingNotifications?.length > 0) { - item = this.lockedVaultPendingNotifications.pop(); - await closeUnlockPopout(); - } - - await this.notificationsService.updateConnection(msg.command === "loggedIn"); - await this.main.refreshBadge(); - await this.main.refreshMenu(false); - this.systemService.cancelProcessReload(); - - if (item) { - await BrowserApi.focusWindow(item.commandToRetry.sender.tab.windowId); - await BrowserApi.focusTab(item.commandToRetry.sender.tab.id); - await BrowserApi.tabSendMessageData( - item.commandToRetry.sender.tab, - "unlockCompleted", - item, - ); - } - break; - } - case "addToLockedVaultPendingNotifications": - this.lockedVaultPendingNotifications.push(msg.data); - break; - case "logout": - await this.main.logout(msg.expired, msg.userId); - break; - case "syncCompleted": - if (msg.successfully) { - setTimeout(async () => { - await this.main.refreshBadge(); - await this.main.refreshMenu(); - }, 2000); - await this.configService.ensureConfigFetched(); - } - break; - case "openPopup": - await this.main.openPopup(); - break; case "triggerAutofillScriptInjection": await this.autofillService.injectAutofillScripts(sender.tab, sender.frameId); break; case "bgCollectPageDetails": await this.main.collectPageDetailsForContentScript(sender.tab, msg.sender, sender.frameId); break; - case "bgUpdateContextMenu": - case "editedCipher": - case "addedCipher": - case "deletedCipher": - await this.main.refreshBadge(); - await this.main.refreshMenu(); - break; - case "bgReseedStorage": - await this.main.reseedStorage(); - break; case "collectPageDetailsResponse": switch (msg.sender) { case "autofiller": @@ -221,6 +166,72 @@ export default class RuntimeBackground { break; } break; + case "biometricUnlock": { + const result = await this.main.biometricUnlock(); + return result; + } + } + } + + async processMessage(msg: any) { + switch (msg.command) { + case "loggedIn": + case "unlocked": { + let item: LockedVaultPendingNotificationsData; + + if (msg.command === "loggedIn") { + await this.sendBwInstalledMessageToVault(); + } + + if (this.lockedVaultPendingNotifications?.length > 0) { + item = this.lockedVaultPendingNotifications.pop(); + await closeUnlockPopout(); + } + + await this.notificationsService.updateConnection(msg.command === "loggedIn"); + await this.main.refreshBadge(); + await this.main.refreshMenu(false); + this.systemService.cancelProcessReload(); + + if (item) { + await BrowserApi.focusWindow(item.commandToRetry.sender.tab.windowId); + await BrowserApi.focusTab(item.commandToRetry.sender.tab.id); + await BrowserApi.tabSendMessageData( + item.commandToRetry.sender.tab, + "unlockCompleted", + item, + ); + } + break; + } + case "addToLockedVaultPendingNotifications": + this.lockedVaultPendingNotifications.push(msg.data); + break; + case "logout": + await this.main.logout(msg.expired, msg.userId); + break; + case "syncCompleted": + if (msg.successfully) { + setTimeout(async () => { + await this.main.refreshBadge(); + await this.main.refreshMenu(); + }, 2000); + await this.configService.ensureConfigFetched(); + } + break; + case "openPopup": + await this.main.openPopup(); + break; + case "bgUpdateContextMenu": + case "editedCipher": + case "addedCipher": + case "deletedCipher": + await this.main.refreshBadge(); + await this.main.refreshMenu(); + break; + case "bgReseedStorage": + await this.main.reseedStorage(); + break; case "authResult": { const env = await firstValueFrom(this.environmentService.environment$); const vaultUrl = env.getWebVaultUrl(); @@ -269,46 +280,6 @@ export default class RuntimeBackground { case "getClickedElementResponse": this.platformUtilsService.copyToClipboard(msg.identifier); break; - case "triggerFido2ContentScriptInjection": - await this.fido2Service.injectFido2ContentScripts(sender); - break; - case "fido2AbortRequest": - this.abortManager.abort(msg.abortedRequestId); - break; - case "checkFido2FeatureEnabled": - return await this.main.fido2ClientService.isFido2FeatureEnabled(msg.hostname, msg.origin); - case "fido2RegisterCredentialRequest": - return await this.abortManager.runWithAbortController( - msg.requestId, - async (abortController) => { - try { - return await this.main.fido2ClientService.createCredential( - msg.data, - sender.tab, - abortController, - ); - } finally { - await BrowserApi.focusTab(sender.tab.id); - await BrowserApi.focusWindow(sender.tab.windowId); - } - }, - ); - case "fido2GetCredentialRequest": - return await this.abortManager.runWithAbortController( - msg.requestId, - async (abortController) => { - try { - return await this.main.fido2ClientService.assertCredential( - msg.data, - sender.tab, - abortController, - ); - } finally { - await BrowserApi.focusTab(sender.tab.id); - await BrowserApi.focusWindow(sender.tab.windowId); - } - }, - ); case "switchAccount": { await this.main.switchAccount(msg.userId); break; @@ -317,9 +288,6 @@ export default class RuntimeBackground { await this.main.clearClipboard(msg.clipboardValue, msg.timeoutMs); break; } - case "biometricUnlock": { - return await this.main.biometricUnlock(); - } } } @@ -343,9 +311,8 @@ export default class RuntimeBackground { private async checkOnInstalled() { setTimeout(async () => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.autofillService.loadAutofillScriptsOnInstall(); + void this.fido2Background.injectFido2ContentScriptsInAllTabs(); + void this.autofillService.loadAutofillScriptsOnInstall(); if (this.onInstalledReason != null) { if (this.onInstalledReason === "install") { diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index aec7523d5e..78f1e2cc41 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -22,13 +22,6 @@ "exclude_matches": ["*://*/*.xml*", "file:///*.xml*"], "run_at": "document_start" }, - { - "all_frames": true, - "js": ["content/fido2/trigger-fido2-content-script-injection.js"], - "matches": ["https://*/*"], - "exclude_matches": ["https://*/*.xml*"], - "run_at": "document_start" - }, { "all_frames": true, "css": ["content/autofill.css"], @@ -67,7 +60,8 @@ "clipboardWrite", "idle", "webRequest", - "webRequestBlocking" + "webRequestBlocking", + "webNavigation" ], "optional_permissions": ["nativeMessaging", "privacy"], "content_security_policy": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index d67b4affab..cdd0869fc5 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -23,13 +23,6 @@ "exclude_matches": ["*://*/*.xml*", "file:///*.xml*"], "run_at": "document_start" }, - { - "all_frames": true, - "js": ["content/fido2/trigger-fido2-content-script-injection.js"], - "matches": ["https://*/*"], - "exclude_matches": ["https://*/*.xml*"], - "run_at": "document_start" - }, { "all_frames": true, "css": ["content/autofill.css"], @@ -71,7 +64,7 @@ "offscreen" ], "optional_permissions": ["nativeMessaging", "privacy"], - "host_permissions": ["*://*/*"], + "host_permissions": [""], "content_security_policy": { "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'", "sandbox": "sandbox allow-scripts; script-src 'self'" diff --git a/apps/browser/src/models/browserSendComponentState.ts b/apps/browser/src/models/browserSendComponentState.ts index 9158efc21d..81dd93323b 100644 --- a/apps/browser/src/models/browserSendComponentState.ts +++ b/apps/browser/src/models/browserSendComponentState.ts @@ -1,5 +1,3 @@ -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; @@ -7,13 +5,6 @@ import { BrowserComponentState } from "./browserComponentState"; export class BrowserSendComponentState extends BrowserComponentState { sends: SendView[]; - typeCounts: Map; - - toJSON() { - return Utils.merge(this, { - typeCounts: Utils.mapToRecord(this.typeCounts), - }); - } static fromJSON(json: DeepJsonify) { if (json == null) { @@ -22,7 +13,6 @@ export class BrowserSendComponentState extends BrowserComponentState { return Object.assign(new BrowserSendComponentState(), json, { sends: json.sends?.map((s) => SendView.fromJSON(s)), - typeCounts: Utils.recordToMap(json.typeCounts), }); } } diff --git a/apps/browser/src/platform/background.ts b/apps/browser/src/platform/background.ts index 9c3510178c..a48c420e77 100644 --- a/apps/browser/src/platform/background.ts +++ b/apps/browser/src/platform/background.ts @@ -5,16 +5,11 @@ import MainBackground from "../background/main.background"; import { BrowserApi } from "./browser/browser-api"; const logService = new ConsoleLogService(false); +if (BrowserApi.isManifestVersion(3)) { + startHeartbeat().catch((error) => logService.error(error)); +} const bitwardenMain = ((self as any).bitwardenMain = new MainBackground()); -bitwardenMain - .bootstrap() - .then(() => { - // Finished bootstrapping - if (BrowserApi.isManifestVersion(3)) { - startHeartbeat().catch((error) => logService.error(error)); - } - }) - .catch((error) => logService.error(error)); +bitwardenMain.bootstrap().catch((error) => logService.error(error)); /** * Tracks when a service worker was last alive and extends the service worker diff --git a/apps/browser/src/platform/background/service-factories/browser-script-injector-service.factory.ts b/apps/browser/src/platform/background/service-factories/browser-script-injector-service.factory.ts new file mode 100644 index 0000000000..e3bc687f28 --- /dev/null +++ b/apps/browser/src/platform/background/service-factories/browser-script-injector-service.factory.ts @@ -0,0 +1,19 @@ +import { BrowserScriptInjectorService } from "../../services/browser-script-injector.service"; + +import { CachedServices, FactoryOptions, factory } from "./factory-options"; + +type BrowserScriptInjectorServiceOptions = FactoryOptions; + +export type BrowserScriptInjectorServiceInitOptions = BrowserScriptInjectorServiceOptions; + +export function browserScriptInjectorServiceFactory( + cache: { browserScriptInjectorService?: BrowserScriptInjectorService } & CachedServices, + opts: BrowserScriptInjectorServiceInitOptions, +): Promise { + return factory( + cache, + "browserScriptInjectorService", + opts, + async () => new BrowserScriptInjectorService(), + ); +} diff --git a/apps/browser/src/platform/background/service-factories/derived-state-provider.factory.ts b/apps/browser/src/platform/background/service-factories/derived-state-provider.factory.ts index 4f329c93d5..4025d01950 100644 --- a/apps/browser/src/platform/background/service-factories/derived-state-provider.factory.ts +++ b/apps/browser/src/platform/background/service-factories/derived-state-provider.factory.ts @@ -4,14 +4,14 @@ import { BackgroundDerivedStateProvider } from "../../state/background-derived-s import { CachedServices, FactoryOptions, factory } from "./factory-options"; import { - MemoryStorageServiceInitOptions, - observableMemoryStorageServiceFactory, -} from "./storage-service.factory"; + StorageServiceProviderInitOptions, + storageServiceProviderFactory, +} from "./storage-service-provider.factory"; type DerivedStateProviderFactoryOptions = FactoryOptions; export type DerivedStateProviderInitOptions = DerivedStateProviderFactoryOptions & - MemoryStorageServiceInitOptions; + StorageServiceProviderInitOptions; export async function derivedStateProviderFactory( cache: { derivedStateProvider?: DerivedStateProvider } & CachedServices, @@ -22,6 +22,6 @@ export async function derivedStateProviderFactory( "derivedStateProvider", opts, async () => - new BackgroundDerivedStateProvider(await observableMemoryStorageServiceFactory(cache, opts)), + new BackgroundDerivedStateProvider(await storageServiceProviderFactory(cache, opts)), ); } diff --git a/apps/browser/src/platform/background/service-factories/message-sender.factory.ts b/apps/browser/src/platform/background/service-factories/message-sender.factory.ts new file mode 100644 index 0000000000..6f50b4b8f5 --- /dev/null +++ b/apps/browser/src/platform/background/service-factories/message-sender.factory.ts @@ -0,0 +1,17 @@ +import { MessageSender } from "@bitwarden/common/platform/messaging"; + +import { CachedServices, factory, FactoryOptions } from "./factory-options"; + +type MessagingServiceFactoryOptions = FactoryOptions; + +export type MessageSenderInitOptions = MessagingServiceFactoryOptions; + +export function messageSenderFactory( + cache: { messagingService?: MessageSender } & CachedServices, + opts: MessageSenderInitOptions, +): Promise { + // NOTE: Name needs to match that of MainBackground property until we delete these. + return factory(cache, "messagingService", opts, () => { + throw new Error("Not implemented, not expected to be used."); + }); +} diff --git a/apps/browser/src/platform/background/service-factories/messaging-service.factory.ts b/apps/browser/src/platform/background/service-factories/messaging-service.factory.ts index 46852712aa..20c6e3f424 100644 --- a/apps/browser/src/platform/background/service-factories/messaging-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/messaging-service.factory.ts @@ -1,19 +1,5 @@ -import { MessagingService as AbstractMessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; - -import { - CachedServices, - factory, - FactoryOptions, -} from "../../background/service-factories/factory-options"; -import BrowserMessagingService from "../../services/browser-messaging.service"; - -type MessagingServiceFactoryOptions = FactoryOptions; - -export type MessagingServiceInitOptions = MessagingServiceFactoryOptions; - -export function messagingServiceFactory( - cache: { messagingService?: AbstractMessagingService } & CachedServices, - opts: MessagingServiceInitOptions, -): Promise { - return factory(cache, "messagingService", opts, () => new BrowserMessagingService()); -} +// Export old messaging service stuff to minimize changes +export { + messageSenderFactory as messagingServiceFactory, + MessageSenderInitOptions as MessagingServiceInitOptions, +} from "./message-sender.factory"; diff --git a/apps/browser/src/platform/background/service-factories/storage-service.factory.ts b/apps/browser/src/platform/background/service-factories/storage-service.factory.ts index 19d5a9c140..83e8a780a6 100644 --- a/apps/browser/src/platform/background/service-factories/storage-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/storage-service.factory.ts @@ -17,6 +17,11 @@ import { KeyGenerationServiceInitOptions, keyGenerationServiceFactory, } from "./key-generation-service.factory"; +import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory"; +import { + platformUtilsServiceFactory, + PlatformUtilsServiceInitOptions, +} from "./platform-utils-service.factory"; export type DiskStorageServiceInitOptions = FactoryOptions; export type SecureStorageServiceInitOptions = FactoryOptions; @@ -25,7 +30,9 @@ export type MemoryStorageServiceInitOptions = FactoryOptions & EncryptServiceInitOptions & KeyGenerationServiceInitOptions & DiskStorageServiceInitOptions & - SessionStorageServiceInitOptions; + SessionStorageServiceInitOptions & + LogServiceInitOptions & + PlatformUtilsServiceInitOptions; export function diskStorageServiceFactory( cache: { diskStorageService?: AbstractStorageService } & CachedServices, @@ -63,10 +70,12 @@ export function memoryStorageServiceFactory( return factory(cache, "memoryStorageService", opts, async () => { if (BrowserApi.isManifestVersion(3)) { return new LocalBackedSessionStorageService( + await logServiceFactory(cache, opts), await encryptServiceFactory(cache, opts), await keyGenerationServiceFactory(cache, opts), await diskStorageServiceFactory(cache, opts), await sessionStorageServiceFactory(cache, opts), + await platformUtilsServiceFactory(cache, opts), "serviceFactories", ); } diff --git a/apps/browser/src/platform/browser/browser-api.register-content-scripts-polyfill.ts b/apps/browser/src/platform/browser/browser-api.register-content-scripts-polyfill.ts new file mode 100644 index 0000000000..8a20f3e999 --- /dev/null +++ b/apps/browser/src/platform/browser/browser-api.register-content-scripts-polyfill.ts @@ -0,0 +1,435 @@ +/** + * MIT License + * + * Copyright (c) Federico Brigante (https://fregante.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @see https://github.com/fregante/content-scripts-register-polyfill + * @version 4.0.2 + */ +import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; + +import { BrowserApi } from "./browser-api"; + +let registerContentScripts: ( + contentScriptOptions: browser.contentScripts.RegisteredContentScriptOptions, + callback?: (registeredContentScript: browser.contentScripts.RegisteredContentScript) => void, +) => Promise; +export async function registerContentScriptsPolyfill( + contentScriptOptions: browser.contentScripts.RegisteredContentScriptOptions, + callback?: (registeredContentScript: browser.contentScripts.RegisteredContentScript) => void, +) { + if (!registerContentScripts) { + registerContentScripts = buildRegisterContentScriptsPolyfill(); + } + + return registerContentScripts(contentScriptOptions, callback); +} + +function buildRegisterContentScriptsPolyfill() { + const logService = new ConsoleLogService(false); + const chromeProxy = globalThis.chrome && NestedProxy(globalThis.chrome); + const patternValidationRegex = + /^(https?|wss?|file|ftp|\*):\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$|^file:\/\/\/.*$|^resource:\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$|^about:/; + const isFirefox = globalThis.navigator?.userAgent.includes("Firefox/"); + const gotScripting = Boolean(globalThis.chrome?.scripting); + const gotNavigation = typeof chrome === "object" && "webNavigation" in chrome; + + function NestedProxy(target: T): T { + return new Proxy(target, { + get(target, prop) { + if (!target[prop as keyof T]) { + return; + } + + if (typeof target[prop as keyof T] !== "function") { + return NestedProxy(target[prop as keyof T]); + } + + return (...arguments_: any[]) => + new Promise((resolve, reject) => { + target[prop as keyof T](...arguments_, (result: any) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(result); + } + }); + }); + }, + }); + } + + function assertValidPattern(matchPattern: string) { + if (!isValidPattern(matchPattern)) { + throw new Error( + `${matchPattern} is an invalid pattern, it must match ${String(patternValidationRegex)}`, + ); + } + } + + function isValidPattern(matchPattern: string) { + return matchPattern === "" || patternValidationRegex.test(matchPattern); + } + + function getRawPatternRegex(matchPattern: string) { + assertValidPattern(matchPattern); + let [, protocol, host = "", pathname] = matchPattern.split(/(^[^:]+:[/][/])([^/]+)?/); + protocol = protocol + .replace("*", isFirefox ? "(https?|wss?)" : "https?") + .replaceAll(/[/]/g, "[/]"); + + if (host === "*") { + host = "[^/]+"; + } else if (host) { + host = host + .replace(/^[*][.]/, "([^/]+.)*") + .replaceAll(/[.]/g, "[.]") + .replace(/[*]$/, "[^.]+"); + } + + pathname = pathname + .replaceAll(/[/]/g, "[/]") + .replaceAll(/[.]/g, "[.]") + .replaceAll(/[*]/g, ".*"); + + return "^" + protocol + host + "(" + pathname + ")?$"; + } + + function patternToRegex(...matchPatterns: string[]) { + if (matchPatterns.length === 0) { + return /$./; + } + + if (matchPatterns.includes("")) { + // regex + return /^(https?|file|ftp):[/]+/; + } + + if (matchPatterns.includes("*://*/*")) { + // all stars regex + return isFirefox ? /^(https?|wss?):[/][/][^/]+([/].*)?$/ : /^https?:[/][/][^/]+([/].*)?$/; + } + + return new RegExp(matchPatterns.map((x) => getRawPatternRegex(x)).join("|")); + } + + function castAllFramesTarget(target: number | { tabId: number; frameId: number }) { + if (typeof target === "object") { + return { ...target, allFrames: false }; + } + + return { + tabId: target, + frameId: undefined, + allFrames: true, + }; + } + + function castArray(possibleArray: any | any[]) { + if (Array.isArray(possibleArray)) { + return possibleArray; + } + + return [possibleArray]; + } + + function arrayOrUndefined(value?: number) { + return value === undefined ? undefined : [value]; + } + + async function insertCSS( + { + tabId, + frameId, + files, + allFrames, + matchAboutBlank, + runAt, + }: { + tabId: number; + frameId?: number; + files: browser.extensionTypes.ExtensionFileOrCode[]; + allFrames: boolean; + matchAboutBlank: boolean; + runAt: browser.extensionTypes.RunAt; + }, + { ignoreTargetErrors }: { ignoreTargetErrors?: boolean } = {}, + ) { + const everyInsertion = Promise.all( + files.map(async (content) => { + if (typeof content === "string") { + content = { file: content }; + } + + if (gotScripting) { + return chrome.scripting.insertCSS({ + target: { + tabId, + frameIds: arrayOrUndefined(frameId), + allFrames: frameId === undefined ? allFrames : undefined, + }, + files: "file" in content ? [content.file] : undefined, + css: "code" in content ? content.code : undefined, + }); + } + + return chromeProxy.tabs.insertCSS(tabId, { + ...content, + matchAboutBlank, + allFrames, + frameId, + runAt: runAt ?? "document_start", + }); + }), + ); + + if (ignoreTargetErrors) { + await catchTargetInjectionErrors(everyInsertion); + } else { + await everyInsertion; + } + } + function assertNoCode(files: browser.extensionTypes.ExtensionFileOrCode[]) { + if (files.some((content) => "code" in content)) { + throw new Error("chrome.scripting does not support injecting strings of `code`"); + } + } + + async function executeScript( + { + tabId, + frameId, + files, + allFrames, + matchAboutBlank, + runAt, + }: { + tabId: number; + frameId?: number; + files: browser.extensionTypes.ExtensionFileOrCode[]; + allFrames: boolean; + matchAboutBlank: boolean; + runAt: browser.extensionTypes.RunAt; + }, + { ignoreTargetErrors }: { ignoreTargetErrors?: boolean } = {}, + ) { + const normalizedFiles = files.map((file) => (typeof file === "string" ? { file } : file)); + + if (gotScripting) { + assertNoCode(normalizedFiles); + const injection = chrome.scripting.executeScript({ + target: { + tabId, + frameIds: arrayOrUndefined(frameId), + allFrames: frameId === undefined ? allFrames : undefined, + }, + files: normalizedFiles.map(({ file }: { file: string }) => file), + }); + + if (ignoreTargetErrors) { + await catchTargetInjectionErrors(injection); + } else { + await injection; + } + + return; + } + + const executions = []; + for (const content of normalizedFiles) { + if ("code" in content) { + await executions.at(-1); + } + + executions.push( + chromeProxy.tabs.executeScript(tabId, { + ...content, + matchAboutBlank, + allFrames, + frameId, + runAt, + }), + ); + } + + if (ignoreTargetErrors) { + await catchTargetInjectionErrors(Promise.all(executions)); + } else { + await Promise.all(executions); + } + } + + async function injectContentScript( + where: { tabId: number; frameId: number }, + scripts: { + css: browser.extensionTypes.ExtensionFileOrCode[]; + js: browser.extensionTypes.ExtensionFileOrCode[]; + matchAboutBlank: boolean; + runAt: browser.extensionTypes.RunAt; + }, + options = {}, + ) { + const targets = castArray(where); + await Promise.all( + targets.map(async (target) => + injectContentScriptInSpecificTarget(castAllFramesTarget(target), scripts, options), + ), + ); + } + + async function injectContentScriptInSpecificTarget( + { frameId, tabId, allFrames }: { frameId?: number; tabId: number; allFrames: boolean }, + scripts: { + css: browser.extensionTypes.ExtensionFileOrCode[]; + js: browser.extensionTypes.ExtensionFileOrCode[]; + matchAboutBlank: boolean; + runAt: browser.extensionTypes.RunAt; + }, + options = {}, + ) { + const injections = castArray(scripts).flatMap((script) => [ + insertCSS( + { + tabId, + frameId, + allFrames, + files: script.css ?? [], + matchAboutBlank: script.matchAboutBlank ?? script.match_about_blank, + runAt: script.runAt ?? script.run_at, + }, + options, + ), + executeScript( + { + tabId, + frameId, + allFrames, + files: script.js ?? [], + matchAboutBlank: script.matchAboutBlank ?? script.match_about_blank, + runAt: script.runAt ?? script.run_at, + }, + options, + ), + ]); + await Promise.all(injections); + } + + async function catchTargetInjectionErrors(promise: Promise) { + try { + await promise; + } catch (error) { + const targetErrors = + /^No frame with id \d+ in tab \d+.$|^No tab with id: \d+.$|^The tab was closed.$|^The frame was removed.$/; + if (!targetErrors.test(error?.message)) { + throw error; + } + } + } + + async function isOriginPermitted(url: string) { + return chromeProxy.permissions.contains({ + origins: [new URL(url).origin + "/*"], + }); + } + + return async ( + contentScriptOptions: browser.contentScripts.RegisteredContentScriptOptions, + callback: CallableFunction, + ) => { + const { + js = [], + css = [], + matchAboutBlank, + matches = [], + excludeMatches, + runAt, + } = contentScriptOptions; + let { allFrames } = contentScriptOptions; + + if (gotNavigation) { + allFrames = false; + } else if (allFrames) { + logService.warning( + "`allFrames: true` requires the `webNavigation` permission to work correctly: https://github.com/fregante/content-scripts-register-polyfill#permissions", + ); + } + + if (matches.length === 0) { + throw new Error( + "Type error for parameter contentScriptOptions (Error processing matches: Array requires at least 1 items; you have 0) for contentScripts.register.", + ); + } + + await Promise.all( + matches.map(async (pattern: string) => { + if (!(await chromeProxy.permissions.contains({ origins: [pattern] }))) { + throw new Error(`Permission denied to register a content script for ${pattern}`); + } + }), + ); + + const matchesRegex = patternToRegex(...matches); + const excludeMatchesRegex = patternToRegex( + ...(excludeMatches !== null && excludeMatches !== void 0 ? excludeMatches : []), + ); + const inject = async (url: string, tabId: number, frameId = 0) => { + if ( + !matchesRegex.test(url) || + excludeMatchesRegex.test(url) || + !(await isOriginPermitted(url)) + ) { + return; + } + + await injectContentScript( + { tabId, frameId }, + { css, js, matchAboutBlank, runAt }, + { ignoreTargetErrors: true }, + ); + }; + const tabListener = async ( + tabId: number, + { status }: chrome.tabs.TabChangeInfo, + { url }: chrome.tabs.Tab, + ) => { + if (status === "loading" && url) { + void inject(url, tabId); + } + }; + const navListener = async ({ + tabId, + frameId, + url, + }: chrome.webNavigation.WebNavigationTransitionCallbackDetails) => { + void inject(url, tabId, frameId); + }; + + if (gotNavigation) { + BrowserApi.addListener(chrome.webNavigation.onCommitted, navListener); + } else { + BrowserApi.addListener(chrome.tabs.onUpdated, tabListener); + } + + const registeredContentScript = { + async unregister() { + if (gotNavigation) { + chrome.webNavigation.onCommitted.removeListener(navListener); + } else { + chrome.tabs.onUpdated.removeListener(tabListener); + } + }, + }; + + if (typeof callback === "function") { + callback(registeredContentScript); + } + + return registeredContentScript; + }; +} diff --git a/apps/browser/src/platform/browser/browser-api.spec.ts b/apps/browser/src/platform/browser/browser-api.spec.ts index a1dafb38ec..e452d6d8ee 100644 --- a/apps/browser/src/platform/browser/browser-api.spec.ts +++ b/apps/browser/src/platform/browser/browser-api.spec.ts @@ -550,4 +550,35 @@ describe("BrowserApi", () => { expect(callbackMock).toHaveBeenCalled(); }); }); + + describe("registerContentScriptsMv2", () => { + const details: browser.contentScripts.RegisteredContentScriptOptions = { + matches: [""], + js: [{ file: "content/fido2/page-script.js" }], + }; + + it("registers content scripts through the `browser.contentScripts` API when the API is available", async () => { + globalThis.browser = mock({ + contentScripts: { register: jest.fn() }, + }); + + await BrowserApi.registerContentScriptsMv2(details); + + expect(browser.contentScripts.register).toHaveBeenCalledWith(details); + }); + + it("registers content scripts through the `registerContentScriptsPolyfill` when the `browser.contentScripts.register` API is not available", async () => { + globalThis.browser = mock({ + contentScripts: { register: undefined }, + }); + jest.spyOn(BrowserApi, "addListener"); + + await BrowserApi.registerContentScriptsMv2(details); + + expect(BrowserApi.addListener).toHaveBeenCalledWith( + chrome.webNavigation.onCommitted, + expect.any(Function), + ); + }); + }); }); diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index b2ee66f051..b793777d8b 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -5,6 +5,8 @@ import { DeviceType } from "@bitwarden/common/enums"; import { TabMessage } from "../../types/tab-messages"; import { BrowserPlatformUtilsService } from "../services/platform-utils/browser-platform-utils.service"; +import { registerContentScriptsPolyfill } from "./browser-api.register-content-scripts-polyfill"; + export class BrowserApi { static isWebExtensionsApi: boolean = typeof browser !== "undefined"; static isSafariApi: boolean = @@ -591,4 +593,41 @@ export class BrowserApi { } }); } + + /** + * Handles registration of static content scripts within manifest v2. + * + * @param contentScriptOptions - Details of the registered content scripts + */ + static async registerContentScriptsMv2( + contentScriptOptions: browser.contentScripts.RegisteredContentScriptOptions, + ): Promise { + if (typeof browser !== "undefined" && !!browser.contentScripts?.register) { + return await browser.contentScripts.register(contentScriptOptions); + } + + return await registerContentScriptsPolyfill(contentScriptOptions); + } + + /** + * Handles registration of static content scripts within manifest v3. + * + * @param scripts - Details of the registered content scripts + */ + static async registerContentScriptsMv3( + scripts: chrome.scripting.RegisteredContentScript[], + ): Promise { + await chrome.scripting.registerContentScripts(scripts); + } + + /** + * Handles unregistering of static content scripts within manifest v3. + * + * @param filter - Optional filter to unregister content scripts. Passing an empty object will unregister all content scripts. + */ + static async unregisterContentScriptsMv3( + filter?: chrome.scripting.ContentScriptFilter, + ): Promise { + await chrome.scripting.unregisterContentScripts(filter); + } } diff --git a/apps/browser/src/platform/listeners/on-install-listener.ts b/apps/browser/src/platform/listeners/on-install-listener.ts index ef206301e3..adf575a17a 100644 --- a/apps/browser/src/platform/listeners/on-install-listener.ts +++ b/apps/browser/src/platform/listeners/on-install-listener.ts @@ -23,6 +23,11 @@ export async function onInstallListener(details: chrome.runtime.InstalledDetails stateServiceOptions: { stateFactory: new StateFactory(GlobalState, Account), }, + platformUtilsServiceOptions: { + win: self, + biometricCallback: async () => false, + clipboardWriteCallback: async () => {}, + }, }; const environmentService = await environmentServiceFactory(cache, opts); diff --git a/apps/browser/src/platform/messaging/chrome-message.sender.ts b/apps/browser/src/platform/messaging/chrome-message.sender.ts new file mode 100644 index 0000000000..0e57ecfb4e --- /dev/null +++ b/apps/browser/src/platform/messaging/chrome-message.sender.ts @@ -0,0 +1,37 @@ +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { CommandDefinition, MessageSender } from "@bitwarden/common/platform/messaging"; +import { getCommand } from "@bitwarden/common/platform/messaging/internal"; + +type ErrorHandler = (logger: LogService, command: string) => void; + +const HANDLED_ERRORS: Record = { + "Could not establish connection. Receiving end does not exist.": (logger, command) => + logger.debug(`Receiving end didn't exist for command '${command}'`), + + "The message port closed before a response was received.": (logger, command) => + logger.debug(`Port was closed for command '${command}'`), +}; + +export class ChromeMessageSender implements MessageSender { + constructor(private readonly logService: LogService) {} + + send( + commandDefinition: string | CommandDefinition, + payload: object | T = {}, + ): void { + const command = getCommand(commandDefinition); + chrome.runtime.sendMessage(Object.assign(payload, { command: command }), () => { + if (chrome.runtime.lastError) { + const errorHandler = HANDLED_ERRORS[chrome.runtime.lastError.message]; + if (errorHandler != null) { + errorHandler(this.logService, command); + return; + } + + this.logService.warning( + `Unhandled error while sending message with command '${command}': ${chrome.runtime.lastError.message}`, + ); + } + }); + } +} diff --git a/apps/browser/src/platform/services/abstractions/script-injector.service.ts b/apps/browser/src/platform/services/abstractions/script-injector.service.ts new file mode 100644 index 0000000000..b41e5c7617 --- /dev/null +++ b/apps/browser/src/platform/services/abstractions/script-injector.service.ts @@ -0,0 +1,45 @@ +export type CommonScriptInjectionDetails = { + /** + * Script injected into the document. + * Overridden by `mv2Details` and `mv3Details`. + */ + file?: string; + /** + * Identifies the frame targeted for script injection. Defaults to the top level frame (0). + * Can also be set to "all_frames" to inject into all frames in a tab. + */ + frame?: "all_frames" | number; + /** + * When the script executes. Defaults to "document_start". + * @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_scripts + */ + runAt?: "document_start" | "document_end" | "document_idle"; +}; + +export type Mv2ScriptInjectionDetails = { + file: string; +}; + +export type Mv3ScriptInjectionDetails = { + file: string; + /** + * The world in which the script should be executed. Defaults to "ISOLATED". + * @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/ExecutionWorld + */ + world?: chrome.scripting.ExecutionWorld; +}; + +/** + * Configuration for injecting a script into a tab. The `file` property should present as a + * path that is relative to the root directory of the extension build, ie "content/script.js". + */ +export type ScriptInjectionConfig = { + tabId: number; + injectDetails: CommonScriptInjectionDetails; + mv2Details?: Mv2ScriptInjectionDetails; + mv3Details?: Mv3ScriptInjectionDetails; +}; + +export abstract class ScriptInjectorService { + abstract inject(config: ScriptInjectionConfig): Promise; +} diff --git a/apps/browser/src/platform/services/browser-memory-storage.service.ts b/apps/browser/src/platform/services/browser-memory-storage.service.ts index f824a1df0d..b067dc5a12 100644 --- a/apps/browser/src/platform/services/browser-memory-storage.service.ts +++ b/apps/browser/src/platform/services/browser-memory-storage.service.ts @@ -1,7 +1,16 @@ +import { AbstractMemoryStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; + import AbstractChromeStorageService from "./abstractions/abstract-chrome-storage-api.service"; -export default class BrowserMemoryStorageService extends AbstractChromeStorageService { +export default class BrowserMemoryStorageService + extends AbstractChromeStorageService + implements AbstractMemoryStorageService +{ constructor() { super(chrome.storage.session); } + type = "MemoryStorageService" as const; + getBypassCache(key: string): Promise { + return this.get(key); + } } diff --git a/apps/browser/src/platform/services/browser-messaging-private-mode-background.service.ts b/apps/browser/src/platform/services/browser-messaging-private-mode-background.service.ts deleted file mode 100644 index 0c7008473b..0000000000 --- a/apps/browser/src/platform/services/browser-messaging-private-mode-background.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; - -export default class BrowserMessagingPrivateModeBackgroundService implements MessagingService { - send(subscriber: string, arg: any = {}) { - const message = Object.assign({}, { command: subscriber }, arg); - (self as any).bitwardenPopupMainMessageListener(message); - } -} diff --git a/apps/browser/src/platform/services/browser-messaging-private-mode-popup.service.ts b/apps/browser/src/platform/services/browser-messaging-private-mode-popup.service.ts deleted file mode 100644 index 5883f61197..0000000000 --- a/apps/browser/src/platform/services/browser-messaging-private-mode-popup.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; - -export default class BrowserMessagingPrivateModePopupService implements MessagingService { - send(subscriber: string, arg: any = {}) { - const message = Object.assign({}, { command: subscriber }, arg); - (self as any).bitwardenBackgroundMessageListener(message); - } -} diff --git a/apps/browser/src/platform/services/browser-messaging.service.ts b/apps/browser/src/platform/services/browser-messaging.service.ts deleted file mode 100644 index 5eff957cb5..0000000000 --- a/apps/browser/src/platform/services/browser-messaging.service.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; - -import { BrowserApi } from "../browser/browser-api"; - -export default class BrowserMessagingService implements MessagingService { - send(subscriber: string, arg: any = {}) { - return BrowserApi.sendMessage(subscriber, arg); - } -} diff --git a/apps/browser/src/platform/services/browser-script-injector.service.spec.ts b/apps/browser/src/platform/services/browser-script-injector.service.spec.ts new file mode 100644 index 0000000000..6ae84c6464 --- /dev/null +++ b/apps/browser/src/platform/services/browser-script-injector.service.spec.ts @@ -0,0 +1,173 @@ +import { BrowserApi } from "../browser/browser-api"; + +import { + CommonScriptInjectionDetails, + Mv3ScriptInjectionDetails, +} from "./abstractions/script-injector.service"; +import { BrowserScriptInjectorService } from "./browser-script-injector.service"; + +describe("ScriptInjectorService", () => { + const tabId = 1; + const combinedManifestVersionFile = "content/autofill-init.js"; + const mv2SpecificFile = "content/autofill-init-mv2.js"; + const mv2Details = { file: mv2SpecificFile }; + const mv3SpecificFile = "content/autofill-init-mv3.js"; + const mv3Details: Mv3ScriptInjectionDetails = { file: mv3SpecificFile, world: "MAIN" }; + const sharedInjectDetails: CommonScriptInjectionDetails = { + runAt: "document_start", + }; + const manifestVersionSpy = jest.spyOn(BrowserApi, "manifestVersion", "get"); + let scriptInjectorService: BrowserScriptInjectorService; + jest.spyOn(BrowserApi, "executeScriptInTab").mockImplementation(); + jest.spyOn(BrowserApi, "isManifestVersion"); + + beforeEach(() => { + scriptInjectorService = new BrowserScriptInjectorService(); + }); + + describe("inject", () => { + describe("injection of a single script that functions in both manifest v2 and v3", () => { + it("injects the script in manifest v2 when given combined injection details", async () => { + manifestVersionSpy.mockReturnValue(2); + + await scriptInjectorService.inject({ + tabId, + injectDetails: { + file: combinedManifestVersionFile, + frame: "all_frames", + ...sharedInjectDetails, + }, + }); + + expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith(tabId, { + ...sharedInjectDetails, + allFrames: true, + file: combinedManifestVersionFile, + }); + }); + + it("injects the script in manifest v3 when given combined injection details", async () => { + manifestVersionSpy.mockReturnValue(3); + + await scriptInjectorService.inject({ + tabId, + injectDetails: { + file: combinedManifestVersionFile, + frame: 10, + ...sharedInjectDetails, + }, + }); + + expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith( + tabId, + { ...sharedInjectDetails, frameId: 10, file: combinedManifestVersionFile }, + { world: "ISOLATED" }, + ); + }); + }); + + describe("injection of mv2 specific details", () => { + describe("given the extension is running manifest v2", () => { + it("injects the mv2 script injection details file", async () => { + manifestVersionSpy.mockReturnValue(2); + + await scriptInjectorService.inject({ + mv2Details, + tabId, + injectDetails: sharedInjectDetails, + }); + + expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith(tabId, { + ...sharedInjectDetails, + frameId: 0, + file: mv2SpecificFile, + }); + }); + }); + + describe("given the extension is running manifest v3", () => { + it("injects the common script injection details file", async () => { + manifestVersionSpy.mockReturnValue(3); + + await scriptInjectorService.inject({ + mv2Details, + tabId, + injectDetails: { ...sharedInjectDetails, file: combinedManifestVersionFile }, + }); + + expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith( + tabId, + { + ...sharedInjectDetails, + frameId: 0, + file: combinedManifestVersionFile, + }, + { world: "ISOLATED" }, + ); + }); + + it("throws an error if no common script injection details file is specified", async () => { + manifestVersionSpy.mockReturnValue(3); + + await expect( + scriptInjectorService.inject({ + mv2Details, + tabId, + injectDetails: { ...sharedInjectDetails, file: null }, + }), + ).rejects.toThrow("No file specified for script injection"); + }); + }); + }); + + describe("injection of mv3 specific details", () => { + describe("given the extension is running manifest v3", () => { + it("injects the mv3 script injection details file", async () => { + manifestVersionSpy.mockReturnValue(3); + + await scriptInjectorService.inject({ + mv3Details, + tabId, + injectDetails: sharedInjectDetails, + }); + + expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith( + tabId, + { ...sharedInjectDetails, frameId: 0, file: mv3SpecificFile }, + { world: "MAIN" }, + ); + }); + }); + + describe("given the extension is running manifest v2", () => { + it("injects the common script injection details file", async () => { + manifestVersionSpy.mockReturnValue(2); + + await scriptInjectorService.inject({ + mv3Details, + tabId, + injectDetails: { ...sharedInjectDetails, file: combinedManifestVersionFile }, + }); + + expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith(tabId, { + ...sharedInjectDetails, + frameId: 0, + file: combinedManifestVersionFile, + }); + }); + + it("throws an error if no common script injection details file is specified", async () => { + manifestVersionSpy.mockReturnValue(2); + + await expect( + scriptInjectorService.inject({ + mv3Details, + tabId, + injectDetails: { ...sharedInjectDetails, file: "" }, + }), + ).rejects.toThrow("No file specified for script injection"); + }); + }); + }); + }); +}); diff --git a/apps/browser/src/platform/services/browser-script-injector.service.ts b/apps/browser/src/platform/services/browser-script-injector.service.ts new file mode 100644 index 0000000000..54513188d5 --- /dev/null +++ b/apps/browser/src/platform/services/browser-script-injector.service.ts @@ -0,0 +1,78 @@ +import { BrowserApi } from "../browser/browser-api"; + +import { + CommonScriptInjectionDetails, + ScriptInjectionConfig, + ScriptInjectorService, +} from "./abstractions/script-injector.service"; + +export class BrowserScriptInjectorService extends ScriptInjectorService { + /** + * Facilitates the injection of a script into a tab context. Will adjust + * behavior between manifest v2 and v3 based on the passed configuration. + * + * @param config - The configuration for the script injection. + */ + async inject(config: ScriptInjectionConfig): Promise { + const { tabId, injectDetails, mv3Details } = config; + const file = this.getScriptFile(config); + if (!file) { + throw new Error("No file specified for script injection"); + } + + const injectionDetails = this.buildInjectionDetails(injectDetails, file); + + if (BrowserApi.isManifestVersion(3)) { + await BrowserApi.executeScriptInTab(tabId, injectionDetails, { + world: mv3Details?.world ?? "ISOLATED", + }); + + return; + } + + await BrowserApi.executeScriptInTab(tabId, injectionDetails); + } + + /** + * Retrieves the script file to inject based on the configuration. + * + * @param config - The configuration for the script injection. + */ + private getScriptFile(config: ScriptInjectionConfig): string { + const { injectDetails, mv2Details, mv3Details } = config; + + if (BrowserApi.isManifestVersion(3)) { + return mv3Details?.file ?? injectDetails?.file; + } + + return mv2Details?.file ?? injectDetails?.file; + } + + /** + * Builds the injection details for the script injection. + * + * @param injectDetails - The details for the script injection. + * @param file - The file to inject. + */ + private buildInjectionDetails( + injectDetails: CommonScriptInjectionDetails, + file: string, + ): chrome.tabs.InjectDetails { + const { frame, runAt } = injectDetails; + const injectionDetails: chrome.tabs.InjectDetails = { file }; + + if (runAt) { + injectionDetails.runAt = runAt; + } + + if (!frame) { + return { ...injectionDetails, frameId: 0 }; + } + + if (frame !== "all_frames") { + return { ...injectionDetails, frameId: frame }; + } + + return { ...injectionDetails, allFrames: true }; + } +} diff --git a/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts b/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts index 7740a22071..a4581e6ac1 100644 --- a/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts +++ b/apps/browser/src/platform/services/local-backed-session-storage.service.spec.ts @@ -2,6 +2,8 @@ import { mock, MockProxy } from "jest-mock-extended"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { AbstractMemoryStorageService, AbstractStorageService, @@ -11,16 +13,26 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { BrowserApi } from "../browser/browser-api"; + import { LocalBackedSessionStorageService } from "./local-backed-session-storage.service"; -describe("LocalBackedSessionStorage", () => { +describe.skip("LocalBackedSessionStorage", () => { + const sendMessageWithResponseSpy: jest.SpyInstance = jest.spyOn( + BrowserApi, + "sendMessageWithResponse", + ); + let encryptService: MockProxy; let keyGenerationService: MockProxy; let localStorageService: MockProxy; let sessionStorageService: MockProxy; + let logService: MockProxy; + let platformUtilsService: MockProxy; - let cache: Map; + let cache: Record; const testObj = { a: 1, b: 2 }; + const stringifiedTestObj = JSON.stringify(testObj); const key = new SymmetricCryptoKey(Utils.fromUtf8ToArray("00000000000000000000000000000000")); let getSessionKeySpy: jest.SpyInstance; @@ -40,20 +52,24 @@ describe("LocalBackedSessionStorage", () => { }; beforeEach(() => { + sendMessageWithResponseSpy.mockResolvedValue(null); + logService = mock(); encryptService = mock(); keyGenerationService = mock(); localStorageService = mock(); sessionStorageService = mock(); sut = new LocalBackedSessionStorageService( + logService, encryptService, keyGenerationService, localStorageService, sessionStorageService, + platformUtilsService, "test", ); - cache = sut["cache"]; + cache = sut["cachedSession"]; keyGenerationService.createKeyWithPurpose.mockResolvedValue({ derivedKey: key, @@ -64,19 +80,27 @@ describe("LocalBackedSessionStorage", () => { getSessionKeySpy = jest.spyOn(sut, "getSessionEncKey"); getSessionKeySpy.mockResolvedValue(key); - sendUpdateSpy = jest.spyOn(sut, "sendUpdate"); - sendUpdateSpy.mockReturnValue(); + // sendUpdateSpy = jest.spyOn(sut, "sendUpdate"); + // sendUpdateSpy.mockReturnValue(); }); describe("get", () => { - it("should return from cache", async () => { - cache.set("test", testObj); - const result = await sut.get("test"); - expect(result).toStrictEqual(testObj); + describe("in local cache or external context cache", () => { + it("should return from local cache", async () => { + cache["test"] = stringifiedTestObj; + const result = await sut.get("test"); + expect(result).toStrictEqual(testObj); + }); + + it("should return from external context cache when local cache is not available", async () => { + sendMessageWithResponseSpy.mockResolvedValue(stringifiedTestObj); + const result = await sut.get("test"); + expect(result).toStrictEqual(testObj); + }); }); describe("not in cache", () => { - const session = { test: testObj }; + const session = { test: stringifiedTestObj }; beforeEach(() => { mockExistingSessionKey(key); @@ -117,8 +141,8 @@ describe("LocalBackedSessionStorage", () => { it("should set retrieved values in cache", async () => { await sut.get("test"); - expect(cache.has("test")).toBe(true); - expect(cache.get("test")).toEqual(session.test); + expect(cache["test"]).toBeTruthy(); + expect(cache["test"]).toEqual(session.test); }); it("should use a deserializer if provided", async () => { @@ -148,13 +172,56 @@ describe("LocalBackedSessionStorage", () => { }); describe("remove", () => { + describe("existing cache value is null", () => { + it("should not save null if the local cached value is already null", async () => { + cache["test"] = null; + await sut.remove("test"); + expect(sendUpdateSpy).not.toHaveBeenCalled(); + }); + + it("should not save null if the externally cached value is already null", async () => { + sendMessageWithResponseSpy.mockResolvedValue(null); + await sut.remove("test"); + expect(sendUpdateSpy).not.toHaveBeenCalled(); + }); + }); + it("should save null", async () => { + cache["test"] = stringifiedTestObj; + await sut.remove("test"); expect(sendUpdateSpy).toHaveBeenCalledWith({ key: "test", updateType: "remove" }); }); }); describe("save", () => { + describe("currently cached", () => { + it("does not save the value a local cached value exists which is an exact match", async () => { + cache["test"] = stringifiedTestObj; + await sut.save("test", testObj); + expect(sendUpdateSpy).not.toHaveBeenCalled(); + }); + + it("does not save the value if a local cached value exists, even if the keys not in the same order", async () => { + cache["test"] = JSON.stringify({ b: 2, a: 1 }); + await sut.save("test", testObj); + expect(sendUpdateSpy).not.toHaveBeenCalled(); + }); + + it("does not save the value a externally cached value exists which is an exact match", async () => { + sendMessageWithResponseSpy.mockResolvedValue(stringifiedTestObj); + await sut.save("test", testObj); + expect(sendUpdateSpy).not.toHaveBeenCalled(); + expect(cache["test"]).toBe(stringifiedTestObj); + }); + + it("saves the value if the currently cached string value evaluates to a falsy value", async () => { + cache["test"] = "null"; + await sut.save("test", testObj); + expect(sendUpdateSpy).toHaveBeenCalledWith({ key: "test", updateType: "save" }); + }); + }); + describe("caching", () => { beforeEach(() => { localStorageService.get.mockResolvedValue(null); @@ -167,21 +234,21 @@ describe("LocalBackedSessionStorage", () => { }); it("should remove key from cache if value is null", async () => { - cache.set("test", {}); - const cacheSetSpy = jest.spyOn(cache, "set"); - expect(cache.has("test")).toBe(true); + cache["test"] = {}; + // const cacheSetSpy = jest.spyOn(cache, "set"); + expect(cache["test"]).toBe(true); await sut.save("test", null); // Don't remove from cache, just replace with null - expect(cache.get("test")).toBe(null); - expect(cacheSetSpy).toHaveBeenCalledWith("test", null); + expect(cache["test"]).toBe(null); + // expect(cacheSetSpy).toHaveBeenCalledWith("test", null); }); it("should set cache if value is non-null", async () => { - expect(cache.has("test")).toBe(false); - const setSpy = jest.spyOn(cache, "set"); + expect(cache["test"]).toBe(false); + // const setSpy = jest.spyOn(cache, "set"); await sut.save("test", testObj); - expect(cache.get("test")).toBe(testObj); - expect(setSpy).toHaveBeenCalledWith("test", testObj); + expect(cache["test"]).toBe(stringifiedTestObj); + // expect(setSpy).toHaveBeenCalledWith("test", stringifiedTestObj); }); }); diff --git a/apps/browser/src/platform/services/local-backed-session-storage.service.ts b/apps/browser/src/platform/services/local-backed-session-storage.service.ts index 3f01e4169e..146eb11b2b 100644 --- a/apps/browser/src/platform/services/local-backed-session-storage.service.ts +++ b/apps/browser/src/platform/services/local-backed-session-storage.service.ts @@ -1,8 +1,10 @@ -import { Observable, Subject, filter, map, merge, share, tap } from "rxjs"; +import { Subject } from "rxjs"; import { Jsonify } from "type-fest"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { AbstractMemoryStorageService, AbstractStorageService, @@ -13,57 +15,77 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { MemoryStorageOptions } from "@bitwarden/common/platform/models/domain/storage-options"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { fromChromeEvent } from "../browser/from-chrome-event"; +import { BrowserApi } from "../browser/browser-api"; import { devFlag } from "../decorators/dev-flag.decorator"; import { devFlagEnabled } from "../flags"; +import { MemoryStoragePortMessage } from "../storage/port-messages"; +import { portName } from "../storage/port-name"; export class LocalBackedSessionStorageService extends AbstractMemoryStorageService implements ObservableStorageService { - private cache = new Map(); private updatesSubject = new Subject(); - - private commandName = `localBackedSessionStorage_${this.name}`; - private encKey = `localEncryptionKey_${this.name}`; - private sessionKey = `session_${this.name}`; - - updates$: Observable; + private commandName = `localBackedSessionStorage_${this.partitionName}`; + private encKey = `localEncryptionKey_${this.partitionName}`; + private sessionKey = `session_${this.partitionName}`; + private cachedSession: Record = {}; + private _ports: Set = new Set([]); + private knownNullishCacheKeys: Set = new Set([]); constructor( + private logService: LogService, private encryptService: EncryptService, private keyGenerationService: KeyGenerationService, private localStorage: AbstractStorageService, private sessionStorage: AbstractStorageService, - private name: string, + private platformUtilsService: PlatformUtilsService, + private partitionName: string, ) { super(); - const remoteObservable = fromChromeEvent(chrome.runtime.onMessage).pipe( - filter(([msg]) => msg.command === this.commandName), - map(([msg]) => msg.update as StorageUpdate), - tap((update) => { - if (update.updateType === "remove") { - this.cache.set(update.key, null); - } else { - this.cache.delete(update.key); - } - }), - share(), - ); + BrowserApi.addListener(chrome.runtime.onConnect, (port) => { + if (port.name !== `${portName(chrome.storage.session)}_${partitionName}`) { + return; + } - remoteObservable.subscribe(); + this._ports.add(port); - this.updates$ = merge(this.updatesSubject.asObservable(), remoteObservable); + const listenerCallback = this.onMessageFromForeground.bind(this); + port.onDisconnect.addListener(() => { + this._ports.delete(port); + port.onMessage.removeListener(listenerCallback); + }); + port.onMessage.addListener(listenerCallback); + // Initialize the new memory storage service with existing data + this.sendMessageTo(port, { + action: "initialization", + data: Array.from(Object.keys(this.cachedSession)), + }); + }); + this.updates$.subscribe((update) => { + this.broadcastMessage({ + action: "subject_update", + data: update, + }); + }); } get valuesRequireDeserialization(): boolean { return true; } + get updates$() { + return this.updatesSubject.asObservable(); + } + async get(key: string, options?: MemoryStorageOptions): Promise { - if (this.cache.has(key)) { - return this.cache.get(key) as T; + if (this.cachedSession[key] != null) { + return this.cachedSession[key] as T; + } + + if (this.knownNullishCacheKeys.has(key)) { + return null; } return await this.getBypassCache(key, options); @@ -71,7 +93,8 @@ export class LocalBackedSessionStorageService async getBypassCache(key: string, options?: MemoryStorageOptions): Promise { const session = await this.getLocalSession(await this.getSessionEncKey()); - if (session == null || !Object.keys(session).includes(key)) { + if (session[key] == null) { + this.knownNullishCacheKeys.add(key); return null; } @@ -80,8 +103,8 @@ export class LocalBackedSessionStorageService value = options.deserializer(value as Jsonify); } - this.cache.set(key, value); - return this.cache.get(key) as T; + void this.save(key, value); + return value as T; } async has(key: string): Promise { @@ -89,41 +112,48 @@ export class LocalBackedSessionStorageService } async save(key: string, obj: T): Promise { + // This is for observation purposes only. At some point, we don't want to write to local session storage if the value is the same. + if (this.platformUtilsService.isDev()) { + const existingValue = this.cachedSession[key] as T; + if (this.compareValues(existingValue, obj)) { + this.logService.warning(`Possible unnecessary write to local session storage. Key: ${key}`); + this.logService.warning(obj as any); + } + } + if (obj == null) { return await this.remove(key); } - this.cache.set(key, obj); + this.knownNullishCacheKeys.delete(key); + this.cachedSession[key] = obj; await this.updateLocalSessionValue(key, obj); - this.sendUpdate({ key, updateType: "save" }); + this.updatesSubject.next({ key, updateType: "save" }); } async remove(key: string): Promise { - this.cache.set(key, null); + this.knownNullishCacheKeys.add(key); + delete this.cachedSession[key]; await this.updateLocalSessionValue(key, null); - this.sendUpdate({ key, updateType: "remove" }); - } - - sendUpdate(storageUpdate: StorageUpdate) { - this.updatesSubject.next(storageUpdate); - void chrome.runtime.sendMessage({ - command: this.commandName, - update: storageUpdate, - }); + this.updatesSubject.next({ key, updateType: "remove" }); } private async updateLocalSessionValue(key: string, obj: T) { const sessionEncKey = await this.getSessionEncKey(); const localSession = (await this.getLocalSession(sessionEncKey)) ?? {}; localSession[key] = obj; - await this.setLocalSession(localSession, sessionEncKey); + void this.setLocalSession(localSession, sessionEncKey); } async getLocalSession(encKey: SymmetricCryptoKey): Promise> { - const local = await this.localStorage.get(this.sessionKey); + if (Object.keys(this.cachedSession).length > 0) { + return this.cachedSession; + } + this.cachedSession = {}; + const local = await this.localStorage.get(this.sessionKey); if (local == null) { - return null; + return this.cachedSession; } if (devFlagEnabled("storeSessionDecrypted")) { @@ -135,9 +165,11 @@ export class LocalBackedSessionStorageService // Error with decryption -- session is lost, delete state and key and start over await this.setSessionEncKey(null); await this.localStorage.remove(this.sessionKey); - return null; + return this.cachedSession; } - return JSON.parse(sessionJson); + + this.cachedSession = JSON.parse(sessionJson); + return this.cachedSession; } async setLocalSession(session: Record, key: SymmetricCryptoKey) { @@ -192,4 +224,76 @@ export class LocalBackedSessionStorageService await this.sessionStorage.save(this.encKey, input); } } + + private compareValues(value1: T, value2: T): boolean { + if (value1 == null && value2 == null) { + return true; + } + + if (value1 && value2 == null) { + return false; + } + + if (value1 == null && value2) { + return false; + } + + if (typeof value1 !== "object" || typeof value2 !== "object") { + return value1 === value2; + } + + if (JSON.stringify(value1) === JSON.stringify(value2)) { + return true; + } + + return Object.entries(value1).sort().toString() === Object.entries(value2).sort().toString(); + } + + private async onMessageFromForeground( + message: MemoryStoragePortMessage, + port: chrome.runtime.Port, + ) { + if (message.originator === "background") { + return; + } + + let result: unknown = null; + + switch (message.action) { + case "get": + case "getBypassCache": + case "has": { + result = await this[message.action](message.key); + break; + } + case "save": + await this.save(message.key, JSON.parse((message.data as string) ?? null) as unknown); + break; + case "remove": + await this.remove(message.key); + break; + } + + this.sendMessageTo(port, { + id: message.id, + key: message.key, + data: JSON.stringify(result), + }); + } + + protected broadcastMessage(data: Omit) { + this._ports.forEach((port) => { + this.sendMessageTo(port, data); + }); + } + + private sendMessageTo( + port: chrome.runtime.Port, + data: Omit, + ) { + port.postMessage({ + ...data, + originator: "background", + }); + } } diff --git a/apps/browser/src/platform/services/platform-utils/foreground-platform-utils.service.ts b/apps/browser/src/platform/services/platform-utils/foreground-platform-utils.service.ts index 8cf1a8d3e4..24aa45d5c3 100644 --- a/apps/browser/src/platform/services/platform-utils/foreground-platform-utils.service.ts +++ b/apps/browser/src/platform/services/platform-utils/foreground-platform-utils.service.ts @@ -1,13 +1,10 @@ -import { SecurityContext } from "@angular/core"; -import { DomSanitizer } from "@angular/platform-browser"; -import { ToastrService } from "ngx-toastr"; +import { ToastService } from "@bitwarden/components"; import { BrowserPlatformUtilsService } from "./browser-platform-utils.service"; export class ForegroundPlatformUtilsService extends BrowserPlatformUtilsService { constructor( - private sanitizer: DomSanitizer, - private toastrService: ToastrService, + private toastService: ToastService, clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void, biometricCallback: () => Promise, win: Window & typeof globalThis, @@ -21,20 +18,6 @@ export class ForegroundPlatformUtilsService extends BrowserPlatformUtilsService text: string | string[], options?: any, ): void { - if (typeof text === "string") { - // Already in the correct format - } else if (text.length === 1) { - text = text[0]; - } else { - let message = ""; - text.forEach( - (t: string) => - (message += "

" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "

"), - ); - text = message; - options.enableHtml = true; - } - this.toastrService.show(text, title, options, "toast-" + type); - // noop + this.toastService._showToast({ type, title, text, options }); } } diff --git a/apps/browser/src/platform/state/background-derived-state.provider.ts b/apps/browser/src/platform/state/background-derived-state.provider.ts index 95eec71113..f3d217789e 100644 --- a/apps/browser/src/platform/state/background-derived-state.provider.ts +++ b/apps/browser/src/platform/state/background-derived-state.provider.ts @@ -1,5 +1,9 @@ import { Observable } from "rxjs"; +import { + AbstractStorageService, + ObservableStorageService, +} from "@bitwarden/common/platform/abstractions/storage.service"; import { DeriveDefinition, DerivedState } from "@bitwarden/common/platform/state"; // eslint-disable-next-line import/no-restricted-paths -- extending this class for this client import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider"; @@ -12,11 +16,14 @@ export class BackgroundDerivedStateProvider extends DefaultDerivedStateProvider parentState$: Observable, deriveDefinition: DeriveDefinition, dependencies: TDeps, + storageLocation: [string, AbstractStorageService & ObservableStorageService], ): DerivedState { + const [cacheKey, storageService] = storageLocation; return new BackgroundDerivedState( parentState$, deriveDefinition, - this.memoryStorage, + storageService, + cacheKey, dependencies, ); } diff --git a/apps/browser/src/platform/state/background-derived-state.ts b/apps/browser/src/platform/state/background-derived-state.ts index 7a7146aa88..c62795acdc 100644 --- a/apps/browser/src/platform/state/background-derived-state.ts +++ b/apps/browser/src/platform/state/background-derived-state.ts @@ -23,10 +23,10 @@ export class BackgroundDerivedState< parentState$: Observable, deriveDefinition: DeriveDefinition, memoryStorage: AbstractStorageService & ObservableStorageService, + portName: string, dependencies: TDeps, ) { super(parentState$, deriveDefinition, memoryStorage, dependencies); - const portName = deriveDefinition.buildCacheKey(); // listen for foreground derived states to connect BrowserApi.addListener(chrome.runtime.onConnect, (port) => { diff --git a/apps/browser/src/platform/state/derived-state-interactions.spec.ts b/apps/browser/src/platform/state/derived-state-interactions.spec.ts index d709c401af..a5df01bc98 100644 --- a/apps/browser/src/platform/state/derived-state-interactions.spec.ts +++ b/apps/browser/src/platform/state/derived-state-interactions.spec.ts @@ -38,14 +38,21 @@ describe("foreground background derived state interactions", () => { let memoryStorage: FakeStorageService; const initialParent = "2020-01-01"; const ngZone = mock(); + const portName = "testPort"; beforeEach(() => { mockPorts(); parentState$ = new Subject(); memoryStorage = new FakeStorageService(); - background = new BackgroundDerivedState(parentState$, deriveDefinition, memoryStorage, {}); - foreground = new ForegroundDerivedState(deriveDefinition, memoryStorage, ngZone); + background = new BackgroundDerivedState( + parentState$, + deriveDefinition, + memoryStorage, + portName, + {}, + ); + foreground = new ForegroundDerivedState(deriveDefinition, memoryStorage, portName, ngZone); }); afterEach(() => { @@ -65,7 +72,12 @@ describe("foreground background derived state interactions", () => { }); it("should initialize a late-connected foreground", async () => { - const newForeground = new ForegroundDerivedState(deriveDefinition, memoryStorage, ngZone); + const newForeground = new ForegroundDerivedState( + deriveDefinition, + memoryStorage, + portName, + ngZone, + ); const backgroundEmissions = trackEmissions(background.state$); parentState$.next(initialParent); await awaitAsync(); @@ -82,8 +94,6 @@ describe("foreground background derived state interactions", () => { const dateString = "2020-12-12"; const emissions = trackEmissions(background.state$); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises await foreground.forceValue(new Date(dateString)); await awaitAsync(); @@ -99,9 +109,7 @@ describe("foreground background derived state interactions", () => { expect(foreground["port"]).toBeDefined(); const newDate = new Date(); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - foreground.forceValue(newDate); + await foreground.forceValue(newDate); await awaitAsync(); expect(connectMock.mock.calls.length).toBe(initialConnectCalls); @@ -114,9 +122,7 @@ describe("foreground background derived state interactions", () => { expect(foreground["port"]).toBeUndefined(); const newDate = new Date(); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - foreground.forceValue(newDate); + await foreground.forceValue(newDate); await awaitAsync(); expect(connectMock.mock.calls.length).toBe(initialConnectCalls + 1); diff --git a/apps/browser/src/platform/state/foreground-derived-state.provider.ts b/apps/browser/src/platform/state/foreground-derived-state.provider.ts index ccefb1157c..d9262e3b6e 100644 --- a/apps/browser/src/platform/state/foreground-derived-state.provider.ts +++ b/apps/browser/src/platform/state/foreground-derived-state.provider.ts @@ -5,6 +5,7 @@ import { AbstractStorageService, ObservableStorageService, } from "@bitwarden/common/platform/abstractions/storage.service"; +import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { DeriveDefinition, DerivedState } from "@bitwarden/common/platform/state"; // eslint-disable-next-line import/no-restricted-paths -- extending this class for this client import { DefaultDerivedStateProvider } from "@bitwarden/common/platform/state/implementations/default-derived-state.provider"; @@ -14,16 +15,18 @@ import { ForegroundDerivedState } from "./foreground-derived-state"; export class ForegroundDerivedStateProvider extends DefaultDerivedStateProvider { constructor( - memoryStorage: AbstractStorageService & ObservableStorageService, + storageServiceProvider: StorageServiceProvider, private ngZone: NgZone, ) { - super(memoryStorage); + super(storageServiceProvider); } override buildDerivedState( _parentState$: Observable, deriveDefinition: DeriveDefinition, _dependencies: TDeps, + storageLocation: [string, AbstractStorageService & ObservableStorageService], ): DerivedState { - return new ForegroundDerivedState(deriveDefinition, this.memoryStorage, this.ngZone); + const [cacheKey, storageService] = storageLocation; + return new ForegroundDerivedState(deriveDefinition, storageService, cacheKey, this.ngZone); } } diff --git a/apps/browser/src/platform/state/foreground-derived-state.spec.ts b/apps/browser/src/platform/state/foreground-derived-state.spec.ts index fce672a5ef..2c29f39bc1 100644 --- a/apps/browser/src/platform/state/foreground-derived-state.spec.ts +++ b/apps/browser/src/platform/state/foreground-derived-state.spec.ts @@ -33,13 +33,14 @@ jest.mock("../browser/run-inside-angular.operator", () => { describe("ForegroundDerivedState", () => { let sut: ForegroundDerivedState; let memoryStorage: FakeStorageService; + const portName = "testPort"; const ngZone = mock(); beforeEach(() => { memoryStorage = new FakeStorageService(); memoryStorage.internalUpdateValuesRequireDeserialization(true); mockPorts(); - sut = new ForegroundDerivedState(deriveDefinition, memoryStorage, ngZone); + sut = new ForegroundDerivedState(deriveDefinition, memoryStorage, portName, ngZone); }); afterEach(() => { diff --git a/apps/browser/src/platform/state/foreground-derived-state.ts b/apps/browser/src/platform/state/foreground-derived-state.ts index b005697be8..b9dda763df 100644 --- a/apps/browser/src/platform/state/foreground-derived-state.ts +++ b/apps/browser/src/platform/state/foreground-derived-state.ts @@ -35,6 +35,7 @@ export class ForegroundDerivedState implements DerivedState { constructor( private deriveDefinition: DeriveDefinition, private memoryStorage: AbstractStorageService & ObservableStorageService, + private portName: string, private ngZone: NgZone, ) { this.storageKey = deriveDefinition.storageKey; @@ -88,7 +89,7 @@ export class ForegroundDerivedState implements DerivedState { return; } - this.port = chrome.runtime.connect({ name: this.deriveDefinition.buildCacheKey() }); + this.port = chrome.runtime.connect({ name: this.portName }); this.backgroundResponses$ = fromChromeEvent(this.port.onMessage).pipe( map(([message]) => message as DerivedStateMessage), diff --git a/apps/browser/src/platform/storage/browser-storage-service.provider.ts b/apps/browser/src/platform/storage/browser-storage-service.provider.ts new file mode 100644 index 0000000000..e0214baef4 --- /dev/null +++ b/apps/browser/src/platform/storage/browser-storage-service.provider.ts @@ -0,0 +1,35 @@ +import { + AbstractStorageService, + ObservableStorageService, +} from "@bitwarden/common/platform/abstractions/storage.service"; +import { + PossibleLocation, + StorageServiceProvider, +} from "@bitwarden/common/platform/services/storage-service.provider"; +// eslint-disable-next-line import/no-restricted-paths +import { ClientLocations } from "@bitwarden/common/platform/state/state-definition"; + +export class BrowserStorageServiceProvider extends StorageServiceProvider { + constructor( + diskStorageService: AbstractStorageService & ObservableStorageService, + limitedMemoryStorageService: AbstractStorageService & ObservableStorageService, + private largeObjectMemoryStorageService: AbstractStorageService & ObservableStorageService, + ) { + super(diskStorageService, limitedMemoryStorageService); + } + + override get( + defaultLocation: PossibleLocation, + overrides: Partial, + ): [location: PossibleLocation, service: AbstractStorageService & ObservableStorageService] { + const location = overrides["browser"] ?? defaultLocation; + switch (location) { + case "memory-large-object": + return ["memory-large-object", this.largeObjectMemoryStorageService]; + default: + // Pass in computed location to super because they could have + // override default "disk" with web "memory". + return super.get(location, overrides); + } + } +} diff --git a/apps/browser/src/platform/storage/foreground-memory-storage.service.ts b/apps/browser/src/platform/storage/foreground-memory-storage.service.ts index 1e5220002a..b3ac8de55e 100644 --- a/apps/browser/src/platform/storage/foreground-memory-storage.service.ts +++ b/apps/browser/src/platform/storage/foreground-memory-storage.service.ts @@ -21,12 +21,16 @@ export class ForegroundMemoryStorageService extends AbstractMemoryStorageService } updates$; - constructor() { + constructor(private partitionName?: string) { super(); this.updates$ = this.updatesSubject.asObservable(); - this._port = chrome.runtime.connect({ name: portName(chrome.storage.session) }); + let name = portName(chrome.storage.session); + if (this.partitionName) { + name = `${name}_${this.partitionName}`; + } + this._port = chrome.runtime.connect({ name }); this._backgroundResponses$ = fromChromeEvent(this._port.onMessage).pipe( map(([message]) => message), filter((message) => message.originator === "background"), diff --git a/apps/browser/src/platform/utils/from-chrome-runtime-messaging.ts b/apps/browser/src/platform/utils/from-chrome-runtime-messaging.ts new file mode 100644 index 0000000000..e30f35b680 --- /dev/null +++ b/apps/browser/src/platform/utils/from-chrome-runtime-messaging.ts @@ -0,0 +1,26 @@ +import { map, share } from "rxjs"; + +import { tagAsExternal } from "@bitwarden/common/platform/messaging/internal"; + +import { fromChromeEvent } from "../browser/from-chrome-event"; + +/** + * Creates an observable that listens to messages through `chrome.runtime.onMessage`. + * @returns An observable stream of messages. + */ +export const fromChromeRuntimeMessaging = () => { + return fromChromeEvent(chrome.runtime.onMessage).pipe( + map(([message, sender]) => { + message ??= {}; + + // Force the sender onto the message as long as we won't overwrite anything + if (!("webExtSender" in message)) { + message.webExtSender = sender; + } + + return message; + }), + tagAsExternal, + share(), + ); +}; diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index b0fdaec4fc..7acaf1ba93 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -1,18 +1,17 @@ import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core"; import { NavigationEnd, Router, RouterOutlet } from "@angular/router"; -import { ToastrService } from "ngx-toastr"; -import { filter, concatMap, Subject, takeUntil, firstValueFrom, map } from "rxjs"; +import { filter, concatMap, Subject, takeUntil, firstValueFrom, tap, map } from "rxjs"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { DialogService, SimpleDialogOptions } from "@bitwarden/components"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { MessageListener } from "@bitwarden/common/platform/messaging"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { DialogService, SimpleDialogOptions, ToastService } from "@bitwarden/components"; import { BrowserApi } from "../platform/browser/browser-api"; -import { ZonedMessageListenerService } from "../platform/browser/zoned-message-listener.service"; import { BrowserStateService } from "../platform/services/abstractions/browser-state.service"; -import { ForegroundPlatformUtilsService } from "../platform/services/platform-utils/foreground-platform-utils.service"; import { BrowserSendStateService } from "../tools/popup/services/browser-send-state.service"; import { VaultBrowserStateService } from "../vault/services/vault-browser-state.service"; @@ -34,19 +33,19 @@ export class AppComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); constructor( - private toastrService: ToastrService, - private broadcasterService: BroadcasterService, private authService: AuthService, private i18nService: I18nService, private router: Router, private stateService: BrowserStateService, private browserSendStateService: BrowserSendStateService, private vaultBrowserStateService: VaultBrowserStateService, + private cipherService: CipherService, private changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone, - private platformUtilsService: ForegroundPlatformUtilsService, + private platformUtilsService: PlatformUtilsService, private dialogService: DialogService, - private browserMessagingApi: ZonedMessageListenerService, + private messageListener: MessageListener, + private toastService: ToastService, ) {} async ngOnInit() { @@ -77,77 +76,76 @@ export class AppComponent implements OnInit, OnDestroy { window.onkeypress = () => this.recordActivity(); }); - const bitwardenPopupMainMessageListener = (msg: any, sender: any) => { - if (msg.command === "doneLoggingOut") { - this.authService.logOut(async () => { - if (msg.expired) { - this.showToast({ - type: "warning", - title: this.i18nService.t("loggedOut"), - text: this.i18nService.t("loginExpired"), + this.messageListener.allMessages$ + .pipe( + tap((msg: any) => { + if (msg.command === "doneLoggingOut") { + this.authService.logOut(async () => { + if (msg.expired) { + this.toastService.showToast({ + variant: "warning", + title: this.i18nService.t("loggedOut"), + message: this.i18nService.t("loginExpired"), + }); + } + + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.router.navigate(["home"]); }); + this.changeDetectorRef.detectChanges(); + } else if (msg.command === "authBlocked") { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.router.navigate(["home"]); + } else if ( + msg.command === "locked" && + (msg.userId == null || msg.userId == this.activeUserId) + ) { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.router.navigate(["lock"]); + } else if (msg.command === "showDialog") { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.showDialog(msg); + } else if (msg.command === "showNativeMessagingFinterprintDialog") { + // TODO: Should be refactored to live in another service. + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.showNativeMessagingFingerprintDialog(msg); + } else if (msg.command === "showToast") { + this.toastService._showToast(msg); + } else if (msg.command === "reloadProcess") { + const forceWindowReload = + this.platformUtilsService.isSafari() || + this.platformUtilsService.isFirefox() || + this.platformUtilsService.isOpera(); + // Wait to make sure background has reloaded first. + window.setTimeout( + () => BrowserApi.reloadExtension(forceWindowReload ? window : null), + 2000, + ); + } else if (msg.command === "reloadPopup") { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.router.navigate(["/"]); + } else if (msg.command === "convertAccountToKeyConnector") { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.router.navigate(["/remove-password"]); + } else if (msg.command === "switchAccountFinish") { + // TODO: unset loading? + // this.loading = false; + } else if (msg.command == "update-temp-password") { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.router.navigate(["/update-temp-password"]); } - - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["home"]); - }); - this.changeDetectorRef.detectChanges(); - } else if (msg.command === "authBlocked") { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["home"]); - } else if ( - msg.command === "locked" && - (msg.userId == null || msg.userId == this.activeUserId) - ) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["lock"]); - } else if (msg.command === "showDialog") { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.showDialog(msg); - } else if (msg.command === "showNativeMessagingFinterprintDialog") { - // TODO: Should be refactored to live in another service. - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.showNativeMessagingFingerprintDialog(msg); - } else if (msg.command === "showToast") { - this.showToast(msg); - } else if (msg.command === "reloadProcess") { - const forceWindowReload = - this.platformUtilsService.isSafari() || - this.platformUtilsService.isFirefox() || - this.platformUtilsService.isOpera(); - // Wait to make sure background has reloaded first. - window.setTimeout( - () => BrowserApi.reloadExtension(forceWindowReload ? window : null), - 2000, - ); - } else if (msg.command === "reloadPopup") { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/"]); - } else if (msg.command === "convertAccountToKeyConnector") { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/remove-password"]); - } else if (msg.command === "switchAccountFinish") { - // TODO: unset loading? - // this.loading = false; - } else if (msg.command == "update-temp-password") { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/update-temp-password"]); - } else { - msg.webExtSender = sender; - this.broadcasterService.send(msg); - } - }; - - (self as any).bitwardenPopupMainMessageListener = bitwardenPopupMainMessageListener; - this.browserMessagingApi.messageListener("app.component", bitwardenPopupMainMessageListener); + }), + takeUntil(this.destroy$), + ) + .subscribe(); // eslint-disable-next-line rxjs/no-async-subscribe this.router.events.pipe(takeUntil(this.destroy$)).subscribe(async (event) => { @@ -161,7 +159,7 @@ export class AppComponent implements OnInit, OnDestroy { await this.clearComponentStates(); } if (url.startsWith("/tabs/")) { - await this.stateService.setAddEditCipherInfo(null); + await this.cipherService.setAddEditCipherInfo(null); } (window as any).previousPopupUrl = url; diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 25e9358ea3..be7a984ae5 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -11,12 +11,11 @@ import { BrowserModule } from "@angular/platform-browser"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component"; -import { BitwardenToastModule } from "@bitwarden/angular/components/toastr.component"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password-count.pipe"; import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe"; import { UserVerificationDialogComponent } from "@bitwarden/auth/angular"; -import { AvatarModule, ButtonModule } from "@bitwarden/components"; +import { AvatarModule, ButtonModule, ToastModule } from "@bitwarden/components"; import { ExportScopeCalloutComponent } from "@bitwarden/vault-export-ui"; import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component"; @@ -88,7 +87,7 @@ import "../platform/popup/locales"; imports: [ A11yModule, AppRoutingModule, - BitwardenToastModule.forRoot({ + ToastModule.forRoot({ maxOpened: 2, autoDismiss: true, closeButton: true, diff --git a/apps/browser/src/popup/scss/plugins.scss b/apps/browser/src/popup/scss/plugins.scss deleted file mode 100644 index e1e386d62d..0000000000 --- a/apps/browser/src/popup/scss/plugins.scss +++ /dev/null @@ -1,98 +0,0 @@ -@import "~ngx-toastr/toastr"; - -@import "variables.scss"; -@import "buttons.scss"; - -// Toaster - -.toast-container { - .toast-close-button { - @include themify($themes) { - color: themed("toastTextColor"); - } - font-size: 18px; - margin-right: 4px; - } - - .ngx-toastr { - @include themify($themes) { - color: themed("toastTextColor"); - } - align-items: center; - background-image: none !important; - border-radius: $border-radius; - box-shadow: 0 0 8px rgba(0, 0, 0, 0.35); - display: flex; - padding: 15px; - - .toast-close-button { - position: absolute; - right: 5px; - top: 0; - } - - &:hover { - box-shadow: 0 0 10px rgba(0, 0, 0, 0.6); - } - - .icon i::before { - float: left; - font-style: normal; - font-family: $icomoon-font-family; - font-size: 25px; - line-height: 20px; - padding-right: 15px; - } - - .toast-message { - p { - margin-bottom: 0.5rem; - - &:last-child { - margin-bottom: 0; - } - } - } - - &.toast-danger, - &.toast-error { - @include themify($themes) { - background-color: themed("dangerColor"); - } - - .icon i::before { - content: map_get($icons, "error"); - } - } - - &.toast-warning { - @include themify($themes) { - background-color: themed("warningColor"); - } - - .icon i::before { - content: map_get($icons, "exclamation-triangle"); - } - } - - &.toast-info { - @include themify($themes) { - background-color: themed("infoColor"); - } - - .icon i:before { - content: map_get($icons, "info-circle"); - } - } - - &.toast-success { - @include themify($themes) { - background-color: themed("successColor"); - } - - .icon i:before { - content: map_get($icons, "check"); - } - } - } -} diff --git a/apps/browser/src/popup/scss/popup.scss b/apps/browser/src/popup/scss/popup.scss index 0d7e428138..850ef96c64 100644 --- a/apps/browser/src/popup/scss/popup.scss +++ b/apps/browser/src/popup/scss/popup.scss @@ -8,7 +8,6 @@ @import "buttons.scss"; @import "misc.scss"; @import "modal.scss"; -@import "plugins.scss"; @import "environment.scss"; @import "pages.scss"; @import "@angular/cdk/overlay-prebuilt.css"; diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index 4036ace31f..c9e6d66c2a 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -9,7 +9,6 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { BrowserApi } from "../../platform/browser/browser-api"; import BrowserPopupUtils from "../../platform/popup/browser-popup-utils"; import { BrowserStateService as StateServiceAbstraction } from "../../platform/services/abstractions/browser-state.service"; - @Injectable() export class InitService { constructor( diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 40daf1b04d..a7da6b7612 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -1,7 +1,6 @@ import { APP_INITIALIZER, NgModule, NgZone } from "@angular/core"; -import { DomSanitizer } from "@angular/platform-browser"; import { Router } from "@angular/router"; -import { ToastrService } from "ngx-toastr"; +import { Subject, merge } from "rxjs"; import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards"; import { AngularThemingService } from "@bitwarden/angular/platform/services/theming/angular-theming.service"; @@ -13,6 +12,7 @@ import { OBSERVABLE_MEMORY_STORAGE, SYSTEM_THEME_OBSERVABLE, SafeInjectionToken, + INTRAPROCESS_MESSAGING_SUBJECT, } from "@bitwarden/angular/services/injection-tokens"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; import { @@ -56,7 +56,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { @@ -65,10 +64,14 @@ import { ObservableStorageService, } from "@bitwarden/common/platform/abstractions/storage.service"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; +import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; +// eslint-disable-next-line no-restricted-imports -- Used for dependency injection +import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; +import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; import { DerivedStateProvider, @@ -83,7 +86,7 @@ import { FolderService as FolderServiceAbstraction } from "@bitwarden/common/vau import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service"; import { TotpService } from "@bitwarden/common/vault/services/totp.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { UnauthGuardService } from "../../auth/popup/services"; import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service"; @@ -91,18 +94,24 @@ import AutofillService from "../../autofill/services/autofill.service"; import MainBackground from "../../background/main.background"; import { Account } from "../../models/account"; import { BrowserApi } from "../../platform/browser/browser-api"; +import { runInsideAngular } from "../../platform/browser/run-inside-angular.operator"; +/* eslint-disable no-restricted-imports */ +import { ChromeMessageSender } from "../../platform/messaging/chrome-message.sender"; +/* eslint-enable no-restricted-imports */ import BrowserPopupUtils from "../../platform/popup/browser-popup-utils"; import { BrowserFileDownloadService } from "../../platform/popup/services/browser-file-download.service"; import { BrowserStateService as StateServiceAbstraction } from "../../platform/services/abstractions/browser-state.service"; +import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service"; import { BrowserEnvironmentService } from "../../platform/services/browser-environment.service"; import BrowserLocalStorageService from "../../platform/services/browser-local-storage.service"; -import BrowserMessagingPrivateModePopupService from "../../platform/services/browser-messaging-private-mode-popup.service"; -import BrowserMessagingService from "../../platform/services/browser-messaging.service"; +import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service"; import { DefaultBrowserStateService } from "../../platform/services/default-browser-state.service"; import I18nService from "../../platform/services/i18n.service"; import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service"; import { ForegroundDerivedStateProvider } from "../../platform/state/foreground-derived-state.provider"; +import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider"; import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service"; +import { fromChromeRuntimeMessaging } from "../../platform/utils/from-chrome-runtime-messaging"; import { BrowserSendStateService } from "../../tools/popup/services/browser-send-state.service"; import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-utils.service"; import { VaultBrowserStateService } from "../../vault/services/vault-browser-state.service"; @@ -113,6 +122,10 @@ import { InitService } from "./init.service"; import { PopupCloseWarningService } from "./popup-close-warning.service"; import { PopupSearchService } from "./popup-search.service"; +const OBSERVABLE_LARGE_OBJECT_MEMORY_STORAGE = new SafeInjectionToken< + AbstractStorageService & ObservableStorageService +>("OBSERVABLE_LARGE_OBJECT_MEMORY_STORAGE"); + const needsBackgroundInit = BrowserPopupUtils.backgroundInitializationRequired(); const isPrivateMode = BrowserPopupUtils.inPrivateMode(); const mainBackground: MainBackground = needsBackgroundInit @@ -120,7 +133,7 @@ const mainBackground: MainBackground = needsBackgroundInit : BrowserApi.getBackgroundPage().bitwardenMain; function createLocalBgService() { - const localBgService = new MainBackground(isPrivateMode); + const localBgService = new MainBackground(isPrivateMode, true); // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises localBgService.bootstrap(); @@ -155,15 +168,6 @@ const safeProviders: SafeProvider[] = [ useClass: UnauthGuardService, deps: [AuthServiceAbstraction, Router], }), - safeProvider({ - provide: MessagingService, - useFactory: () => { - return needsBackgroundInit && BrowserApi.isManifestVersion(2) - ? new BrowserMessagingPrivateModePopupService() - : new BrowserMessagingService(); - }, - deps: [], - }), safeProvider({ provide: TwoFactorService, useFactory: getBgService("twoFactorService"), @@ -257,15 +261,9 @@ const safeProviders: SafeProvider[] = [ }), safeProvider({ provide: PlatformUtilsService, - useExisting: ForegroundPlatformUtilsService, - }), - safeProvider({ - provide: ForegroundPlatformUtilsService, - useClass: ForegroundPlatformUtilsService, - useFactory: (sanitizer: DomSanitizer, toastrService: ToastrService) => { + useFactory: (toastService: ToastService) => { return new ForegroundPlatformUtilsService( - sanitizer, - toastrService, + toastService, (clipboardValue: string, clearMs: number) => { void BrowserApi.sendMessage("clearClipboard", { clipboardValue, clearMs }); }, @@ -282,7 +280,7 @@ const safeProviders: SafeProvider[] = [ window, ); }, - deps: [DomSanitizer, ToastrService], + deps: [ToastService], }), safeProvider({ provide: PasswordGenerationServiceAbstraction, @@ -319,8 +317,15 @@ const safeProviders: SafeProvider[] = [ DomainSettingsService, UserVerificationService, BillingAccountProfileStateService, + ScriptInjectorService, + AccountServiceAbstraction, ], }), + safeProvider({ + provide: ScriptInjectorService, + useClass: BrowserScriptInjectorService, + deps: [], + }), safeProvider({ provide: KeyConnectorService, useFactory: getBgService("keyConnectorService"), @@ -381,6 +386,21 @@ const safeProviders: SafeProvider[] = [ }, deps: [], }), + safeProvider({ + provide: OBSERVABLE_LARGE_OBJECT_MEMORY_STORAGE, + useFactory: ( + regularMemoryStorageService: AbstractMemoryStorageService & ObservableStorageService, + ) => { + if (BrowserApi.isManifestVersion(2)) { + return regularMemoryStorageService; + } + + return getBgService( + "largeObjectMemoryStorageForStateProviders", + )(); + }, + deps: [OBSERVABLE_MEMORY_STORAGE], + }), safeProvider({ provide: OBSERVABLE_DISK_STORAGE, useExisting: AbstractStorageService, @@ -467,7 +487,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: DerivedStateProvider, useClass: ForegroundDerivedStateProvider, - deps: [OBSERVABLE_MEMORY_STORAGE, NgZone], + deps: [StorageServiceProvider, NgZone], }), safeProvider({ provide: AutofillSettingsServiceAbstraction, @@ -484,6 +504,74 @@ const safeProviders: SafeProvider[] = [ useClass: BrowserSendStateService, deps: [StateProvider], }), + safeProvider({ + provide: MessageListener, + useFactory: (subject: Subject>, ngZone: NgZone) => + new MessageListener( + merge( + subject.asObservable(), // For messages in the same context + fromChromeRuntimeMessaging().pipe(runInsideAngular(ngZone)), // For messages in the same context + ), + ), + deps: [INTRAPROCESS_MESSAGING_SUBJECT, NgZone], + }), + safeProvider({ + provide: MessageSender, + useFactory: (subject: Subject>, logService: LogService) => + MessageSender.combine( + new SubjectMessageSender(subject), // For sending messages in the same context + new ChromeMessageSender(logService), // For sending messages to different contexts + ), + deps: [INTRAPROCESS_MESSAGING_SUBJECT, LogService], + }), + safeProvider({ + provide: INTRAPROCESS_MESSAGING_SUBJECT, + useFactory: () => { + if (BrowserPopupUtils.backgroundInitializationRequired()) { + // There is no persistent main background which means we have one in memory, + // we need the same instance that our in memory background is utilizing. + return getBgService("intraprocessMessagingSubject")(); + } else { + return new Subject>(); + } + }, + deps: [], + }), + safeProvider({ + provide: MessageSender, + useFactory: (subject: Subject>, logService: LogService) => + MessageSender.combine( + new SubjectMessageSender(subject), // For sending messages in the same context + new ChromeMessageSender(logService), // For sending messages to different contexts + ), + deps: [INTRAPROCESS_MESSAGING_SUBJECT, LogService], + }), + safeProvider({ + provide: INTRAPROCESS_MESSAGING_SUBJECT, + useFactory: () => { + if (needsBackgroundInit) { + // We will have created a popup within this context, in that case + // we want to make sure we have the same subject as that context so we + // can message with it. + return getBgService("intraprocessMessagingSubject")(); + } else { + // There isn't a locally created background so we will communicate with + // the true background through chrome apis, in that case, we can just create + // one for ourself. + return new Subject>(); + } + }, + deps: [], + }), + safeProvider({ + provide: StorageServiceProvider, + useClass: BrowserStorageServiceProvider, + deps: [ + OBSERVABLE_DISK_STORAGE, + OBSERVABLE_MEMORY_STORAGE, + OBSERVABLE_LARGE_OBJECT_MEMORY_STORAGE, + ], + }), ]; @NgModule({ diff --git a/apps/browser/src/popup/settings/settings.component.html b/apps/browser/src/popup/settings/settings.component.html index f099528918..98c218b0db 100644 --- a/apps/browser/src/popup/settings/settings.component.html +++ b/apps/browser/src/popup/settings/settings.component.html @@ -153,7 +153,7 @@ *ngIf="showChangeMasterPass" >
{{ "changeMasterPassword" | i18n }}
- + diff --git a/apps/browser/src/tools/popup/send/send-groupings.component.ts b/apps/browser/src/tools/popup/send/send-groupings.component.ts index a49773367d..87d03c4b76 100644 --- a/apps/browser/src/tools/popup/send/send-groupings.component.ts +++ b/apps/browser/src/tools/popup/send/send-groupings.component.ts @@ -29,8 +29,6 @@ const ComponentId = "SendComponent"; export class SendGroupingsComponent extends BaseSendComponent { // Header showLeftHeader = true; - // Send Type Calculations - typeCounts = new Map(); // State Handling state: BrowserSendComponentState; private loadedTimeout: number; @@ -65,7 +63,6 @@ export class SendGroupingsComponent extends BaseSendComponent { dialogService, ); super.onSuccessfulLoad = async () => { - this.calculateTypeCounts(); this.selectAll(); }; } @@ -174,17 +171,8 @@ export class SendGroupingsComponent extends BaseSendComponent { return this.hasSearched || (!this.searchPending && this.isSearchable); } - private calculateTypeCounts() { - // Create type counts - const typeCounts = new Map(); - this.sends.forEach((s) => { - if (typeCounts.has(s.type)) { - typeCounts.set(s.type, typeCounts.get(s.type) + 1); - } else { - typeCounts.set(s.type, 1); - } - }); - this.typeCounts = typeCounts; + getSendCount(sends: SendView[], type: SendType): number { + return sends.filter((s) => s.type === type).length; } private async saveState() { @@ -192,7 +180,6 @@ export class SendGroupingsComponent extends BaseSendComponent { scrollY: BrowserPopupUtils.getContentScrollY(window), searchText: this.searchText, sends: this.sends, - typeCounts: this.typeCounts, }); await this.stateService.setBrowserSendComponentState(this.state); } @@ -206,9 +193,6 @@ export class SendGroupingsComponent extends BaseSendComponent { if (this.state.sends != null) { this.sends = this.state.sends; } - if (this.state.typeCounts != null) { - this.typeCounts = this.state.typeCounts; - } return true; } diff --git a/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts b/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts index 3dafc0934a..6f0ae1455a 100644 --- a/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts +++ b/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts @@ -6,7 +6,6 @@ import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider import { awaitAsync } from "@bitwarden/common/../spec/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { UserId } from "@bitwarden/common/types/guid"; import { BrowserComponentState } from "../../../models/browserComponentState"; @@ -33,7 +32,6 @@ describe("Browser Send State Service", () => { const state = new BrowserSendComponentState(); state.scrollY = 0; state.searchText = "test"; - state.typeCounts = new Map().set(SendType.File, 1); await stateService.setBrowserSendComponentState(state); diff --git a/apps/browser/src/tools/popup/services/browser-send-state.service.ts b/apps/browser/src/tools/popup/services/browser-send-state.service.ts index b814ee5bc9..52aeb01a92 100644 --- a/apps/browser/src/tools/popup/services/browser-send-state.service.ts +++ b/apps/browser/src/tools/popup/services/browser-send-state.service.ts @@ -42,7 +42,7 @@ export class BrowserSendStateService { } /** Set the active user's browser send component state - * @param { BrowserSendComponentState } value sets the sends and type counts along with the scroll position and search text for + * @param { BrowserSendComponentState } value sets the sends along with the scroll position and search text for * the send component on the browser */ async setBrowserSendComponentState(value: BrowserSendComponentState): Promise { diff --git a/apps/browser/src/tools/popup/services/key-definitions.spec.ts b/apps/browser/src/tools/popup/services/key-definitions.spec.ts index 3ba574efa3..7517771669 100644 --- a/apps/browser/src/tools/popup/services/key-definitions.spec.ts +++ b/apps/browser/src/tools/popup/services/key-definitions.spec.ts @@ -1,7 +1,5 @@ import { Jsonify } from "type-fest"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; - import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; import { BROWSER_SEND_COMPONENT, BROWSER_SEND_TYPE_COMPONENT } from "./key-definitions"; @@ -12,7 +10,8 @@ describe("Key definitions", () => { const keyDef = BROWSER_SEND_COMPONENT; const expectedState = { - typeCounts: new Map(), + scrollY: 0, + searchText: "test", }; const result = keyDef.deserializer( diff --git a/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts b/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts index 8ffeca72bc..57366ea8c0 100644 --- a/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts +++ b/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts @@ -42,6 +42,7 @@ import { i18nServiceFactory, I18nServiceInitOptions, } from "../../../platform/background/service-factories/i18n-service.factory"; +import { stateProviderFactory } from "../../../platform/background/service-factories/state-provider.factory"; import { stateServiceFactory, StateServiceInitOptions, @@ -81,6 +82,7 @@ export function cipherServiceFactory( await encryptServiceFactory(cache, opts), await cipherFileUploadServiceFactory(cache, opts), await configServiceFactory(cache, opts), + await stateProviderFactory(cache, opts), ), ); } diff --git a/apps/browser/src/vault/background/service_factories/folder-service.factory.ts b/apps/browser/src/vault/background/service_factories/folder-service.factory.ts index 72847a0536..0593dc904c 100644 --- a/apps/browser/src/vault/background/service_factories/folder-service.factory.ts +++ b/apps/browser/src/vault/background/service_factories/folder-service.factory.ts @@ -14,11 +14,10 @@ import { i18nServiceFactory, I18nServiceInitOptions, } from "../../../platform/background/service-factories/i18n-service.factory"; -import { stateProviderFactory } from "../../../platform/background/service-factories/state-provider.factory"; import { - stateServiceFactory as stateServiceFactory, - StateServiceInitOptions, -} from "../../../platform/background/service-factories/state-service.factory"; + stateProviderFactory, + StateProviderInitOptions, +} from "../../../platform/background/service-factories/state-provider.factory"; import { cipherServiceFactory, CipherServiceInitOptions } from "./cipher-service.factory"; @@ -28,7 +27,7 @@ export type FolderServiceInitOptions = FolderServiceFactoryOptions & CryptoServiceInitOptions & CipherServiceInitOptions & I18nServiceInitOptions & - StateServiceInitOptions; + StateProviderInitOptions; export function folderServiceFactory( cache: { folderService?: AbstractFolderService } & CachedServices, @@ -43,7 +42,6 @@ export function folderServiceFactory( await cryptoServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), await cipherServiceFactory(cache, opts), - await stateServiceFactory(cache, opts), await stateProviderFactory(cache, opts), ), ); diff --git a/apps/browser/src/vault/fido2/background/abstractions/fido2.background.ts b/apps/browser/src/vault/fido2/background/abstractions/fido2.background.ts new file mode 100644 index 0000000000..49f248c7b8 --- /dev/null +++ b/apps/browser/src/vault/fido2/background/abstractions/fido2.background.ts @@ -0,0 +1,57 @@ +import { + AssertCredentialParams, + AssertCredentialResult, + CreateCredentialParams, + CreateCredentialResult, +} from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction"; + +type SharedFido2ScriptInjectionDetails = { + runAt: browser.contentScripts.RegisteredContentScriptOptions["runAt"]; +}; + +type SharedFido2ScriptRegistrationOptions = SharedFido2ScriptInjectionDetails & { + matches: string[]; + excludeMatches: string[]; + allFrames: true; +}; + +type Fido2ExtensionMessage = { + [key: string]: any; + command: string; + hostname?: string; + origin?: string; + requestId?: string; + abortedRequestId?: string; + data?: AssertCredentialParams | CreateCredentialParams; +}; + +type Fido2ExtensionMessageEventParams = { + message: Fido2ExtensionMessage; + sender: chrome.runtime.MessageSender; +}; + +type Fido2BackgroundExtensionMessageHandlers = { + [key: string]: CallableFunction; + fido2AbortRequest: ({ message }: Fido2ExtensionMessageEventParams) => void; + fido2RegisterCredentialRequest: ({ + message, + sender, + }: Fido2ExtensionMessageEventParams) => Promise; + fido2GetCredentialRequest: ({ + message, + sender, + }: Fido2ExtensionMessageEventParams) => Promise; +}; + +interface Fido2Background { + init(): void; + injectFido2ContentScriptsInAllTabs(): Promise; +} + +export { + SharedFido2ScriptInjectionDetails, + SharedFido2ScriptRegistrationOptions, + Fido2ExtensionMessage, + Fido2BackgroundExtensionMessageHandlers, + Fido2Background, +}; diff --git a/apps/browser/src/vault/fido2/background/fido2.background.spec.ts b/apps/browser/src/vault/fido2/background/fido2.background.spec.ts new file mode 100644 index 0000000000..534d8a99c5 --- /dev/null +++ b/apps/browser/src/vault/fido2/background/fido2.background.spec.ts @@ -0,0 +1,414 @@ +import { mock, MockProxy } from "jest-mock-extended"; +import { BehaviorSubject } from "rxjs"; + +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { + AssertCredentialParams, + CreateCredentialParams, +} from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction"; +import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; +import { Fido2ClientService } from "@bitwarden/common/vault/services/fido2/fido2-client.service"; + +import { createPortSpyMock } from "../../../autofill/spec/autofill-mocks"; +import { + flushPromises, + sendExtensionRuntimeMessage, + triggerPortOnDisconnectEvent, + triggerRuntimeOnConnectEvent, +} from "../../../autofill/spec/testing-utils"; +import { BrowserApi } from "../../../platform/browser/browser-api"; +import { BrowserScriptInjectorService } from "../../../platform/services/browser-script-injector.service"; +import { AbortManager } from "../../background/abort-manager"; +import { Fido2ContentScript, Fido2ContentScriptId } from "../enums/fido2-content-script.enum"; +import { Fido2PortName } from "../enums/fido2-port-name.enum"; + +import { Fido2ExtensionMessage } from "./abstractions/fido2.background"; +import { Fido2Background } from "./fido2.background"; + +const sharedExecuteScriptOptions = { runAt: "document_start" }; +const sharedScriptInjectionDetails = { frame: "all_frames", ...sharedExecuteScriptOptions }; +const contentScriptDetails = { + file: Fido2ContentScript.ContentScript, + ...sharedScriptInjectionDetails, +}; +const sharedRegistrationOptions = { + matches: ["https://*/*"], + excludeMatches: ["https://*/*.xml*"], + allFrames: true, + ...sharedExecuteScriptOptions, +}; + +describe("Fido2Background", () => { + const tabsQuerySpy: jest.SpyInstance = jest.spyOn(BrowserApi, "tabsQuery"); + const isManifestVersionSpy: jest.SpyInstance = jest.spyOn(BrowserApi, "isManifestVersion"); + const focusTabSpy: jest.SpyInstance = jest.spyOn(BrowserApi, "focusTab").mockResolvedValue(); + const focusWindowSpy: jest.SpyInstance = jest + .spyOn(BrowserApi, "focusWindow") + .mockResolvedValue(); + let abortManagerMock!: MockProxy; + let abortController!: MockProxy; + let registeredContentScripsMock!: MockProxy; + let tabMock!: MockProxy; + let senderMock!: MockProxy; + let logService!: MockProxy; + let fido2ClientService!: MockProxy; + let vaultSettingsService!: MockProxy; + let scriptInjectorServiceMock!: MockProxy; + let enablePasskeysMock$!: BehaviorSubject; + let fido2Background!: Fido2Background; + + beforeEach(() => { + tabMock = mock({ + id: 123, + url: "https://example.com", + windowId: 456, + }); + senderMock = mock({ id: "1", tab: tabMock }); + logService = mock(); + fido2ClientService = mock(); + vaultSettingsService = mock(); + abortManagerMock = mock(); + abortController = mock(); + registeredContentScripsMock = mock(); + scriptInjectorServiceMock = mock(); + + enablePasskeysMock$ = new BehaviorSubject(true); + vaultSettingsService.enablePasskeys$ = enablePasskeysMock$; + fido2ClientService.isFido2FeatureEnabled.mockResolvedValue(true); + fido2Background = new Fido2Background( + logService, + fido2ClientService, + vaultSettingsService, + scriptInjectorServiceMock, + ); + fido2Background["abortManager"] = abortManagerMock; + abortManagerMock.runWithAbortController.mockImplementation((_requestId, runner) => + runner(abortController), + ); + isManifestVersionSpy.mockImplementation((manifestVersion) => manifestVersion === 2); + }); + + afterEach(() => { + jest.resetModules(); + jest.clearAllMocks(); + }); + + describe("injectFido2ContentScriptsInAllTabs", () => { + it("does not inject any FIDO2 content scripts when no tabs have a secure url protocol", async () => { + const insecureTab = mock({ id: 789, url: "http://example.com" }); + tabsQuerySpy.mockResolvedValueOnce([insecureTab]); + + await fido2Background.injectFido2ContentScriptsInAllTabs(); + + expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalled(); + }); + + it("only injects the FIDO2 content script into tabs that contain a secure url protocol", async () => { + const secondTabMock = mock({ id: 456, url: "https://example.com" }); + const insecureTab = mock({ id: 789, url: "http://example.com" }); + const noUrlTab = mock({ id: 101, url: undefined }); + tabsQuerySpy.mockResolvedValueOnce([tabMock, secondTabMock, insecureTab, noUrlTab]); + + await fido2Background.injectFido2ContentScriptsInAllTabs(); + + expect(scriptInjectorServiceMock.inject).toHaveBeenCalledWith({ + tabId: tabMock.id, + injectDetails: contentScriptDetails, + }); + expect(scriptInjectorServiceMock.inject).toHaveBeenCalledWith({ + tabId: secondTabMock.id, + injectDetails: contentScriptDetails, + }); + expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalledWith({ + tabId: insecureTab.id, + injectDetails: contentScriptDetails, + }); + expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalledWith({ + tabId: noUrlTab.id, + injectDetails: contentScriptDetails, + }); + }); + + it("injects the `page-script.js` content script into the provided tab", async () => { + tabsQuerySpy.mockResolvedValueOnce([tabMock]); + + await fido2Background.injectFido2ContentScriptsInAllTabs(); + + expect(scriptInjectorServiceMock.inject).toHaveBeenCalledWith({ + tabId: tabMock.id, + injectDetails: sharedScriptInjectionDetails, + mv2Details: { file: Fido2ContentScript.PageScriptAppend }, + mv3Details: { file: Fido2ContentScript.PageScript, world: "MAIN" }, + }); + }); + }); + + describe("handleEnablePasskeysUpdate", () => { + let portMock!: MockProxy; + + beforeEach(() => { + fido2Background.init(); + jest.spyOn(BrowserApi, "registerContentScriptsMv2"); + jest.spyOn(BrowserApi, "registerContentScriptsMv3"); + jest.spyOn(BrowserApi, "unregisterContentScriptsMv3"); + portMock = createPortSpyMock(Fido2PortName.InjectedScript); + triggerRuntimeOnConnectEvent(portMock); + triggerRuntimeOnConnectEvent(createPortSpyMock("some-other-port")); + + tabsQuerySpy.mockResolvedValue([tabMock]); + }); + + it("does not destroy and re-inject the content scripts when triggering `handleEnablePasskeysUpdate` with an undefined currentEnablePasskeysSetting property", async () => { + await flushPromises(); + + expect(portMock.disconnect).not.toHaveBeenCalled(); + expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalled(); + }); + + it("destroys the content scripts but skips re-injecting them when the enablePasskeys setting is set to `false`", async () => { + enablePasskeysMock$.next(false); + await flushPromises(); + + expect(portMock.disconnect).toHaveBeenCalled(); + expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalled(); + }); + + it("destroys and re-injects the content scripts when the enablePasskeys setting is set to `true`", async () => { + enablePasskeysMock$.next(true); + await flushPromises(); + + expect(portMock.disconnect).toHaveBeenCalled(); + expect(scriptInjectorServiceMock.inject).toHaveBeenCalledWith({ + tabId: tabMock.id, + injectDetails: sharedScriptInjectionDetails, + mv2Details: { file: Fido2ContentScript.PageScriptAppend }, + mv3Details: { file: Fido2ContentScript.PageScript, world: "MAIN" }, + }); + expect(scriptInjectorServiceMock.inject).toHaveBeenCalledWith({ + tabId: tabMock.id, + injectDetails: contentScriptDetails, + }); + }); + + describe("given manifest v2", () => { + it("registers the page-script-append-mv2.js and content-script.js content scripts when the enablePasskeys setting is set to `true`", async () => { + isManifestVersionSpy.mockImplementation((manifestVersion) => manifestVersion === 2); + + enablePasskeysMock$.next(true); + await flushPromises(); + + expect(BrowserApi.registerContentScriptsMv2).toHaveBeenCalledWith({ + js: [ + { file: Fido2ContentScript.PageScriptAppend }, + { file: Fido2ContentScript.ContentScript }, + ], + ...sharedRegistrationOptions, + }); + }); + + it("unregisters any existing registered content scripts when the enablePasskeys setting is set to `false`", async () => { + isManifestVersionSpy.mockImplementation((manifestVersion) => manifestVersion === 2); + fido2Background["registeredContentScripts"] = registeredContentScripsMock; + + enablePasskeysMock$.next(false); + await flushPromises(); + + expect(registeredContentScripsMock.unregister).toHaveBeenCalled(); + expect(BrowserApi.registerContentScriptsMv2).not.toHaveBeenCalledTimes(2); + }); + }); + + describe("given manifest v3", () => { + it("registers the page-script.js and content-script.js content scripts when the enablePasskeys setting is set to `true`", async () => { + isManifestVersionSpy.mockImplementation((manifestVersion) => manifestVersion === 3); + + enablePasskeysMock$.next(true); + await flushPromises(); + + expect(BrowserApi.registerContentScriptsMv3).toHaveBeenCalledWith([ + { + id: Fido2ContentScriptId.PageScript, + js: [Fido2ContentScript.PageScript], + world: "MAIN", + ...sharedRegistrationOptions, + }, + { + id: Fido2ContentScriptId.ContentScript, + js: [Fido2ContentScript.ContentScript], + ...sharedRegistrationOptions, + }, + ]); + expect(BrowserApi.unregisterContentScriptsMv3).not.toHaveBeenCalled(); + }); + + it("unregisters the page-script.js and content-script.js content scripts when the enablePasskeys setting is set to `false`", async () => { + isManifestVersionSpy.mockImplementation((manifestVersion) => manifestVersion === 3); + + enablePasskeysMock$.next(false); + await flushPromises(); + + expect(BrowserApi.unregisterContentScriptsMv3).toHaveBeenCalledWith({ + ids: [Fido2ContentScriptId.PageScript, Fido2ContentScriptId.ContentScript], + }); + expect(BrowserApi.registerContentScriptsMv3).not.toHaveBeenCalledTimes(2); + }); + }); + }); + + describe("extension message handlers", () => { + beforeEach(() => { + fido2Background.init(); + }); + + it("ignores messages that do not have a handler associated with a command within the message", () => { + const message = mock({ command: "nonexistentCommand" }); + + sendExtensionRuntimeMessage(message); + + expect(abortManagerMock.abort).not.toHaveBeenCalled(); + }); + + it("sends a response for rejected promises returned by a handler", async () => { + const message = mock({ command: "fido2RegisterCredentialRequest" }); + const sender = mock(); + const sendResponse = jest.fn(); + fido2ClientService.createCredential.mockRejectedValue(new Error("error")); + + sendExtensionRuntimeMessage(message, sender, sendResponse); + await flushPromises(); + + expect(sendResponse).toHaveBeenCalledWith({ error: { message: "error" } }); + }); + + describe("fido2AbortRequest message", () => { + it("aborts the request associated with the passed abortedRequestId", async () => { + const message = mock({ + command: "fido2AbortRequest", + abortedRequestId: "123", + }); + + sendExtensionRuntimeMessage(message); + await flushPromises(); + + expect(abortManagerMock.abort).toHaveBeenCalledWith(message.abortedRequestId); + }); + }); + + describe("fido2RegisterCredentialRequest message", () => { + it("creates a credential within the Fido2ClientService", async () => { + const message = mock({ + command: "fido2RegisterCredentialRequest", + requestId: "123", + data: mock(), + }); + + sendExtensionRuntimeMessage(message, senderMock); + await flushPromises(); + + expect(fido2ClientService.createCredential).toHaveBeenCalledWith( + message.data, + tabMock, + abortController, + ); + expect(focusTabSpy).toHaveBeenCalledWith(tabMock.id); + expect(focusWindowSpy).toHaveBeenCalledWith(tabMock.windowId); + }); + }); + + describe("fido2GetCredentialRequest", () => { + it("asserts a credential within the Fido2ClientService", async () => { + const message = mock({ + command: "fido2GetCredentialRequest", + requestId: "123", + data: mock(), + }); + + sendExtensionRuntimeMessage(message, senderMock); + await flushPromises(); + + expect(fido2ClientService.assertCredential).toHaveBeenCalledWith( + message.data, + tabMock, + abortController, + ); + expect(focusTabSpy).toHaveBeenCalledWith(tabMock.id); + expect(focusWindowSpy).toHaveBeenCalledWith(tabMock.windowId); + }); + }); + }); + + describe("handle ports onConnect", () => { + let portMock!: MockProxy; + + beforeEach(() => { + fido2Background.init(); + portMock = createPortSpyMock(Fido2PortName.InjectedScript); + fido2ClientService.isFido2FeatureEnabled.mockResolvedValue(true); + }); + + it("ignores port connections that do not have the correct port name", async () => { + const port = createPortSpyMock("nonexistentPort"); + + triggerRuntimeOnConnectEvent(port); + await flushPromises(); + + expect(port.onDisconnect.addListener).not.toHaveBeenCalled(); + }); + + it("ignores port connections that do not have a sender url", async () => { + portMock.sender = undefined; + + triggerRuntimeOnConnectEvent(portMock); + await flushPromises(); + + expect(portMock.onDisconnect.addListener).not.toHaveBeenCalled(); + }); + + it("disconnects the port connection when the Fido2 feature is not enabled", async () => { + fido2ClientService.isFido2FeatureEnabled.mockResolvedValue(false); + + triggerRuntimeOnConnectEvent(portMock); + await flushPromises(); + + expect(portMock.disconnect).toHaveBeenCalled(); + }); + + it("disconnects the port connection when the url is malformed", async () => { + portMock.sender.url = "malformed-url"; + + triggerRuntimeOnConnectEvent(portMock); + await flushPromises(); + + expect(portMock.disconnect).toHaveBeenCalled(); + expect(logService.error).toHaveBeenCalled(); + }); + + it("adds the port to the fido2ContentScriptPortsSet when the Fido2 feature is enabled", async () => { + triggerRuntimeOnConnectEvent(portMock); + await flushPromises(); + + expect(portMock.onDisconnect.addListener).toHaveBeenCalled(); + }); + }); + + describe("handleInjectScriptPortOnDisconnect", () => { + let portMock!: MockProxy; + + beforeEach(() => { + fido2Background.init(); + portMock = createPortSpyMock(Fido2PortName.InjectedScript); + triggerRuntimeOnConnectEvent(portMock); + fido2Background["fido2ContentScriptPortsSet"].add(portMock); + }); + + it("does not destroy or inject the content script when the port has already disconnected before the enablePasskeys setting is set to `false`", async () => { + triggerPortOnDisconnectEvent(portMock); + + enablePasskeysMock$.next(false); + await flushPromises(); + + expect(portMock.disconnect).not.toHaveBeenCalled(); + expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/browser/src/vault/fido2/background/fido2.background.ts b/apps/browser/src/vault/fido2/background/fido2.background.ts new file mode 100644 index 0000000000..856874cee3 --- /dev/null +++ b/apps/browser/src/vault/fido2/background/fido2.background.ts @@ -0,0 +1,356 @@ +import { firstValueFrom, startWith } from "rxjs"; +import { pairwise } from "rxjs/operators"; + +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { + AssertCredentialParams, + AssertCredentialResult, + CreateCredentialParams, + CreateCredentialResult, + Fido2ClientService, +} from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction"; +import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; + +import { BrowserApi } from "../../../platform/browser/browser-api"; +import { ScriptInjectorService } from "../../../platform/services/abstractions/script-injector.service"; +import { AbortManager } from "../../background/abort-manager"; +import { Fido2ContentScript, Fido2ContentScriptId } from "../enums/fido2-content-script.enum"; +import { Fido2PortName } from "../enums/fido2-port-name.enum"; + +import { + Fido2Background as Fido2BackgroundInterface, + Fido2BackgroundExtensionMessageHandlers, + Fido2ExtensionMessage, + SharedFido2ScriptInjectionDetails, + SharedFido2ScriptRegistrationOptions, +} from "./abstractions/fido2.background"; + +export class Fido2Background implements Fido2BackgroundInterface { + private abortManager = new AbortManager(); + private fido2ContentScriptPortsSet = new Set(); + private registeredContentScripts: browser.contentScripts.RegisteredContentScript; + private readonly sharedInjectionDetails: SharedFido2ScriptInjectionDetails = { + runAt: "document_start", + }; + private readonly sharedRegistrationOptions: SharedFido2ScriptRegistrationOptions = { + matches: ["https://*/*"], + excludeMatches: ["https://*/*.xml*"], + allFrames: true, + ...this.sharedInjectionDetails, + }; + private readonly extensionMessageHandlers: Fido2BackgroundExtensionMessageHandlers = { + fido2AbortRequest: ({ message }) => this.abortRequest(message), + fido2RegisterCredentialRequest: ({ message, sender }) => + this.registerCredentialRequest(message, sender), + fido2GetCredentialRequest: ({ message, sender }) => this.getCredentialRequest(message, sender), + }; + + constructor( + private logService: LogService, + private fido2ClientService: Fido2ClientService, + private vaultSettingsService: VaultSettingsService, + private scriptInjectorService: ScriptInjectorService, + ) {} + + /** + * Initializes the FIDO2 background service. Sets up the extension message + * and port listeners. Subscribes to the enablePasskeys$ observable to + * handle passkey enable/disable events. + */ + init() { + BrowserApi.messageListener("fido2.background", this.handleExtensionMessage); + BrowserApi.addListener(chrome.runtime.onConnect, this.handleInjectedScriptPortConnection); + this.vaultSettingsService.enablePasskeys$ + .pipe(startWith(undefined), pairwise()) + .subscribe(([previous, current]) => this.handleEnablePasskeysUpdate(previous, current)); + } + + /** + * Injects the FIDO2 content and page script into all existing browser tabs. + */ + async injectFido2ContentScriptsInAllTabs() { + const tabs = await BrowserApi.tabsQuery({}); + for (let index = 0; index < tabs.length; index++) { + const tab = tabs[index]; + if (!tab.url?.startsWith("https")) { + continue; + } + + void this.injectFido2ContentScripts(tab); + } + } + + /** + * Handles reacting to the enablePasskeys setting being updated. If the setting + * is enabled, the FIDO2 content scripts are injected into all tabs. If the setting + * is disabled, the FIDO2 content scripts will be from all tabs. This logic will + * not trigger until after the first setting update. + * + * @param previousEnablePasskeysSetting - The previous value of the enablePasskeys setting. + * @param enablePasskeys - The new value of the enablePasskeys setting. + */ + private async handleEnablePasskeysUpdate( + previousEnablePasskeysSetting: boolean, + enablePasskeys: boolean, + ) { + await this.updateContentScriptRegistration(); + + if (previousEnablePasskeysSetting === undefined) { + return; + } + + this.destroyLoadedFido2ContentScripts(); + if (enablePasskeys) { + void this.injectFido2ContentScriptsInAllTabs(); + } + } + + /** + * Updates the registration status of static FIDO2 content + * scripts based on the enablePasskeys setting. + */ + private async updateContentScriptRegistration() { + if (BrowserApi.isManifestVersion(2)) { + await this.updateMv2ContentScriptsRegistration(); + + return; + } + + await this.updateMv3ContentScriptsRegistration(); + } + + /** + * Updates the registration status of static FIDO2 content + * scripts based on the enablePasskeys setting for manifest v2. + */ + private async updateMv2ContentScriptsRegistration() { + if (!(await this.isPasskeySettingEnabled())) { + await this.registeredContentScripts?.unregister(); + + return; + } + + this.registeredContentScripts = await BrowserApi.registerContentScriptsMv2({ + js: [ + { file: Fido2ContentScript.PageScriptAppend }, + { file: Fido2ContentScript.ContentScript }, + ], + ...this.sharedRegistrationOptions, + }); + } + + /** + * Updates the registration status of static FIDO2 content + * scripts based on the enablePasskeys setting for manifest v3. + */ + private async updateMv3ContentScriptsRegistration() { + if (await this.isPasskeySettingEnabled()) { + void BrowserApi.registerContentScriptsMv3([ + { + id: Fido2ContentScriptId.PageScript, + js: [Fido2ContentScript.PageScript], + world: "MAIN", + ...this.sharedRegistrationOptions, + }, + { + id: Fido2ContentScriptId.ContentScript, + js: [Fido2ContentScript.ContentScript], + ...this.sharedRegistrationOptions, + }, + ]); + + return; + } + + void BrowserApi.unregisterContentScriptsMv3({ + ids: [Fido2ContentScriptId.PageScript, Fido2ContentScriptId.ContentScript], + }); + } + + /** + * Injects the FIDO2 content and page script into the current tab. + * + * @param tab - The current tab to inject the scripts into. + */ + private async injectFido2ContentScripts(tab: chrome.tabs.Tab): Promise { + void this.scriptInjectorService.inject({ + tabId: tab.id, + injectDetails: { frame: "all_frames", ...this.sharedInjectionDetails }, + mv2Details: { file: Fido2ContentScript.PageScriptAppend }, + mv3Details: { file: Fido2ContentScript.PageScript, world: "MAIN" }, + }); + + void this.scriptInjectorService.inject({ + tabId: tab.id, + injectDetails: { + file: Fido2ContentScript.ContentScript, + frame: "all_frames", + ...this.sharedInjectionDetails, + }, + }); + } + + /** + * Iterates over the set of injected FIDO2 content script ports + * and disconnects them, destroying the content scripts. + */ + private destroyLoadedFido2ContentScripts() { + this.fido2ContentScriptPortsSet.forEach((port) => { + port.disconnect(); + this.fido2ContentScriptPortsSet.delete(port); + }); + } + + /** + * Aborts the FIDO2 request with the provided requestId. + * + * @param message - The FIDO2 extension message containing the requestId to abort. + */ + private abortRequest(message: Fido2ExtensionMessage) { + this.abortManager.abort(message.abortedRequestId); + } + + /** + * Registers a new FIDO2 credential with the provided request data. + * + * @param message - The FIDO2 extension message containing the request data. + * @param sender - The sender of the message. + */ + private async registerCredentialRequest( + message: Fido2ExtensionMessage, + sender: chrome.runtime.MessageSender, + ): Promise { + return await this.handleCredentialRequest( + message, + sender.tab, + this.fido2ClientService.createCredential.bind(this.fido2ClientService), + ); + } + + /** + * Gets a FIDO2 credential with the provided request data. + * + * @param message - The FIDO2 extension message containing the request data. + * @param sender - The sender of the message. + */ + private async getCredentialRequest( + message: Fido2ExtensionMessage, + sender: chrome.runtime.MessageSender, + ): Promise { + return await this.handleCredentialRequest( + message, + sender.tab, + this.fido2ClientService.assertCredential.bind(this.fido2ClientService), + ); + } + + /** + * Handles Fido2 credential requests by calling the provided callback with the + * request data, tab, and abort controller. The callback is expected to return + * a promise that resolves with the result of the credential request. + * + * @param requestId - The request ID associated with the request. + * @param data - The request data to handle. + * @param tab - The tab associated with the request. + * @param callback - The callback to call with the request data, tab, and abort controller. + */ + private handleCredentialRequest = async ( + { requestId, data }: Fido2ExtensionMessage, + tab: chrome.tabs.Tab, + callback: ( + data: AssertCredentialParams | CreateCredentialParams, + tab: chrome.tabs.Tab, + abortController: AbortController, + ) => Promise, + ) => { + return await this.abortManager.runWithAbortController(requestId, async (abortController) => { + try { + return await callback(data, tab, abortController); + } finally { + await BrowserApi.focusTab(tab.id); + await BrowserApi.focusWindow(tab.windowId); + } + }); + }; + + /** + * Checks if the enablePasskeys setting is enabled. + */ + private async isPasskeySettingEnabled() { + return await firstValueFrom(this.vaultSettingsService.enablePasskeys$); + } + + /** + * Handles the FIDO2 extension message by calling the + * appropriate handler based on the message command. + * + * @param message - The FIDO2 extension message to handle. + * @param sender - The sender of the message. + * @param sendResponse - The function to call with the response. + */ + private handleExtensionMessage = ( + message: Fido2ExtensionMessage, + sender: chrome.runtime.MessageSender, + sendResponse: (response?: any) => void, + ) => { + const handler: CallableFunction | undefined = this.extensionMessageHandlers[message?.command]; + if (!handler) { + return; + } + + const messageResponse = handler({ message, sender }); + if (!messageResponse) { + return; + } + + Promise.resolve(messageResponse) + .then( + (response) => sendResponse(response), + (error) => sendResponse({ error: { ...error, message: error.message } }), + ) + .catch(this.logService.error); + + return true; + }; + + /** + * Handles the connection of a FIDO2 content script port by checking if the + * FIDO2 feature is enabled for the sender's hostname and origin. If the feature + * is not enabled, the port is disconnected. + * + * @param port - The port which is connecting + */ + private handleInjectedScriptPortConnection = async (port: chrome.runtime.Port) => { + if (port.name !== Fido2PortName.InjectedScript || !port.sender?.url) { + return; + } + + try { + const { hostname, origin } = new URL(port.sender.url); + if (!(await this.fido2ClientService.isFido2FeatureEnabled(hostname, origin))) { + port.disconnect(); + return; + } + + this.fido2ContentScriptPortsSet.add(port); + port.onDisconnect.addListener(this.handleInjectScriptPortOnDisconnect); + } catch (error) { + this.logService.error(error); + port.disconnect(); + } + }; + + /** + * Handles the disconnection of a FIDO2 content script port + * by removing it from the set of connected ports. + * + * @param port - The port which is disconnecting + */ + private handleInjectScriptPortOnDisconnect = (port: chrome.runtime.Port) => { + if (port.name !== Fido2PortName.InjectedScript) { + return; + } + + this.fido2ContentScriptPortsSet.delete(port); + }; +} diff --git a/apps/browser/src/vault/fido2/content/content-script.spec.ts b/apps/browser/src/vault/fido2/content/content-script.spec.ts new file mode 100644 index 0000000000..29d3e9c257 --- /dev/null +++ b/apps/browser/src/vault/fido2/content/content-script.spec.ts @@ -0,0 +1,164 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +import { CreateCredentialResult } from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction"; + +import { createPortSpyMock } from "../../../autofill/spec/autofill-mocks"; +import { triggerPortOnDisconnectEvent } from "../../../autofill/spec/testing-utils"; +import { Fido2PortName } from "../enums/fido2-port-name.enum"; + +import { InsecureCreateCredentialParams, MessageType } from "./messaging/message"; +import { MessageWithMetadata, Messenger } from "./messaging/messenger"; + +jest.mock("../../../autofill/utils", () => ({ + sendExtensionMessage: jest.fn((command, options) => { + return chrome.runtime.sendMessage(Object.assign({ command }, options)); + }), +})); + +describe("Fido2 Content Script", () => { + let messenger: Messenger; + const messengerForDOMCommunicationSpy = jest + .spyOn(Messenger, "forDOMCommunication") + .mockImplementation((window) => { + const windowOrigin = window.location.origin; + + messenger = new Messenger({ + postMessage: (message, port) => window.postMessage(message, windowOrigin, [port]), + addEventListener: (listener) => window.addEventListener("message", listener), + removeEventListener: (listener) => window.removeEventListener("message", listener), + }); + messenger.destroy = jest.fn(); + return messenger; + }); + const portSpy: MockProxy = createPortSpyMock(Fido2PortName.InjectedScript); + chrome.runtime.connect = jest.fn(() => portSpy); + + afterEach(() => { + Object.defineProperty(document, "contentType", { + value: "text/html", + writable: true, + }); + + jest.clearAllMocks(); + jest.resetModules(); + }); + + it("destroys the messenger when the port is disconnected", () => { + require("./content-script"); + + triggerPortOnDisconnectEvent(portSpy); + + expect(messenger.destroy).toHaveBeenCalled(); + }); + + it("handles a FIDO2 credential creation request message from the window message listener, formats the message and sends the formatted message to the extension background", async () => { + const message = mock({ + type: MessageType.CredentialCreationRequest, + data: mock(), + }); + const mockResult = { credentialId: "mock" } as CreateCredentialResult; + jest.spyOn(chrome.runtime, "sendMessage").mockResolvedValue(mockResult); + + require("./content-script"); + + const response = await messenger.handler!(message, new AbortController()); + + expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({ + command: "fido2RegisterCredentialRequest", + data: expect.objectContaining({ + origin: globalThis.location.origin, + sameOriginWithAncestors: true, + }), + requestId: expect.any(String), + }); + expect(response).toEqual({ + type: MessageType.CredentialCreationResponse, + result: mockResult, + }); + }); + + it("handles a FIDO2 credential get request message from the window message listener, formats the message and sends the formatted message to the extension background", async () => { + const message = mock({ + type: MessageType.CredentialGetRequest, + data: mock(), + }); + + require("./content-script"); + + await messenger.handler!(message, new AbortController()); + + expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({ + command: "fido2GetCredentialRequest", + data: expect.objectContaining({ + origin: globalThis.location.origin, + sameOriginWithAncestors: true, + }), + requestId: expect.any(String), + }); + }); + + it("removes the abort handler when the FIDO2 request is complete", async () => { + const message = mock({ + type: MessageType.CredentialCreationRequest, + data: mock(), + }); + const abortController = new AbortController(); + const abortSpy = jest.spyOn(abortController.signal, "removeEventListener"); + + require("./content-script"); + + await messenger.handler!(message, abortController); + + expect(abortSpy).toHaveBeenCalled(); + }); + + it("sends an extension message to abort the FIDO2 request when the abort controller is signaled", async () => { + const message = mock({ + type: MessageType.CredentialCreationRequest, + data: mock(), + }); + const abortController = new AbortController(); + const abortSpy = jest.spyOn(abortController.signal, "addEventListener"); + jest + .spyOn(chrome.runtime, "sendMessage") + .mockImplementationOnce(async (extensionId: string, message: unknown, options: any) => { + abortController.abort(); + }); + + require("./content-script"); + + await messenger.handler!(message, abortController); + + expect(abortSpy).toHaveBeenCalled(); + expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({ + command: "fido2AbortRequest", + abortedRequestId: expect.any(String), + }); + }); + + it("rejects credential requests and returns an error result", async () => { + const errorMessage = "Test error"; + const message = mock({ + type: MessageType.CredentialCreationRequest, + data: mock(), + }); + const abortController = new AbortController(); + jest.spyOn(chrome.runtime, "sendMessage").mockResolvedValue({ error: errorMessage }); + + require("./content-script"); + const result = messenger.handler!(message, abortController); + + await expect(result).rejects.toEqual(errorMessage); + }); + + it("skips initializing the content script if the document content type is not 'text/html'", () => { + Object.defineProperty(document, "contentType", { + value: "application/json", + writable: true, + }); + + require("./content-script"); + + expect(messengerForDOMCommunicationSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/browser/src/vault/fido2/content/content-script.ts b/apps/browser/src/vault/fido2/content/content-script.ts index c2fc862f55..809db11553 100644 --- a/apps/browser/src/vault/fido2/content/content-script.ts +++ b/apps/browser/src/vault/fido2/content/content-script.ts @@ -3,142 +3,134 @@ import { CreateCredentialParams, } from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction"; -import { Message, MessageType } from "./messaging/message"; -import { Messenger } from "./messaging/messenger"; +import { sendExtensionMessage } from "../../../autofill/utils"; +import { Fido2PortName } from "../enums/fido2-port-name.enum"; -function isFido2FeatureEnabled(): Promise { - return new Promise((resolve) => { - chrome.runtime.sendMessage( - { - command: "checkFido2FeatureEnabled", - hostname: window.location.hostname, - origin: window.location.origin, - }, - (response: { result?: boolean }) => resolve(response.result), - ); - }); -} - -function isSameOriginWithAncestors() { - try { - return window.self === window.top; - } catch { - return false; - } -} -const messenger = Messenger.forDOMCommunication(window); - -function injectPageScript() { - // Locate an existing page-script on the page - const existingPageScript = document.getElementById("bw-fido2-page-script"); - - // Inject the page-script if it doesn't exist - if (!existingPageScript) { - const s = document.createElement("script"); - s.src = chrome.runtime.getURL("content/fido2/page-script.js"); - s.id = "bw-fido2-page-script"; - (document.head || document.documentElement).appendChild(s); +import { + InsecureAssertCredentialParams, + InsecureCreateCredentialParams, + Message, + MessageType, +} from "./messaging/message"; +import { MessageWithMetadata, Messenger } from "./messaging/messenger"; +(function (globalContext) { + if (globalContext.document.contentType !== "text/html") { return; } - // If the page-script already exists, send a reconnect message to the page-script - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - messenger.sendReconnectCommand(); -} + // Initialization logic, set up the messenger and connect a port to the background script. + const messenger = Messenger.forDOMCommunication(globalContext.window); + messenger.handler = handleFido2Message; + const port = chrome.runtime.connect({ name: Fido2PortName.InjectedScript }); + port.onDisconnect.addListener(handlePortOnDisconnect); -function initializeFido2ContentScript() { - injectPageScript(); - - messenger.handler = async (message, abortController) => { + /** + * Handles FIDO2 credential requests and returns the result. + * + * @param message - The message to handle. + * @param abortController - The abort controller used to handle exit conditions from the FIDO2 request. + */ + async function handleFido2Message( + message: MessageWithMetadata, + abortController: AbortController, + ) { const requestId = Date.now().toString(); const abortHandler = () => - chrome.runtime.sendMessage({ - command: "fido2AbortRequest", - abortedRequestId: requestId, - }); + sendExtensionMessage("fido2AbortRequest", { abortedRequestId: requestId }); abortController.signal.addEventListener("abort", abortHandler); - if (message.type === MessageType.CredentialCreationRequest) { - return new Promise((resolve, reject) => { - const data: CreateCredentialParams = { - ...message.data, - origin: window.location.origin, - sameOriginWithAncestors: isSameOriginWithAncestors(), - }; - - chrome.runtime.sendMessage( - { - command: "fido2RegisterCredentialRequest", - data, - requestId: requestId, - }, - (response) => { - if (response && response.error !== undefined) { - return reject(response.error); - } - - resolve({ - type: MessageType.CredentialCreationResponse, - result: response.result, - }); - }, + try { + if (message.type === MessageType.CredentialCreationRequest) { + return handleCredentialCreationRequestMessage( + requestId, + message.data as InsecureCreateCredentialParams, ); - }); - } + } - if (message.type === MessageType.CredentialGetRequest) { - return new Promise((resolve, reject) => { - const data: AssertCredentialParams = { - ...message.data, - origin: window.location.origin, - sameOriginWithAncestors: isSameOriginWithAncestors(), - }; - - chrome.runtime.sendMessage( - { - command: "fido2GetCredentialRequest", - data, - requestId: requestId, - }, - (response) => { - if (response && response.error !== undefined) { - return reject(response.error); - } - - resolve({ - type: MessageType.CredentialGetResponse, - result: response.result, - }); - }, + if (message.type === MessageType.CredentialGetRequest) { + return handleCredentialGetRequestMessage( + requestId, + message.data as InsecureAssertCredentialParams, ); - }).finally(() => - abortController.signal.removeEventListener("abort", abortHandler), - ) as Promise; + } + } finally { + abortController.signal.removeEventListener("abort", abortHandler); } - - return undefined; - }; -} - -async function run() { - if (!(await isFido2FeatureEnabled())) { - return; } - initializeFido2ContentScript(); + /** + * Handles the credential creation request message and returns the result. + * + * @param requestId - The request ID of the message. + * @param data - Data associated with the credential request. + */ + async function handleCredentialCreationRequestMessage( + requestId: string, + data: InsecureCreateCredentialParams, + ): Promise { + return respondToCredentialRequest( + "fido2RegisterCredentialRequest", + MessageType.CredentialCreationResponse, + requestId, + data, + ); + } - const port = chrome.runtime.connect({ name: "fido2ContentScriptReady" }); - port.onDisconnect.addListener(() => { - // Cleanup the messenger and remove the event listener - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - messenger.destroy(); - }); -} + /** + * Handles the credential get request message and returns the result. + * + * @param requestId - The request ID of the message. + * @param data - Data associated with the credential request. + */ + async function handleCredentialGetRequestMessage( + requestId: string, + data: InsecureAssertCredentialParams, + ): Promise { + return respondToCredentialRequest( + "fido2GetCredentialRequest", + MessageType.CredentialGetResponse, + requestId, + data, + ); + } -// Only run the script if the document is an HTML document -if (document.contentType === "text/html") { - void run(); -} + /** + * Sends a message to the extension to handle the + * credential request and returns the result. + * + * @param command - The command to send to the extension. + * @param type - The type of message, either CredentialCreationResponse or CredentialGetResponse. + * @param requestId - The request ID of the message. + * @param messageData - Data associated with the credential request. + */ + async function respondToCredentialRequest( + command: string, + type: MessageType.CredentialCreationResponse | MessageType.CredentialGetResponse, + requestId: string, + messageData: InsecureCreateCredentialParams | InsecureAssertCredentialParams, + ): Promise { + const data: CreateCredentialParams | AssertCredentialParams = { + ...messageData, + origin: globalContext.location.origin, + sameOriginWithAncestors: globalContext.self === globalContext.top, + }; + + const result = await sendExtensionMessage(command, { data, requestId }); + + if (result && result.error !== undefined) { + return Promise.reject(result.error); + } + + return Promise.resolve({ type, result }); + } + + /** + * Handles the disconnect event of the port. Calls + * to the messenger to destroy and tear down the + * implemented page-script.js logic. + */ + function handlePortOnDisconnect() { + void messenger.destroy(); + } +})(globalThis); diff --git a/apps/browser/src/vault/fido2/content/messaging/messenger.spec.ts b/apps/browser/src/vault/fido2/content/messaging/messenger.spec.ts index 0c46ac39aa..5283c60882 100644 --- a/apps/browser/src/vault/fido2/content/messaging/messenger.spec.ts +++ b/apps/browser/src/vault/fido2/content/messaging/messenger.spec.ts @@ -68,7 +68,7 @@ describe("Messenger", () => { const abortController = new AbortController(); // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises - messengerA.request(createRequest(), abortController); + messengerA.request(createRequest(), abortController.signal); abortController.abort(); const received = handlerB.receive(); diff --git a/apps/browser/src/vault/fido2/content/messaging/messenger.ts b/apps/browser/src/vault/fido2/content/messaging/messenger.ts index cc29282227..f05c138eab 100644 --- a/apps/browser/src/vault/fido2/content/messaging/messenger.ts +++ b/apps/browser/src/vault/fido2/content/messaging/messenger.ts @@ -47,7 +47,7 @@ export class Messenger { } /** - * The handler that will be called when a message is recieved. The handler should return + * The handler that will be called when a message is received. The handler should return * a promise that resolves to the response message. If the handler throws an error, the * error will be sent back to the sender. */ @@ -65,10 +65,10 @@ export class Messenger { * AbortController signals will be forwarded to the content script. * * @param request data to send to the content script - * @param abortController the abort controller that might be used to abort the request + * @param abortSignal the abort controller that might be used to abort the request * @returns the response from the content script */ - async request(request: Message, abortController?: AbortController): Promise { + async request(request: Message, abortSignal?: AbortSignal): Promise { const requestChannel = new MessageChannel(); const { port1: localPort, port2: remotePort } = requestChannel; @@ -82,7 +82,7 @@ export class Messenger { metadata: { SENDER }, type: MessageType.AbortRequest, }); - abortController?.signal.addEventListener("abort", abortListener); + abortSignal?.addEventListener("abort", abortListener); this.broadcastChannel.postMessage( { ...request, SENDER, senderId: this.messengerId }, @@ -90,7 +90,7 @@ export class Messenger { ); const response = await promise; - abortController?.signal.removeEventListener("abort", abortListener); + abortSignal?.removeEventListener("abort", abortListener); if (response.type === MessageType.ErrorResponse) { const error = new Error(); @@ -113,12 +113,7 @@ export class Messenger { const message = event.data; const port = event.ports?.[0]; - if ( - message?.SENDER !== SENDER || - message.senderId == this.messengerId || - message == null || - port == null - ) { + if (message?.SENDER !== SENDER || message.senderId == this.messengerId || port == null) { return; } @@ -167,10 +162,6 @@ export class Messenger { } } - async sendReconnectCommand() { - await this.request({ type: MessageType.ReconnectRequest }); - } - private async sendDisconnectCommand() { await this.request({ type: MessageType.DisconnectRequest }); } diff --git a/apps/browser/src/vault/fido2/content/page-script-append.mv2.spec.ts b/apps/browser/src/vault/fido2/content/page-script-append.mv2.spec.ts new file mode 100644 index 0000000000..d40a725a1f --- /dev/null +++ b/apps/browser/src/vault/fido2/content/page-script-append.mv2.spec.ts @@ -0,0 +1,69 @@ +import { Fido2ContentScript } from "../enums/fido2-content-script.enum"; + +describe("FIDO2 page-script for manifest v2", () => { + let createdScriptElement: HTMLScriptElement; + jest.spyOn(window.document, "createElement"); + + afterEach(() => { + Object.defineProperty(window.document, "contentType", { value: "text/html", writable: true }); + jest.clearAllMocks(); + jest.resetModules(); + }); + + it("skips appending the `page-script.js` file if the document contentType is not `text/html`", () => { + Object.defineProperty(window.document, "contentType", { value: "text/plain", writable: true }); + + require("./page-script-append.mv2"); + + expect(window.document.createElement).not.toHaveBeenCalled(); + }); + + it("appends the `page-script.js` file to the document head when the contentType is `text/html`", () => { + jest.spyOn(window.document.head, "insertBefore").mockImplementation((node) => { + createdScriptElement = node as HTMLScriptElement; + return node; + }); + + require("./page-script-append.mv2"); + + expect(window.document.createElement).toHaveBeenCalledWith("script"); + expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript); + expect(window.document.head.insertBefore).toHaveBeenCalledWith( + expect.any(HTMLScriptElement), + window.document.head.firstChild, + ); + expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`); + }); + + it("appends the `page-script.js` file to the document element if the head is not available", () => { + window.document.documentElement.removeChild(window.document.head); + jest.spyOn(window.document.documentElement, "insertBefore").mockImplementation((node) => { + createdScriptElement = node as HTMLScriptElement; + return node; + }); + + require("./page-script-append.mv2"); + + expect(window.document.createElement).toHaveBeenCalledWith("script"); + expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript); + expect(window.document.documentElement.insertBefore).toHaveBeenCalledWith( + expect.any(HTMLScriptElement), + window.document.documentElement.firstChild, + ); + expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`); + }); + + it("removes the appended `page-script.js` file after the script has triggered a load event", () => { + createdScriptElement = document.createElement("script"); + jest.spyOn(window.document, "createElement").mockImplementation((element) => { + return createdScriptElement; + }); + + require("./page-script-append.mv2"); + + jest.spyOn(createdScriptElement, "remove"); + createdScriptElement.dispatchEvent(new Event("load")); + + expect(createdScriptElement.remove).toHaveBeenCalled(); + }); +}); diff --git a/apps/browser/src/vault/fido2/content/page-script-append.mv2.ts b/apps/browser/src/vault/fido2/content/page-script-append.mv2.ts new file mode 100644 index 0000000000..4e806d2990 --- /dev/null +++ b/apps/browser/src/vault/fido2/content/page-script-append.mv2.ts @@ -0,0 +1,19 @@ +/** + * This script handles injection of the FIDO2 override page script into the document. + * This is required for manifest v2, but will be removed when we migrate fully to manifest v3. + */ +import { Fido2ContentScript } from "../enums/fido2-content-script.enum"; + +(function (globalContext) { + if (globalContext.document.contentType !== "text/html") { + return; + } + + const script = globalContext.document.createElement("script"); + script.src = chrome.runtime.getURL(Fido2ContentScript.PageScript); + script.addEventListener("load", () => script.remove()); + + const scriptInsertionPoint = + globalContext.document.head || globalContext.document.documentElement; + scriptInsertionPoint.insertBefore(script, scriptInsertionPoint.firstChild); +})(globalThis); diff --git a/apps/browser/src/vault/fido2/content/page-script.ts b/apps/browser/src/vault/fido2/content/page-script.ts index 9adea68307..1de0f3258a 100644 --- a/apps/browser/src/vault/fido2/content/page-script.ts +++ b/apps/browser/src/vault/fido2/content/page-script.ts @@ -5,212 +5,229 @@ import { WebauthnUtils } from "../webauthn-utils"; import { MessageType } from "./messaging/message"; import { Messenger } from "./messaging/messenger"; -const BrowserPublicKeyCredential = window.PublicKeyCredential; - -const browserNativeWebauthnSupport = window.PublicKeyCredential != undefined; -let browserNativeWebauthnPlatformAuthenticatorSupport = false; -if (!browserNativeWebauthnSupport) { - // Polyfill webauthn support - try { - // credentials is read-only if supported, use type-casting to force assignment - (navigator as any).credentials = { - async create() { - throw new Error("Webauthn not supported in this browser."); - }, - async get() { - throw new Error("Webauthn not supported in this browser."); - }, - }; - window.PublicKeyCredential = class PolyfillPublicKeyCredential { - static isUserVerifyingPlatformAuthenticatorAvailable() { - return Promise.resolve(true); - } - } as any; - window.AuthenticatorAttestationResponse = - class PolyfillAuthenticatorAttestationResponse {} as any; - } catch { - /* empty */ +(function (globalContext) { + if (globalContext.document.contentType !== "text/html") { + return; } -} + const BrowserPublicKeyCredential = globalContext.PublicKeyCredential; + const BrowserNavigatorCredentials = navigator.credentials; + const BrowserAuthenticatorAttestationResponse = globalContext.AuthenticatorAttestationResponse; -if (browserNativeWebauthnSupport) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - BrowserPublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable().then((available) => { - browserNativeWebauthnPlatformAuthenticatorSupport = available; - - if (!available) { - // Polyfill platform authenticator support - window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable = () => - Promise.resolve(true); + const browserNativeWebauthnSupport = globalContext.PublicKeyCredential != undefined; + let browserNativeWebauthnPlatformAuthenticatorSupport = false; + if (!browserNativeWebauthnSupport) { + // Polyfill webauthn support + try { + // credentials are read-only if supported, use type-casting to force assignment + (navigator as any).credentials = { + async create() { + throw new Error("Webauthn not supported in this browser."); + }, + async get() { + throw new Error("Webauthn not supported in this browser."); + }, + }; + globalContext.PublicKeyCredential = class PolyfillPublicKeyCredential { + static isUserVerifyingPlatformAuthenticatorAvailable() { + return Promise.resolve(true); + } + } as any; + globalContext.AuthenticatorAttestationResponse = + class PolyfillAuthenticatorAttestationResponse {} as any; + } catch { + /* empty */ } - }); -} + } else { + void BrowserPublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable().then( + (available) => { + browserNativeWebauthnPlatformAuthenticatorSupport = available; -const browserCredentials = { - create: navigator.credentials.create.bind( - navigator.credentials, - ) as typeof navigator.credentials.create, - get: navigator.credentials.get.bind(navigator.credentials) as typeof navigator.credentials.get, -}; - -const messenger = ((window as any).messenger = Messenger.forDOMCommunication(window)); - -navigator.credentials.create = createWebAuthnCredential; -navigator.credentials.get = getWebAuthnCredential; - -/** - * Creates a new webauthn credential. - * - * @param options Options for creating new credentials. - * @param abortController Abort controller to abort the request if needed. - * @returns Promise that resolves to the new credential object. - */ -async function createWebAuthnCredential( - options?: CredentialCreationOptions, - abortController?: AbortController, -): Promise { - if (!isWebauthnCall(options)) { - return await browserCredentials.create(options); - } - - const fallbackSupported = - (options?.publicKey?.authenticatorSelection?.authenticatorAttachment === "platform" && - browserNativeWebauthnPlatformAuthenticatorSupport) || - (options?.publicKey?.authenticatorSelection?.authenticatorAttachment !== "platform" && - browserNativeWebauthnSupport); - try { - const response = await messenger.request( - { - type: MessageType.CredentialCreationRequest, - data: WebauthnUtils.mapCredentialCreationOptions(options, fallbackSupported), + if (!available) { + // Polyfill platform authenticator support + globalContext.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable = () => + Promise.resolve(true); + } }, - abortController, ); + } - if (response.type !== MessageType.CredentialCreationResponse) { - throw new Error("Something went wrong."); - } + const browserCredentials = { + create: navigator.credentials.create.bind( + navigator.credentials, + ) as typeof navigator.credentials.create, + get: navigator.credentials.get.bind(navigator.credentials) as typeof navigator.credentials.get, + }; - return WebauthnUtils.mapCredentialRegistrationResult(response.result); - } catch (error) { - if (error && error.fallbackRequested && fallbackSupported) { - await waitForFocus(); + const messenger = Messenger.forDOMCommunication(window); + let waitForFocusTimeout: number | NodeJS.Timeout; + let focusListenerHandler: () => void; + + navigator.credentials.create = createWebAuthnCredential; + navigator.credentials.get = getWebAuthnCredential; + + /** + * Creates a new webauthn credential. + * + * @param options Options for creating new credentials. + * @returns Promise that resolves to the new credential object. + */ + async function createWebAuthnCredential( + options?: CredentialCreationOptions, + ): Promise { + if (!isWebauthnCall(options)) { return await browserCredentials.create(options); } - throw error; - } -} + const authenticatorAttachmentIsPlatform = + options?.publicKey?.authenticatorSelection?.authenticatorAttachment === "platform"; -/** - * Retrieves a webauthn credential. - * - * @param options Options for creating new credentials. - * @param abortController Abort controller to abort the request if needed. - * @returns Promise that resolves to the new credential object. - */ -async function getWebAuthnCredential( - options?: CredentialRequestOptions, - abortController?: AbortController, -): Promise { - if (!isWebauthnCall(options)) { - return await browserCredentials.get(options); + const fallbackSupported = + (authenticatorAttachmentIsPlatform && browserNativeWebauthnPlatformAuthenticatorSupport) || + (!authenticatorAttachmentIsPlatform && browserNativeWebauthnSupport); + try { + const response = await messenger.request( + { + type: MessageType.CredentialCreationRequest, + data: WebauthnUtils.mapCredentialCreationOptions(options, fallbackSupported), + }, + options?.signal, + ); + + if (response.type !== MessageType.CredentialCreationResponse) { + throw new Error("Something went wrong."); + } + + return WebauthnUtils.mapCredentialRegistrationResult(response.result); + } catch (error) { + if (error && error.fallbackRequested && fallbackSupported) { + await waitForFocus(); + return await browserCredentials.create(options); + } + + throw error; + } } - const fallbackSupported = browserNativeWebauthnSupport; - - try { - if (options?.mediation && options.mediation !== "optional") { - throw new FallbackRequestedError(); - } - - const response = await messenger.request( - { - type: MessageType.CredentialGetRequest, - data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported), - }, - abortController, - ); - - if (response.type !== MessageType.CredentialGetResponse) { - throw new Error("Something went wrong."); - } - - return WebauthnUtils.mapCredentialAssertResult(response.result); - } catch (error) { - if (error && error.fallbackRequested && fallbackSupported) { - await waitForFocus(); + /** + * Retrieves a webauthn credential. + * + * @param options Options for creating new credentials. + * @returns Promise that resolves to the new credential object. + */ + async function getWebAuthnCredential(options?: CredentialRequestOptions): Promise { + if (!isWebauthnCall(options)) { return await browserCredentials.get(options); } - throw error; - } -} + const fallbackSupported = browserNativeWebauthnSupport; -function isWebauthnCall(options?: CredentialCreationOptions | CredentialRequestOptions) { - return options && "publicKey" in options; -} + try { + if (options?.mediation && options.mediation !== "optional") { + throw new FallbackRequestedError(); + } -/** - * Wait for window to be focused. - * Safari doesn't allow scripts to trigger webauthn when window is not focused. - * - * @param fallbackWait How long to wait when the script is not able to add event listeners to `window.top`. Defaults to 500ms. - * @param timeout Maximum time to wait for focus in milliseconds. Defaults to 5 minutes. - * @returns Promise that resolves when window is focused, or rejects if timeout is reached. - */ -async function waitForFocus(fallbackWait = 500, timeout = 5 * 60 * 1000) { - try { - if (window.top.document.hasFocus()) { - return; + const response = await messenger.request( + { + type: MessageType.CredentialGetRequest, + data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported), + }, + options?.signal, + ); + + if (response.type !== MessageType.CredentialGetResponse) { + throw new Error("Something went wrong."); + } + + return WebauthnUtils.mapCredentialAssertResult(response.result); + } catch (error) { + if (error && error.fallbackRequested && fallbackSupported) { + await waitForFocus(); + return await browserCredentials.get(options); + } + + throw error; } - } catch { - // Cannot access window.top due to cross-origin frame, fallback to waiting - return await new Promise((resolve) => window.setTimeout(resolve, fallbackWait)); } - let focusListener; - const focusPromise = new Promise((resolve) => { - focusListener = () => resolve(); - window.top.addEventListener("focus", focusListener); - }); - - let timeoutId; - const timeoutPromise = new Promise((_, reject) => { - timeoutId = window.setTimeout( - () => - reject( - new DOMException("The operation either timed out or was not allowed.", "AbortError"), - ), - timeout, - ); - }); - - try { - await Promise.race([focusPromise, timeoutPromise]); - } finally { - window.top.removeEventListener("focus", focusListener); - window.clearTimeout(timeoutId); - } -} - -/** - * Sets up a listener to handle cleanup or reconnection when the extension's - * context changes due to being reloaded or unloaded. - */ -messenger.handler = (message, abortController) => { - const type = message.type; - - // Handle cleanup for disconnect request - if (type === MessageType.DisconnectRequest && browserNativeWebauthnSupport) { - navigator.credentials.create = browserCredentials.create; - navigator.credentials.get = browserCredentials.get; + function isWebauthnCall(options?: CredentialCreationOptions | CredentialRequestOptions) { + return options && "publicKey" in options; } - // Handle reinitialization for reconnect request - if (type === MessageType.ReconnectRequest && browserNativeWebauthnSupport) { - navigator.credentials.create = createWebAuthnCredential; - navigator.credentials.get = getWebAuthnCredential; + /** + * Wait for window to be focused. + * Safari doesn't allow scripts to trigger webauthn when window is not focused. + * + * @param fallbackWait How long to wait when the script is not able to add event listeners to `window.top`. Defaults to 500ms. + * @param timeout Maximum time to wait for focus in milliseconds. Defaults to 5 minutes. + * @returns Promise that resolves when window is focused, or rejects if timeout is reached. + */ + async function waitForFocus(fallbackWait = 500, timeout = 5 * 60 * 1000) { + try { + if (globalContext.top.document.hasFocus()) { + return; + } + } catch { + // Cannot access window.top due to cross-origin frame, fallback to waiting + return await new Promise((resolve) => globalContext.setTimeout(resolve, fallbackWait)); + } + + const focusPromise = new Promise((resolve) => { + focusListenerHandler = () => resolve(); + globalContext.top.addEventListener("focus", focusListenerHandler); + }); + + const timeoutPromise = new Promise((_, reject) => { + waitForFocusTimeout = globalContext.setTimeout( + () => + reject( + new DOMException("The operation either timed out or was not allowed.", "AbortError"), + ), + timeout, + ); + }); + + try { + await Promise.race([focusPromise, timeoutPromise]); + } finally { + clearWaitForFocus(); + } } -}; + + function clearWaitForFocus() { + globalContext.top.removeEventListener("focus", focusListenerHandler); + if (waitForFocusTimeout) { + globalContext.clearTimeout(waitForFocusTimeout); + } + } + + function destroy() { + try { + if (browserNativeWebauthnSupport) { + navigator.credentials.create = browserCredentials.create; + navigator.credentials.get = browserCredentials.get; + } else { + (navigator as any).credentials = BrowserNavigatorCredentials; + globalContext.PublicKeyCredential = BrowserPublicKeyCredential; + globalContext.AuthenticatorAttestationResponse = BrowserAuthenticatorAttestationResponse; + } + + clearWaitForFocus(); + void messenger.destroy(); + } catch (e) { + /** empty */ + } + } + + /** + * Sets up a listener to handle cleanup or reconnection when the extension's + * context changes due to being reloaded or unloaded. + */ + messenger.handler = (message) => { + const type = message.type; + + // Handle cleanup for disconnect request + if (type === MessageType.DisconnectRequest) { + destroy(); + } + }; +})(globalThis); diff --git a/apps/browser/src/vault/fido2/content/page-script.webauthn-supported.spec.ts b/apps/browser/src/vault/fido2/content/page-script.webauthn-supported.spec.ts new file mode 100644 index 0000000000..211959c466 --- /dev/null +++ b/apps/browser/src/vault/fido2/content/page-script.webauthn-supported.spec.ts @@ -0,0 +1,121 @@ +import { + createAssertCredentialResultMock, + createCreateCredentialResultMock, + createCredentialCreationOptionsMock, + createCredentialRequestOptionsMock, + setupMockedWebAuthnSupport, +} from "../../../autofill/spec/fido2-testing-utils"; +import { WebauthnUtils } from "../webauthn-utils"; + +import { MessageType } from "./messaging/message"; +import { Messenger } from "./messaging/messenger"; + +let messenger: Messenger; +jest.mock("./messaging/messenger", () => { + return { + Messenger: class extends jest.requireActual("./messaging/messenger").Messenger { + static forDOMCommunication: any = jest.fn((window) => { + const windowOrigin = window.location.origin; + + messenger = new Messenger({ + postMessage: (message, port) => window.postMessage(message, windowOrigin, [port]), + addEventListener: (listener) => window.addEventListener("message", listener), + removeEventListener: (listener) => window.removeEventListener("message", listener), + }); + messenger.destroy = jest.fn(); + return messenger; + }); + }, + }; +}); +jest.mock("../webauthn-utils"); + +describe("Fido2 page script with native WebAuthn support", () => { + const mockCredentialCreationOptions = createCredentialCreationOptionsMock(); + const mockCreateCredentialsResult = createCreateCredentialResultMock(); + const mockCredentialRequestOptions = createCredentialRequestOptionsMock(); + const mockCredentialAssertResult = createAssertCredentialResultMock(); + setupMockedWebAuthnSupport(); + + require("./page-script"); + + afterAll(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + describe("creating WebAuthn credentials", () => { + beforeEach(() => { + messenger.request = jest.fn().mockResolvedValue({ + type: MessageType.CredentialCreationResponse, + result: mockCreateCredentialsResult, + }); + }); + + it("falls back to the default browser credentials API if an error occurs", async () => { + window.top.document.hasFocus = jest.fn().mockReturnValue(true); + messenger.request = jest.fn().mockRejectedValue({ fallbackRequested: true }); + + try { + await navigator.credentials.create(mockCredentialCreationOptions); + expect("This will fail the test").toBe(true); + } catch { + expect(WebauthnUtils.mapCredentialRegistrationResult).not.toHaveBeenCalled(); + } + }); + + it("creates and returns a WebAuthn credential when the navigator API is called to create credentials", async () => { + await navigator.credentials.create(mockCredentialCreationOptions); + + expect(WebauthnUtils.mapCredentialCreationOptions).toHaveBeenCalledWith( + mockCredentialCreationOptions, + true, + ); + expect(WebauthnUtils.mapCredentialRegistrationResult).toHaveBeenCalledWith( + mockCreateCredentialsResult, + ); + }); + }); + + describe("get WebAuthn credentials", () => { + beforeEach(() => { + messenger.request = jest.fn().mockResolvedValue({ + type: MessageType.CredentialGetResponse, + result: mockCredentialAssertResult, + }); + }); + + it("falls back to the default browser credentials API when an error occurs", async () => { + window.top.document.hasFocus = jest.fn().mockReturnValue(true); + messenger.request = jest.fn().mockRejectedValue({ fallbackRequested: true }); + + const returnValue = await navigator.credentials.get(mockCredentialRequestOptions); + + expect(returnValue).toBeDefined(); + expect(WebauthnUtils.mapCredentialAssertResult).not.toHaveBeenCalled(); + }); + + it("gets and returns the WebAuthn credentials", async () => { + await navigator.credentials.get(mockCredentialRequestOptions); + + expect(WebauthnUtils.mapCredentialRequestOptions).toHaveBeenCalledWith( + mockCredentialRequestOptions, + true, + ); + expect(WebauthnUtils.mapCredentialAssertResult).toHaveBeenCalledWith( + mockCredentialAssertResult, + ); + }); + }); + + describe("destroy", () => { + it("should destroy the message listener when receiving a disconnect request", async () => { + jest.spyOn(globalThis.top, "removeEventListener"); + const SENDER = "bitwarden-webauthn"; + void messenger.handler({ type: MessageType.DisconnectRequest, SENDER, senderId: "1" }); + + expect(globalThis.top.removeEventListener).toHaveBeenCalledWith("focus", undefined); + expect(messenger.destroy).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/browser/src/vault/fido2/content/page-script.webauthn-unsupported.spec.ts b/apps/browser/src/vault/fido2/content/page-script.webauthn-unsupported.spec.ts new file mode 100644 index 0000000000..f3aee685e1 --- /dev/null +++ b/apps/browser/src/vault/fido2/content/page-script.webauthn-unsupported.spec.ts @@ -0,0 +1,96 @@ +import { + createAssertCredentialResultMock, + createCreateCredentialResultMock, + createCredentialCreationOptionsMock, + createCredentialRequestOptionsMock, +} from "../../../autofill/spec/fido2-testing-utils"; +import { WebauthnUtils } from "../webauthn-utils"; + +import { MessageType } from "./messaging/message"; +import { Messenger } from "./messaging/messenger"; + +let messenger: Messenger; +jest.mock("./messaging/messenger", () => { + return { + Messenger: class extends jest.requireActual("./messaging/messenger").Messenger { + static forDOMCommunication: any = jest.fn((window) => { + const windowOrigin = window.location.origin; + + messenger = new Messenger({ + postMessage: (message, port) => window.postMessage(message, windowOrigin, [port]), + addEventListener: (listener) => window.addEventListener("message", listener), + removeEventListener: (listener) => window.removeEventListener("message", listener), + }); + messenger.destroy = jest.fn(); + return messenger; + }); + }, + }; +}); +jest.mock("../webauthn-utils"); + +describe("Fido2 page script without native WebAuthn support", () => { + const mockCredentialCreationOptions = createCredentialCreationOptionsMock(); + const mockCreateCredentialsResult = createCreateCredentialResultMock(); + const mockCredentialRequestOptions = createCredentialRequestOptionsMock(); + const mockCredentialAssertResult = createAssertCredentialResultMock(); + require("./page-script"); + + afterAll(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + describe("creating WebAuthn credentials", () => { + beforeEach(() => { + messenger.request = jest.fn().mockResolvedValue({ + type: MessageType.CredentialCreationResponse, + result: mockCreateCredentialsResult, + }); + }); + + it("creates and returns a WebAuthn credential", async () => { + await navigator.credentials.create(mockCredentialCreationOptions); + + expect(WebauthnUtils.mapCredentialCreationOptions).toHaveBeenCalledWith( + mockCredentialCreationOptions, + false, + ); + expect(WebauthnUtils.mapCredentialRegistrationResult).toHaveBeenCalledWith( + mockCreateCredentialsResult, + ); + }); + }); + + describe("get WebAuthn credentials", () => { + beforeEach(() => { + messenger.request = jest.fn().mockResolvedValue({ + type: MessageType.CredentialGetResponse, + result: mockCredentialAssertResult, + }); + }); + + it("gets and returns the WebAuthn credentials", async () => { + await navigator.credentials.get(mockCredentialRequestOptions); + + expect(WebauthnUtils.mapCredentialRequestOptions).toHaveBeenCalledWith( + mockCredentialRequestOptions, + false, + ); + expect(WebauthnUtils.mapCredentialAssertResult).toHaveBeenCalledWith( + mockCredentialAssertResult, + ); + }); + }); + + describe("destroy", () => { + it("should destroy the message listener when receiving a disconnect request", async () => { + jest.spyOn(globalThis.top, "removeEventListener"); + const SENDER = "bitwarden-webauthn"; + void messenger.handler({ type: MessageType.DisconnectRequest, SENDER, senderId: "1" }); + + expect(globalThis.top.removeEventListener).toHaveBeenCalledWith("focus", undefined); + expect(messenger.destroy).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/browser/src/vault/fido2/content/trigger-fido2-content-script-injection.spec.ts b/apps/browser/src/vault/fido2/content/trigger-fido2-content-script-injection.spec.ts deleted file mode 100644 index 8f4efe0330..0000000000 --- a/apps/browser/src/vault/fido2/content/trigger-fido2-content-script-injection.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -describe("TriggerFido2ContentScriptInjection", () => { - afterEach(() => { - jest.resetModules(); - jest.clearAllMocks(); - }); - - describe("init", () => { - it("sends a message to the extension background", () => { - require("../content/trigger-fido2-content-script-injection"); - - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({ - command: "triggerFido2ContentScriptInjection", - }); - }); - }); -}); diff --git a/apps/browser/src/vault/fido2/content/trigger-fido2-content-script-injection.ts b/apps/browser/src/vault/fido2/content/trigger-fido2-content-script-injection.ts deleted file mode 100644 index 7ca6956729..0000000000 --- a/apps/browser/src/vault/fido2/content/trigger-fido2-content-script-injection.ts +++ /dev/null @@ -1,5 +0,0 @@ -(function () { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - chrome.runtime.sendMessage({ command: "triggerFido2ContentScriptInjection" }); -})(); diff --git a/apps/browser/src/vault/fido2/enums/fido2-content-script.enum.ts b/apps/browser/src/vault/fido2/enums/fido2-content-script.enum.ts new file mode 100644 index 0000000000..287de6804b --- /dev/null +++ b/apps/browser/src/vault/fido2/enums/fido2-content-script.enum.ts @@ -0,0 +1,10 @@ +export const Fido2ContentScript = { + PageScript: "content/fido2/page-script.js", + PageScriptAppend: "content/fido2/page-script-append-mv2.js", + ContentScript: "content/fido2/content-script.js", +} as const; + +export const Fido2ContentScriptId = { + PageScript: "fido2-page-script-registration", + ContentScript: "fido2-content-script-registration", +} as const; diff --git a/apps/browser/src/vault/fido2/enums/fido2-port-name.enum.ts b/apps/browser/src/vault/fido2/enums/fido2-port-name.enum.ts new file mode 100644 index 0000000000..7836247425 --- /dev/null +++ b/apps/browser/src/vault/fido2/enums/fido2-port-name.enum.ts @@ -0,0 +1,3 @@ +export const Fido2PortName = { + InjectedScript: "fido2-injected-content-script-port", +} as const; diff --git a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts index b27a986231..a566b054c0 100644 --- a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts +++ b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts @@ -304,7 +304,7 @@ export class AddEditComponent extends BaseAddEditComponent { } private saveCipherState() { - return this.stateService.setAddEditCipherInfo({ + return this.cipherService.setAddEditCipherInfo({ cipher: this.cipher, collectionIds: this.collections == null diff --git a/apps/browser/src/vault/popup/components/vault/current-tab.component.html b/apps/browser/src/vault/popup/components/vault/current-tab.component.html index fc8b4212ba..0b2e16d09d 100644 --- a/apps/browser/src/vault/popup/components/vault/current-tab.component.html +++ b/apps/browser/src/vault/popup/components/vault/current-tab.component.html @@ -40,12 +40,22 @@ *ngIf=" (unassignedItemsBannerEnabled$ | async) && (unassignedItemsBannerService.showBanner$ | async) && - (unassignedItemsBannerService.bannerText$ | async) + !(unassignedItemsBannerService.loading$ | async) " type="info" >

{{ unassignedItemsBannerService.bannerText$ | async | i18n }} + {{ "unassignedItemsBannerCTAPartOne" | i18n }} + {{ "adminConsole" | i18n }} + {{ "unassignedItemsBannerCTAPartTwo" | i18n }} Promise; - injectFido2ContentScripts: (sender: chrome.runtime.MessageSender) => Promise; -} diff --git a/apps/browser/src/vault/services/fido2.service.spec.ts b/apps/browser/src/vault/services/fido2.service.spec.ts deleted file mode 100644 index 1db2bdfb77..0000000000 --- a/apps/browser/src/vault/services/fido2.service.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { BrowserApi } from "../../platform/browser/browser-api"; - -import Fido2Service from "./fido2.service"; - -describe("Fido2Service", () => { - let fido2Service: Fido2Service; - let tabMock: chrome.tabs.Tab; - let sender: chrome.runtime.MessageSender; - - beforeEach(() => { - fido2Service = new Fido2Service(); - tabMock = { id: 123, url: "https://bitwarden.com" } as chrome.tabs.Tab; - sender = { tab: tabMock }; - jest.spyOn(BrowserApi, "executeScriptInTab").mockImplementation(); - }); - - afterEach(() => { - jest.resetModules(); - jest.clearAllMocks(); - }); - - describe("injectFido2ContentScripts", () => { - const fido2ContentScript = "content/fido2/content-script.js"; - const defaultExecuteScriptOptions = { runAt: "document_start" }; - - it("accepts an extension message sender and injects the fido2 scripts into the tab of the sender", async () => { - await fido2Service.injectFido2ContentScripts(sender); - - expect(BrowserApi.executeScriptInTab).toHaveBeenCalledWith(tabMock.id, { - file: fido2ContentScript, - ...defaultExecuteScriptOptions, - }); - }); - }); -}); diff --git a/apps/browser/src/vault/services/fido2.service.ts b/apps/browser/src/vault/services/fido2.service.ts deleted file mode 100644 index 98b440b109..0000000000 --- a/apps/browser/src/vault/services/fido2.service.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { BrowserApi } from "../../platform/browser/browser-api"; - -import { Fido2Service as Fido2ServiceInterface } from "./abstractions/fido2.service"; - -export default class Fido2Service implements Fido2ServiceInterface { - async init() { - const tabs = await BrowserApi.tabsQuery({}); - tabs.forEach((tab) => { - if (tab.url?.startsWith("https")) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.injectFido2ContentScripts({ tab } as chrome.runtime.MessageSender); - } - }); - - BrowserApi.addListener(chrome.runtime.onConnect, (port) => { - if (port.name === "fido2ContentScriptReady") { - port.postMessage({ command: "fido2ContentScriptInit" }); - } - }); - } - - /** - * Injects the FIDO2 content script into the current tab. - * @param {chrome.runtime.MessageSender} sender - * @returns {Promise} - */ - async injectFido2ContentScripts(sender: chrome.runtime.MessageSender): Promise { - await BrowserApi.executeScriptInTab(sender.tab.id, { - file: "content/fido2/content-script.js", - frameId: sender.frameId, - runAt: "document_start", - }); - } -} diff --git a/apps/browser/store/locales/ar/copy.resx b/apps/browser/store/locales/ar/copy.resx index e74606ff15..e1bfa48b44 100644 --- a/apps/browser/store/locales/ar/copy.resx +++ b/apps/browser/store/locales/ar/copy.resx @@ -1,17 +1,17 @@  - @@ -118,42 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - مدير كلمات مرور مجاني + Bitwarden Password Manager - مدير كلمات مرور مجاني وآمن لجميع أجهزتك + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - شركة Bitwarden, Inc هي الشركة الأم لشركة 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -تم تصنيف Bitwarden كأفصل مدير كلمات مرور بواسطة كل من The Verge، U.S News & World Report، CNET، وغيرهم. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -قم بادراة وحفظ وتأمين كلمات المرور الخاصة بك، ومشاركتها بين اجهزتك من اي مكان. -يوفر Bitwarden حل مفتوح المصدر لادارة كلمات المرور للجميع، سواء في المنزل، في العمل او في اي مكان. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -قم بانشاء كلمات مرور قوية وفريدة وعشوائية حسب متطلبات الأمان للصفحات التي تزورها. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -يوفر Bitwarden Send امكانية ارسال البيانات --- النصوص والملفات --- بطريقة مشفرة وسريعة لأي شخص. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -يوفر Bitwarden خطط خاصة للفرق والشركات والمؤسسات لتمكنك من مشاركة كلمات المرور مع زملائك في العمل. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -لماذا قد تختار Bitwarden: -تشفير على مستوى عالمي -كلمات المرور محمية بتشفير متقدم تام (end-to-end encryption) من نوعية AES-256 bit، مع salted hashing، و PBKDF2 SHA-256. كل هذا لابقاء بياناتك محمية وخاصة. +More reasons to choose Bitwarden: -مولد كلمات المرور المدمج -قم بانشاء كلمات مرور قوية وفريدة وعشوائية حسب متطلبات الأمان للصفحات التي تزورها. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -الترجمات العالمية -يتوفر Bitwarden باكثر من 40 لغة، وتتنامى الترجمات بفضل مجتمعنا العالمي. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -تطبيقات متعددة المنصات -قم بحماية ومشاركة بياناتاك الحساسة عبر خزنة Bitwarden من اي متصفح ويب، او هاتف ذكي، او جهاز كمبيوتر، وغيرها. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - مدير كلمات مرور مجاني وآمن لجميع أجهزتك + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. مزامنة خزنتك والوصول إليها من عدة أجهزة diff --git a/apps/browser/store/locales/az/copy.resx b/apps/browser/store/locales/az/copy.resx index cb05f8e5d9..2a3d507df2 100644 --- a/apps/browser/store/locales/az/copy.resx +++ b/apps/browser/store/locales/az/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Ödənişsiz Parol Meneceri + Bitwarden Password Manager - Bütün cihazlarınız üçün güvənli və ödənişsiz bir parol meneceri + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc., 8bit Solutions LLC-nin ana şirkətidir. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -THE VERGE, U.S. NEWS & WORLD REPORT, CNET VƏ BİR ÇOXUNA GÖRƏ ƏN YAXŞI PAROL MENECERİDİR. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Hər yerdən limitsiz cihazda limitsiz parolu idarə edin, saxlayın, qoruyun və paylaşın. Bitwarden evdə, işdə və ya yolda hər kəsə açıq mənbəli parol idarəetmə həllərini təqdim edir. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Çox istifadə etdiyiniz hər veb sayt üçün təhlükəsizlik tələblərinə görə güclü, unikal və təsadüfi parollar yaradın. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send şifrələnmiş məlumatların (fayl və sadə mətnləri) birbaşa və sürətli göndərilməsini təmin edir. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden, parolları iş yoldaşlarınızla təhlükəsiz paylaşa bilməyiniz üçün şirkətlərə Teams və Enterprise planları təklif edir. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Nəyə görə Bitwarden-i seçməliyik: -Yüksək səviyyə şifrələmə -Parollarınız qabaqcıl ucdan-uca şifrələmə (AES-256 bit, salted hashtag və PBKDF2 SHA-256) ilə qorunur, beləcə datanızın güvənli və gizli qalmasını təmin edir. +More reasons to choose Bitwarden: -Daxili parol yaradıcı -Çox istifadə etdiyiniz hər veb sayt üçün təhlükəsizlik tələblərinə görə güclü, unikal və təsadüfi şifrələr yaradın. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Qlobal tərcümələr -Bitwarden tərcümələri 40 dildə mövcuddur və qlobal cəmiyyətimiz sayəsində böyüməyə davam edir. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Çarpaz platform tətbiqləri -Bitwarden anbarındakı həssas verilənləri, istənilən brauzerdən, mobil cihazdan və ya masaüstü əməliyyat sistemindən və daha çoxundan qoruyub paylaşın. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Bütün cihazlarınız üçün güvənli və ödənişsiz bir parol meneceri + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Anbarınıza bir neçə cihazdan eyniləşdirərək müraciət edin diff --git a/apps/browser/store/locales/be/copy.resx b/apps/browser/store/locales/be/copy.resx index f84dd699a7..65c337826b 100644 --- a/apps/browser/store/locales/be/copy.resx +++ b/apps/browser/store/locales/be/copy.resx @@ -1,17 +1,17 @@  - @@ -118,24 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – бясплатны менеджар пароляў + Bitwarden Password Manager - Бяспечны і бясплатны менеджар пароляў для ўсіх вашых прылад + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden - просты і бяспечны спосаб захоўваць усе вашы імёны карыстальніка і паролі, а таксама лёгка іх сінхранізаваць паміж усімі вашымі прыладамі. Пашырэнне праграмы Bitwarden дазваляе хутка ўвайсці на любы вэб-сайт з дапамогай Safari або Chrome і падтрымліваецца сотнямі іншых папулярных праграм. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -Крадзеж пароляў — сур'ёзная праблема. Сайты і праграмы, якія вы выкарыстоўваеце падвяргаюцца нападам кожны дзень. Праблемы ў іх бяспецы могуць прывесці да крадзяжу вашага пароля. Акрамя таго, калі вы выкарыстоўваеце адны і тыя ж паролі на розных сайтах і праграмах, то хакеры могуць лёгка атрымаць доступ да некалькіх вашых уліковых запісаў адразу (да паштовай скрыні, да банкаўскага рахунку ды г. д.). +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Эксперты па бяспецы рэкамендуюць выкарыстоўваць розныя выпадкова знегерыраваныя паролі для кожнага створанага вамі ўліковага запісу. Але як жа кіраваць усімі гэтымі паролямі? Bitwarden дазваляе вам лёгка атрымаць доступ да вашых пароляў, а гэтак жа ствараць і захоўваць іх. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Bitwarden захоўвае ўсе вашы імёны карыстальніка і паролі ў зашыфраваным сховішчы, якое сінхранізуецца паміж усімі вашымі прыладамі. Да таго, як даныя пакінуць вашу прыладу, яны будуць зашыфраваны і толькі потым адпраўлены. Мы ў Bitwarden не зможам прачытаць вашы даныя, нават калі мы гэтага захочам. Вашы даныя зашыфраваны пры дапамозе алгарытму AES-256 і PBKDF2 SHA-256. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden — гэта праграмнае забеспячэнне з адкрытым на 100% зыходным кодам. Зыходны код Bitwarden размешчаны на GitHub, і кожны можа свабодна праглядаць, правяраць і рабіць унёсак у код Bitwarden. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. + +Use Bitwarden to secure your workforce and share sensitive information with colleagues. + + +More reasons to choose Bitwarden: + +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. + +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. + +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - Бяспечны і бясплатны менеджар пароляў для ўсіх вашых прылад + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Сінхранізацыя і доступ да сховішча з некалькіх прылад diff --git a/apps/browser/store/locales/bg/copy.resx b/apps/browser/store/locales/bg/copy.resx index 29c468f045..bc08f6a107 100644 --- a/apps/browser/store/locales/bg/copy.resx +++ b/apps/browser/store/locales/bg/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden — безплатен управител на пароли + Bitwarden Password Manager - Сигурен и свободен управител на пароли за всички устройства + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - „Bitwarden, Inc.“ е компанията-майка на „8bit Solutions LLC“. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -ОПРЕДЕЛЕН КАТО НАЙ-ДОБРИЯТ УПРАВИТЕЛ НА ПАРОЛИ ОТ „THE VERGE“, „U.S. NEWS & WORLD REPORT“, „CNET“ И ОЩЕ. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Управлявайте, съхранявайте, защитавайте и споделяйте неограничен брой пароли на неограничен брой устройства от всяка точка на света. Битуорден предоставя решение за управление на паролите с отворен код, от което може да се възползва всеки, било то у дома, на работа или на път. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Създавайте сигурни, уникални и случайни пароли според изискванията за сигурност на всеки уеб сайт, който посещавате. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -С Изпращанията на Битуорден можете незабавно да предавате шифрована информация под формата на файлове и обикновен текст – директно и с всекиго. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Битуорден предлага планове за екипи и големи фирми, така че служителите в компаниите да могат безопасно да споделят пароли помежду си. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Защо да изберете Битуорден: -Шифроване от най-висока класа -Паролите са защитени със сложен шифър „от край до край“ (AES-256 bit, salted hashtag и PBKDF2 SHA-256), така че данните Ви остават да са защитени и неприкосновени. +More reasons to choose Bitwarden: -Вграден генератор на пароли -Създавайте сигурни, уникални и случайни пароли според изискванията за сигурност на всеки уеб сайт, който посещавате. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Глобални преводи -Битуорден е преведен на 40 езика и този брой не спира да расте, благодарение на нашата глобална общност. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Приложения за всяка система -Защитете и споделяйте поверителните данни от своя трезор в Битуорден от всеки браузър, мобилно устройство или компютър. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Сигурен и свободен управител на пароли за всички устройства + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Удобен достъп до трезора, който се синхронизира от всички устройства diff --git a/apps/browser/store/locales/bn/copy.resx b/apps/browser/store/locales/bn/copy.resx index a8eb4f7c75..1bcfb19001 100644 --- a/apps/browser/store/locales/bn/copy.resx +++ b/apps/browser/store/locales/bn/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – বিনামূল্যের পাসওয়ার্ড ব্যবস্থাপক + Bitwarden Password Manager - আপনার সমস্ত ডিভাইসের জন্য একটি সুরক্ষিত এবং বিনামূল্যের পাসওয়ার্ড ব্যবস্থাপক + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - আপনার সমস্ত ডিভাইসের জন্য একটি সুরক্ষিত এবং বিনামূল্যের পাসওয়ার্ড ব্যবস্থাপক + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. একাধিক ডিভাইস থেকে আপনার ভল্ট সিঙ্ক এবং ব্যাবহার করুন diff --git a/apps/browser/store/locales/bs/copy.resx b/apps/browser/store/locales/bs/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/bs/copy.resx +++ b/apps/browser/store/locales/bs/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/ca/copy.resx b/apps/browser/store/locales/ca/copy.resx index 0bd454addb..27e685841b 100644 --- a/apps/browser/store/locales/ca/copy.resx +++ b/apps/browser/store/locales/ca/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Administrador de contrasenyes gratuït + Bitwarden Password Manager - Administrador de contrasenyes segur i gratuït per a tots els vostres dispositius + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. és la companyia matriu de solucions de 8 bits LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -Nomenada Millor gestor de contrasenyes per THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Gestioneu, emmagatzemeu, segures i compartiu contrasenyes il·limitades a través de dispositius il·limitats des de qualsevol lloc. Bitwarden lliura solucions de gestió de contrasenyes de codi obert a tothom, ja siga a casa, a la feina o sobre la marxa. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Genereu contrasenyes fortes, úniques i aleatòries basades en els requisits de seguretat per a cada lloc web que freqüenteu. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send transmet ràpidament informació xifrada --- Fitxers i text complet - directament a qualsevol persona. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden ofereix equips i plans empresarials per a empreses perquè pugueu compartir amb seguretat contrasenyes amb els companys. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Per què triar Bitwarden: -Xifratge de classe mundial -Les contrasenyes estan protegides amb un xifratge avançat fi-a-fi (AES-256 bit, salted hashtag, and PBKDF2 SHA-256), de manera que les vostres dades es mantenen segures i privades. +More reasons to choose Bitwarden: -Generador de contrasenyes integrat -Genereu contrasenyes fortes, úniques i aleatòries basades en els requisits de seguretat per a cada lloc web que freqüenteu. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Traduccions globals -Les traduccions de Bitwarden existeixen en 40 idiomes i creixen, gràcies a la nostra comunitat global. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Aplicacions de plataforma creuada -Assegureu-vos i compartiu dades sensibles a la vostra caixa forta de Bitwarden des de qualsevol navegador, dispositiu mòbil o S.O. d'escriptori, i molt més. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Administrador de contrasenyes segur i gratuït per a tots els vostres dispositius + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sincronitzeu i accediu a la vostra caixa forta des de diversos dispositius diff --git a/apps/browser/store/locales/cs/copy.resx b/apps/browser/store/locales/cs/copy.resx index 6b711e2863..59d8c60b40 100644 --- a/apps/browser/store/locales/cs/copy.resx +++ b/apps/browser/store/locales/cs/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Bezplatný správce hesel + Bitwarden Password Manager - Bezpečný a bezplatný správce hesel pro všechna Vaše zařízení + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. je mateřskou společností 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -THE VERGE, U.S. NEWS & WORLD REPORT, CNET A DALŠÍ JI OZNAČILY ZA NEJLEPŠÍHO SPRÁVCE HESEL. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Spravujte, ukládejte, zabezpečujte a sdílejte neomezený počet hesel na neomezeném počtu zařízení odkudkoliv. Bitwarden poskytuje open source řešení pro správu hesel všem, ať už doma, v práci nebo na cestách. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generujte silná, jedinečná a náhodná hesla na základě bezpečnostních požadavků pro každou webovou stránku, kterou navštěvujete. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send rychle přenáší šifrované informace --- soubory a prostý text -- přímo a komukoli. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden nabízí plány Teams a Enterprise pro firmy, takže můžete bezpečně sdílet hesla s kolegy. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Proč si vybrat Bitwarden: -Šifrování na světové úrovni -Hesla jsou chráněna pokročilým koncovým šifrováním (AES-256 bit, salted hashování a PBKDF2 SHA-256), takže Vaše data zůstanou bezpečná a soukromá. +More reasons to choose Bitwarden: -Vestavěný generátor hesel -Generujte silná, jedinečná a náhodná hesla na základě bezpečnostních požadavků pro každou webovou stránku, kterou navštěvujete. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Globální překlady -Překlady Bitwarden existují ve 40 jazycích a díky naší globální komunitě se stále rozšiřují. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Aplikace pro více platforem -Zabezpečte a sdílejte citlivá data v rámci svého trezoru Bitwarden z jakéhokoli prohlížeče, mobilního zařízení nebo operačního systému pro počítač. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Bezpečný a bezplatný správce hesel pro všechna Vaše zařízení + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Synchronizujte a přistupujte ke svému trezoru z různých zařízení diff --git a/apps/browser/store/locales/cy/copy.resx b/apps/browser/store/locales/cy/copy.resx index 8222329630..983a112c07 100644 --- a/apps/browser/store/locales/cy/copy.resx +++ b/apps/browser/store/locales/cy/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Rheolydd cyfineiriau am ddim + Bitwarden Password Manager - Rheolydd diogel a rhad ac am ddim ar gyfer eich holl ddyfeisiau + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. yw rhiant-gwmni 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -Y RHEOLYDD CYFRINEIRIAU GORAU YN ÔL THE VERGE, US NEWS & WORLD REPORT, CNET, A MWY. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Rheolwch, storiwch, diogelwch a rhannwch gyfrineiriau di-ri ar draws dyfeiriau di-ri o unrhyw le. Mae Bitwarden yn cynnig rhaglenni rheoli cyfrineiriau cod-agored i bawb, boed gartref, yn y gwaith, neu ar fynd. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Gallwch gynhyrchu cyfrineiriau cryf, unigryw ac ar hap yn seiliedig ar ofynion diogelwch ar gyfer pob gwefan rydych chi'n ei defnyddio. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Mae Bitwarden Send yn trosglwyddo gwybodaeth wedi'i hamgryptio yn gyflym -- ffeiliau a thestun plaen -- yn uniongyrchol i unrhyw un. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Mae Bitwarden yn cynnig cynlluniau Teams ac Enterprise i gwmnïau er mwyn i chi allu rhannu cyfrineiriau gyda chydweithwyr yn ddiogel. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Pam dewis Bitwarden: -Amgryptio o'r radd flaenaf -Mae cyfrineiriau wedi'u hamddiffyn ag amgryptio datblygedig o un pen i'r llall (AES-256 bit, hashio â halen, a PBKDF2 SHA-256) er mwyn i'ch data aros yn ddiogel ac yn breifat. +More reasons to choose Bitwarden: -Cynhyrchydd cyfrineiriau -Gallwch gynhyrchu cyfrineiriau cryf, unigryw ac ar hap yn seiliedig ar ofynion diogelwch ar gyfer pob gwefan rydych chi'n ei defnyddio. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Ar gael yn eich iaith chi -Mae Bitwarden wedi'i gyfieithu i dros 40 o ieithoedd, diolch i'n cymuned fyd-eang. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Rhaglenni traws-blatfform -Diogelwch a rhannwch ddata sensitif yn eich cell Bitwarden o unrhyw borwr, dyfais symudol, neu system weithredu, a mwy. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Rheolydd diogel a rhad ac am ddim ar gyfer eich holl ddyfeisiau + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Gallwch gael mynediad at, a chysoni, eich cell o sawl dyfais diff --git a/apps/browser/store/locales/da/copy.resx b/apps/browser/store/locales/da/copy.resx index 858c56dea9..775a3edd81 100644 --- a/apps/browser/store/locales/da/copy.resx +++ b/apps/browser/store/locales/da/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Gratis adgangskodemanager + Bitwarden Password Manager - En sikker og gratis adgangskodemanager til alle dine enheder + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. er moderselskab for 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -UDNÆVNT BEDSTE PASSWORD MANAGER AF THE VERGE, U.S. NEWS & WORLD REPORT, CNET OG FLERE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Administrér, gem, sikr og del adgangskoder ubegrænset på tværs af enheder hvor som helst. Bitwarden leverer open source adgangskodeadministrationsløsninger til alle, hvad enten det er hjemme, på arbejdspladsen eller på farten. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generér stærke, unikke og tilfældige adgangskoder baseret på sikkerhedskrav til hvert websted, du besøger. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send overfører hurtigt krypterede oplysninger --- filer og almindelig tekst - direkte til enhver. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden tilbyder Teams og Enterprise-planer for virksomheder, så du sikkert kan dele adgangskoder med kolleger. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Hvorfor vælge Bitwarden: -Kryptering i verdensklasse -Adgangskoder er beskyttet med avanceret end-to-end-kryptering (AES-256 bit, saltet hashing og PBKDF2 SHA-256), så dine data forbliver sikre og private. +More reasons to choose Bitwarden: -Indbygget adgangskodegenerator -Generér stærke, unikke og tilfældige adgangskoder baseret på sikkerhedskrav til hvert websted, du besøger. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Globale oversættelser -Bitwarden findes på 40 sprog, og flere kommer til, takket være vores globale fællesskab. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Applikationer på tværs af platforme -Beskyt og del følsomme data i din Bitwarden boks fra enhver browser, mobilenhed eller desktop OS og mere. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - En sikker og gratis adgangskodemanager til alle dine enheder + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Synkroniser og få adgang til din boks fra flere enheder diff --git a/apps/browser/store/locales/de/copy.resx b/apps/browser/store/locales/de/copy.resx index 139a6026fd..2267c6c85e 100644 --- a/apps/browser/store/locales/de/copy.resx +++ b/apps/browser/store/locales/de/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Kostenloser Passwort-Manager + Bitwarden Passwort-Manager - Ein sicherer und kostenloser Passwort-Manager für all deine Geräte + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. ist die Muttergesellschaft von 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -AUSGEZEICHNET ALS BESTER PASSWORTMANAGER VON THE VERGE, U.S. NEWS & WORLD REPORT, CNET UND ANDEREN. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Verwalte, speichere, sichere und teile unbegrenzte Passwörter von überall auf unbegrenzten Geräten. Bitwarden liefert Open-Source-Passwort-Management-Lösungen für alle, sei es zu Hause, am Arbeitsplatz oder unterwegs. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generiere starke, einzigartige und zufällige Passwörter basierend auf Sicherheitsanforderungen für jede Website, die du häufig besuchst. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send überträgt schnell verschlüsselte Informationen - Dateien und Klartext - direkt an jeden. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden bietet Teams und Enterprise Pläne für Unternehmen an, damit du Passwörter sicher mit Kollegen teilen kannst. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Warum Bitwarden: -Weltklasse Verschlüsselung -Passwörter sind durch erweiterte Ende-zu-Ende-Verschlüsselung (AES-256 Bit, salted hashing und PBKDF2 SHA-256) so bleiben deine Daten sicher und privat. +More reasons to choose Bitwarden: -Integrierter Passwortgenerator -Generiere starke, einzigartige und zufällige Passwörter basierend auf Sicherheitsanforderungen für jede Website, die du häufig besuchst. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Globale Übersetzungen -Bitwarden Übersetzungen existieren in 40 Sprachen und wachsen dank unserer globalen Community. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Plattformübergreifende Anwendungen -Sichere und teile vertrauliche Daten in deinem Bitwarden Tresor von jedem Browser, jedem mobilen Gerät oder Desktop-Betriebssystem und mehr. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Ein sicherer und kostenloser Passwort-Manager für all deine Geräte + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Synchronisiere und greife auf deinen Tresor von unterschiedlichen Geräten aus zu diff --git a/apps/browser/store/locales/el/copy.resx b/apps/browser/store/locales/el/copy.resx index 01def6ea5a..fb50f95bdc 100644 --- a/apps/browser/store/locales/el/copy.resx +++ b/apps/browser/store/locales/el/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Δωρεάν Διαχειριστής Κωδικών + Bitwarden Password Manager - Ένας ασφαλής και δωρεάν διαχειριστής κωδικών για όλες τις συσκευές σας + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Η Bitwarden, Inc. είναι η μητρική εταιρεία της 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -ΟΝΟΜΑΣΘΗΚΕ ΩΣ Ο ΚΑΛΥΤΕΡΟΣ ΔΙΑΧΕΙΡΙΣΤΗΣ ΚΩΔΙΚΩΝ ΠΡΟΣΒΑΣΗΣ ΑΠΟ ΤΟ VERGE, το U.S. NEWS & WORLD REPORT, το CNET και άλλα. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Διαχειριστείτε, αποθηκεύστε, ασφαλίστε και μοιραστείτε απεριόριστους κωδικούς πρόσβασης σε απεριόριστες συσκευές από οπουδήποτε. Το Bitwarden παρέχει λύσεις διαχείρισης κωδικών πρόσβασης ανοιχτού κώδικα σε όλους, στο σπίτι, στη δουλειά ή εν κινήσει. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Δημιουργήστε ισχυρούς, μοναδικούς και τυχαίους κωδικούς πρόσβασης βάσει των απαιτήσεων ασφαλείας, για κάθε ιστότοπο που συχνάζετε. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Το Bitwarden Send αποστέλλει γρήγορα κρυπτογραφημένες πληροφορίες --- αρχεία και απλό κείμενο -- απευθείας σε οποιονδήποτε. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Το Bitwarden προσφέρει προγράμματα Teams και Enterprise για εταιρείες, ώστε να μπορείτε να μοιράζεστε με ασφάλεια τους κωδικούς πρόσβασης με τους συναδέλφους σας. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Γιατί να επιλέξετε το Bitwarden: -Κρυπτογράφηση παγκόσμιας κλάσης -Οι κωδικοί πρόσβασης προστατεύονται με προηγμένη end-to-end κρυπτογράφηση (AES-256 bit, salted hashing και PBKDF2 SHA-256), ώστε τα δεδομένα σας να παραμένουν ασφαλή και ιδιωτικά. +More reasons to choose Bitwarden: -Ενσωματωμένη Γεννήτρια Κωδικών Πρόσβασης -Δημιουργήστε ισχυρούς, μοναδικούς και τυχαίους κωδικούς πρόσβασης βάσει των απαιτήσεων ασφαλείας, για κάθε ιστότοπο που συχνάζετε. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Παγκόσμιες Μεταφράσεις -Υπάρχουν μεταφράσεις για το Bitwarden σε 40 γλώσσες και αυξάνονται συνεχώς, χάρη στην παγκόσμια κοινότητά μας. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Εφαρμογές για όλες τις πλατφόρμες -Ασφαλίστε και μοιραστείτε ευαίσθητα δεδομένα εντός του Bitwarden Vault από οποιοδήποτε πρόγραμμα περιήγησης, κινητή συσκευή ή λειτουργικό σύστημα υπολογιστών, και πολλά άλλα. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Ένας ασφαλής και δωρεάν διαχειριστής κωδικών για όλες τις συσκευές σας + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Συγχρονίστε και αποκτήστε πρόσβαση στο θησαυροφυλάκιό σας από πολλαπλές συσκευές diff --git a/apps/browser/store/locales/en/copy.resx b/apps/browser/store/locales/en/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/en/copy.resx +++ b/apps/browser/store/locales/en/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/en_GB/copy.resx b/apps/browser/store/locales/en_GB/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/en_GB/copy.resx +++ b/apps/browser/store/locales/en_GB/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/en_IN/copy.resx b/apps/browser/store/locales/en_IN/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/en_IN/copy.resx +++ b/apps/browser/store/locales/en_IN/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/es/copy.resx b/apps/browser/store/locales/es/copy.resx index dc7484777a..472697d825 100644 --- a/apps/browser/store/locales/es/copy.resx +++ b/apps/browser/store/locales/es/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Gestor de contraseñas gratuito + Bitwarden Password Manager - Un gestor de contraseñas seguro y gratuito para todos tus dispositivos + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. es la empresa matriz de 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NOMBRADO MEJOR ADMINISTRADOR DE CONTRASEÑAS POR THE VERGE, U.S. NEWS & WORLD REPORT, CNET Y MÁS. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Administre, almacene, proteja y comparta contraseñas ilimitadas en dispositivos ilimitados desde cualquier lugar. Bitwarden ofrece soluciones de gestión de contraseñas de código abierto para todos, ya sea en casa, en el trabajo o en mientras estás de viaje. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Genere contraseñas seguras, únicas y aleatorias en función de los requisitos de seguridad de cada sitio web que frecuenta. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send transmite rápidamente información cifrada --- archivos y texto sin formato, directamente a cualquier persona. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden ofrece planes Teams y Enterprise para empresas para que pueda compartir contraseñas de forma segura con colegas. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -¿Por qué elegir Bitwarden? -Cifrado de clase mundial -Las contraseñas están protegidas con cifrado avanzado de extremo a extremo (AES-256 bits, salted hashing y PBKDF2 SHA-256) para que sus datos permanezcan seguros y privados. +More reasons to choose Bitwarden: -Generador de contraseñas incorporado -Genere contraseñas fuertes, únicas y aleatorias en función de los requisitos de seguridad de cada sitio web que frecuenta. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Traducciones Globales -Las traducciones de Bitwarden existen en 40 idiomas y están creciendo, gracias a nuestra comunidad global. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Aplicaciones multiplataforma -Proteja y comparta datos confidenciales dentro de su Caja Fuerte de Bitwarden desde cualquier navegador, dispositivo móvil o sistema operativo de escritorio, y más. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Un gestor de contraseñas seguro y gratuito para todos tus dispositivos + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sincroniza y accede a tu caja fuerte desde múltiples dispositivos diff --git a/apps/browser/store/locales/et/copy.resx b/apps/browser/store/locales/et/copy.resx index 2014ec88a8..eccbeba1ed 100644 --- a/apps/browser/store/locales/et/copy.resx +++ b/apps/browser/store/locales/et/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Tasuta paroolihaldur + Bitwarden Password Manager - Tasuta ja turvaline paroolihaldur kõikidele sinu seadmetele + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Tasuta ja turvaline paroolihaldur kõikidele Sinu seadmetele + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sünkroniseeri ja halda oma kontot erinevates seadmetes diff --git a/apps/browser/store/locales/eu/copy.resx b/apps/browser/store/locales/eu/copy.resx index e5b3d542e3..e4271e8ae3 100644 --- a/apps/browser/store/locales/eu/copy.resx +++ b/apps/browser/store/locales/eu/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden — Doaneko pasahitz kudeatzailea + Bitwarden Password Manager - Bitwarden, zure gailu guztietarako pasahitzen kudeatzaile seguru eta doakoa + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. 8bit Solutions LLC-ren enpresa matrizea da. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -THE VERGE, U.S. NEWS & WORLD REPORT ETA CNET ENPRESEK PASAHITZ-ADMINISTRATZAILE ONENA izendatu dute, besteak beste. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Gailu guztien artean pasahitz mugagabeak kudeatu, biltegiratu, babestu eta partekatzen ditu. Bitwardenek kode irekiko pasahitzak administratzeko irtenbideak eskaintzen ditu, bai etxean, bai lanean, bai bidaiatzen ari zaren bitartean. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Pasahitz sendoak, bakarrak eta ausazkoak sortzen ditu, webgune bakoitzaren segurtasun-baldintzetan oinarrituta. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send-ek azkar transmititzen du zifratutako informazioa --- artxiboak eta testu sinplea -- edozein pertsonari zuzenean. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden-ek Taldeak eta Enpresak planak eskaintzen ditu, enpresa bereko lankideek pasahitzak modu seguruan parteka ditzaten. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Zergatik aukeratu Bitwarden: -Mundu-mailako zifratzea -Pasahitzak muturretik muturrerako zifratze aurreratuarekin babestuta daude (AES-256 bit, salted hashtag eta PBKDF2 SHA-256), zure informazioa seguru eta pribatu egon dadin. +More reasons to choose Bitwarden: -Pasahitzen sortzailea -Pasahitz sendoak, bakarrak eta ausazkoak sortzen ditu, web gune bakoitzaren segurtasun-baldintzetan oinarrituta. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Itzulpenak -Bitwarden 40 hizkuntzatan dago, eta gero eta gehiago dira, gure komunitate globalari esker. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Plataforma anitzeko aplikazioak -Babestu eta partekatu zure Bitwarden kutxa gotorraren informazio konfidentziala edozein nabigatzailetatik, gailu mugikorretatik, mahaigaineko aplikaziotik eta gehiagotatik. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Zure gailu guztietarako pasahitzen kudeatzaile seguru eta doakoa + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sinkronizatu eta sartu zure kutxa gotorrean hainbat gailutatik diff --git a/apps/browser/store/locales/fa/copy.resx b/apps/browser/store/locales/fa/copy.resx index 23cb3f3bf0..67095435ac 100644 --- a/apps/browser/store/locales/fa/copy.resx +++ b/apps/browser/store/locales/fa/copy.resx @@ -1,17 +1,17 @@  - @@ -118,40 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - مدیریت کلمه عبور رایگان + Bitwarden Password Manager - یک مدیریت کننده کلمه عبور رایگان برای تمامی دستگاه‌هایتان + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden، Inc. شرکت مادر 8bit Solutions LLC است. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -به عنوان بهترین مدیر کلمه عبور توسط VERGE، US News & WORLD REPORT، CNET و دیگران انتخاب شد. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -کلمه‌های عبور با تعداد نامحدود را در دستگاه‌های نامحدود از هر کجا مدیریت کنید، ذخیره کنید، ایمن کنید و به اشتراک بگذارید. Bitwarden راه حل های مدیریت رمز عبور منبع باز را به همه ارائه می دهد، چه در خانه، چه در محل کار یا در حال حرکت. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -بر اساس الزامات امنیتی برای هر وب سایتی که بازدید می کنید، کلمه‌های عبور قوی، منحصر به فرد و تصادفی ایجاد کنید. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send به سرعت اطلاعات رمزگذاری شده --- فایل ها و متن ساده - را مستقیماً به هر کسی منتقل می کند. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden برنامه‌های Teams و Enterprise را برای شرکت‌ها ارائه می‌دهد تا بتوانید به‌طور ایمن کلمه‌های را با همکاران خود به اشتراک بگذارید. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -چرا Bitwarden را انتخاب کنید: -رمزگذاری در کلاس جهانی -کلمه‌های عبور با رمزگذاری پیشرفته (AES-256 بیت، هشتگ سالت و PBKDF2 SHA-256) محافظت می‌شوند تا داده‌های شما امن و خصوصی بمانند. +More reasons to choose Bitwarden: -تولیدکننده کلمه عبور داخلی -بر اساس الزامات امنیتی برای هر وب سایتی که بازدید می کنید، کلمه‌های عبور قوی، منحصر به فرد و تصادفی ایجاد کنید. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -ترجمه های جهانی -ترجمه Bitwarden به 40 زبان وجود دارد و به لطف جامعه جهانی ما در حال رشد است. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -برنامه های کاربردی چند پلتفرمی -داده‌های حساس را در Bitwarden Vault خود از هر مرورگر، دستگاه تلفن همراه یا سیستم عامل دسکتاپ و غیره ایمن کنید و به اشتراک بگذارید. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - یک مدیریت کننده کلمه عبور رایگان برای تمامی دستگاه‌هایتان + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. همگام‌سازی و دسترسی به گاوصندوق خود از دستگاه های مختلف diff --git a/apps/browser/store/locales/fi/copy.resx b/apps/browser/store/locales/fi/copy.resx index 4440603239..42e914a13f 100644 --- a/apps/browser/store/locales/fi/copy.resx +++ b/apps/browser/store/locales/fi/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Ilmainen salasanahallinta + Bitwarden – Salasanahallinta - Turvallinen ja ilmainen salasanahallinta kaikille laitteillesi + Kotona, töissä tai reissussa, Bitwarden suojaa helposti kaikki salasanasi, avainkoodisi ja arkaluonteiset tietosi. - Bitwarden, Inc. on 8bit Solutions LLC:n emoyhtiö. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NIMENNYT PARHAAKSI SALASANOJEN HALLINNAKSI MM. THE VERGE, U.S. NEWS & WORLD REPORT JA CNET. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Hallinnoi, säilytä, suojaa ja jaa salasanoja rajattomalta laitemäärältä mistä tahansa. Bitwarden tarjoaa avoimeen lähdekoodin perustuvan salasanojen hallintaratkaisun kaikille, olitpa sitten kotona, töissä tai liikkeellä. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Luo usein käyttämillesi sivustoille automaattisesti vahvoja, yksilöllisiä ja satunnaisia salasanoja. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send -ominaisuudella lähetät tietoa nopeasti salattuna — tiedostoja ja tekstiä — suoraan kenelle tahansa. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Yritystoimintaan Bitwarden tarjoaa yrityksille Teams- ja Enterprise-tilaukset, jotta salasanojen jakaminen kollegoiden kesken on turvallista. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Miksi Bitwarden?: -Maailmanluokan salaus -Salasanat on suojattu tehokkaalla päästä päähän salauksella (AES-256 bit, suolattu hajautus ja PBKDF2 SHA-256), joten tietosi pysyvät turvassa ja yksityisinä. +More reasons to choose Bitwarden: -Sisäänrakennettu salasanageneraattori -Luo usein käyttämillesi sivustoille vahvoja, yksilöllisiä ja satunnaisia salasanoja. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Monikielinen -Bitwardenin sovelluksia on käännetty yli 40 kielelle ja määrä kasvaa jatkuvasti, kiitos kansainvälisen yhteisömme. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Alustariippumattomaton -Suojaa, käytä ja jaa Bitwarden-holvisi arkaluontoisia tietoja kaikilla selaimilla, mobiililaitteilla, pöytätietokoneilla ja muissa järjestelmissä. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Turvallinen ja ilmainen salasanahallinta kaikille laitteillesi + Kotona, töissä tai reissussa, Bitwarden suojaa helposti kaikki salasanasi, avainkoodisi ja arkaluonteiset tietosi. Synkronoi ja hallitse holviasi useilla laitteilla diff --git a/apps/browser/store/locales/fil/copy.resx b/apps/browser/store/locales/fil/copy.resx index 4947fa6996..0f68a90bfa 100644 --- a/apps/browser/store/locales/fil/copy.resx +++ b/apps/browser/store/locales/fil/copy.resx @@ -1,17 +1,17 @@  - @@ -118,17 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Libreng Password Manager + Bitwarden Password Manager - Isang ligtas at libreng password manager para sa lahat ng iyong mga aparato. + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Si Bitwarden, Inc. ang parent company ng 8bit Solutions LLC. Tinawag na Pinakamahusay na Password Manager ng The Verge, U.S. News & World Report, CNET at iba pa. I-manage, i-store, i-secure at i-share ang walang limitasyong mga password sa walang limitasyong mga device mula sa kahit saan. Bitwarden nagbibigay ng mga open source na solusyon sa password management sa lahat, kahit sa bahay, sa trabaho o habang nasa daan. Lumikha ng mga matatas, natatanging, at mga random na password na naka-base sa mga pangangailangan ng seguridad para sa bawat website na madalas mong bisitahin. Ang Bitwarden Send ay nagpapadala ng maayos na naka-encrypt na impormasyon - mga file at plaintext - diretso sa sinuman. Ang Bitwarden ay nag-aalok ng mga Teams at Enterprise plans para sa m. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! + +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. + +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. + +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. + +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. + +Use Bitwarden to secure your workforce and share sensitive information with colleagues. + + +More reasons to choose Bitwarden: + +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. + +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. + +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Isang ligtas at libreng password manager para sa lahat ng iyong mga aparato. + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. I-sync at i-access ang iyong kahadeyero mula sa maraming mga aparato diff --git a/apps/browser/store/locales/fr/copy.resx b/apps/browser/store/locales/fr/copy.resx index 9d311fe7cf..9927f885d3 100644 --- a/apps/browser/store/locales/fr/copy.resx +++ b/apps/browser/store/locales/fr/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Gestion des mots de passe + Bitwarden Password Manager - Un gestionnaire de mots de passe sécurisé et gratuit pour tous vos appareils + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. est la société mère de 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NOMMÉ MEILLEUR GESTIONNAIRE DE MOTS DE PASSE PAR THE VERGE, U.S. NEWS & WORLD REPORT, CNET, ET PLUS ENCORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Gérez, stockez, sécurisez et partagez un nombre illimité de mots de passe sur un nombre illimité d'appareils, où que vous soyez. Bitwarden fournit des solutions de gestion de mots de passe open source à tout le monde, que ce soit chez soi, au travail ou en déplacement. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Générez des mots de passe robustes, uniques et aléatoires basés sur des exigences de sécurité pour chaque site web que vous fréquentez. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send transmet rapidement des informations chiffrées --- fichiers et texte --- directement à quiconque. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden propose des plans Teams et Enterprise pour les sociétés afin que vous puissiez partager des mots de passe en toute sécurité avec vos collègues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Pourquoi choisir Bitwarden : -Un chiffrement de classe internationale -Les mots de passe sont protégés par un cryptage avancé de bout en bout (AES-256 bit, hachage salé et PBKDF2 SHA-256) afin que vos données restent sécurisées et privées. +More reasons to choose Bitwarden: -Générateur de mots de passe intégré -Générez des mots de passe forts, uniques et aléatoires en fonction des exigences de sécurité pour chaque site web que vous fréquentez. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Traductions internationales -Les traductions de Bitwarden existent dans 40 langues et ne cessent de croître, grâce à notre communauté globale. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Applications multiplateformes -Sécurisez et partagez des données sensibles dans votre coffre Bitwarden à partir de n'importe quel navigateur, appareil mobile ou système d'exploitation de bureau, et plus encore. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Un gestionnaire de mots de passe sécurisé et gratuit pour tous vos appareils + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Synchroniser et accéder à votre coffre depuis plusieurs appareils diff --git a/apps/browser/store/locales/gl/copy.resx b/apps/browser/store/locales/gl/copy.resx index d812256fb7..0fdb224988 100644 --- a/apps/browser/store/locales/gl/copy.resx +++ b/apps/browser/store/locales/gl/copy.resx @@ -1,17 +1,17 @@  - @@ -118,47 +118,64 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Xestor de contrasinais gratuíto + Bitwarden Password Manager - Un xestor de contrasinais seguro e gratuíto para todos os teus dispositivos + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. é a empresa matriz de 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NOMEADO MELLOR ADMINISTRADOR DE CONTRASINAIS POR THE VERGE, Ou.S. NEWS & WORLD REPORT, CNET E MÁS. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Administre, almacene, protexa e comparta contrasinais ilimitados en dispositivos ilimitados desde calquera lugar. Bitwarden ofrece solucións de xestión de contrasinais de código aberto para todos, xa sexa en casa, no traballo ou en mentres estás de viaxe. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Xere contrasinais seguros, únicas e aleatorias en función dos requisitos de seguridade de cada sitio web que frecuenta. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send transmite rapidamente información cifrada --- arquivos e texto sen formato, directamente a calquera persoa. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden ofrece plans Teams e Enterprise para empresas para que poida compartir contrasinais de forma segura con colegas. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Por que elixir Bitwarden? -Cifrado de clase mundial -Os contrasinais están protexidas con cifrado avanzado de extremo a extremo (AES-256 bits, salted hashing e PBKDF2 XA-256) para que os seus datos permanezan seguros e privados. +More reasons to choose Bitwarden: -Xerador de contrasinais incorporado -Xere contrasinais fortes, únicas e aleatorias en función dos requisitos de seguridade de cada sitio web que frecuenta. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Traducións Globais -As traducións de Bitwarden existen en 40 idiomas e están a crecer, grazas á nosa comunidade global. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Aplicacións multiplataforma -Protexa e comparta datos confidenciais dentro da súa Caixa Forte de Bitwarden desde calquera navegador, dispositivo móbil ou sistema operativo de escritorio, e máis. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Un xestor de contrasinais seguro e gratuíto para todos os teus dispositivos + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sincroniza e accede á túa caixa forte desde múltiples dispositivos - Xestiona todos os teus usuarios e contrasinais desde unha caixa forte segura + Xestiona todos os teus inicios de sesión e contrasinais desde unha caixa forte segura Autocompleta rapidamente os teus datos de acceso en calquera páxina web que visites diff --git a/apps/browser/store/locales/he/copy.resx b/apps/browser/store/locales/he/copy.resx index cd980970fc..7f366f0e93 100644 --- a/apps/browser/store/locales/he/copy.resx +++ b/apps/browser/store/locales/he/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – מנהל ססמאות חינמי + Bitwarden Password Manager - מנהל ססמאות חינמי ומאובטח עבור כל המכשירים שלך + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - מנהל סיסמאות חינמי ומאובטח עבור כל המכשירים שלך + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. סנכרון וגישה לכספת שלך ממגוון מכשירים diff --git a/apps/browser/store/locales/hi/copy.resx b/apps/browser/store/locales/hi/copy.resx index 8db837a3c3..1ea7314d52 100644 --- a/apps/browser/store/locales/hi/copy.resx +++ b/apps/browser/store/locales/hi/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - बिटवार्डन - मुक्त कूटशब्द प्रबंधक + Bitwarden Password Manager - आपके सभी उपकरणों के लिए एक सुरक्षित और नि: शुल्क कूटशब्द प्रबंधक + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - आपके सभी उपकरणों के लिए एक सुरक्षित और नि: शुल्क पासवर्ड प्रबंधक + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. अनेक उपकरणों से अपने तिजोरी सिंक और एक्सेस करें diff --git a/apps/browser/store/locales/hr/copy.resx b/apps/browser/store/locales/hr/copy.resx index 5ff2bcbe01..dff95b3796 100644 --- a/apps/browser/store/locales/hr/copy.resx +++ b/apps/browser/store/locales/hr/copy.resx @@ -1,17 +1,17 @@  - @@ -118,40 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - besplatni upravitelj lozinki + Bitwarden Password Manager - Siguran i besplatan upravitelj lozinki za sve vaše uređaje + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. je vlasnik tvrtke 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -THE VERGE, U.S. NEWS & WORLD REPORT, CNET I DRUGI ODABRALI SU BITWARDEN NAJBOLJIM UPRAVITELJEM LOZINKI. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Upravljajte, spremajte, osigurajte i dijelite neograničen broj lozinki na neograničenom broju uređaja bilo gdje. Bitwarden omogućuje upravljanje lozinkama, bazirano na otvorenom kodu, svima, bilo kod kuće, na poslu ili u pokretu. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generirajte jake, jedinstvene i nasumične lozinke bazirane na sigurnosnim zahtjevima za svaku web stranicu koju često posjećujete. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send omoguzćuje jednostavno i brzo slanje šifriranih podataka --- datoteki ili teksta -- direktno, bilo kome. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden nudi Teams i Enterprise planove za tvrtke kako biste sigurno mogli dijeliti lozinke s kolegama na poslu. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Zašto odabrati Bitwarden? -Svjetski priznata enkripcija -Lozinke su zaštićene naprednim end-to-end šifriranjem (AES-256 bit, salted hashtag i PBKDF2 SHA-256) kako bi vaši osobni podaci ostali sigurni i samo vaši. +More reasons to choose Bitwarden: -Ugrađen generator lozinki -Generirajte jake, jedinstvene i nasumične lozinke bazirane na sigurnosnim zahtjevima za svako web mjesto koje često posjećujete. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Svjetski dostupan -Bitwarden je, zahvaljujući našoj globalnoj zajednici, dostupan na više od 40 jezika. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Podržani svi OS -Osigurajte i sigurno dijelite osjetljive podatke sadržane u vašem Bitwarden trezoru iz bilo kojeg preglednika, mobilnog uređaja ili stolnog računala s bilo kojim OS. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - Siguran i besplatan upravitelj lozinki za sve tvoje uređaje + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sinkroniziraj i pristupi svojem trezoru s više uređaja diff --git a/apps/browser/store/locales/hu/copy.resx b/apps/browser/store/locales/hu/copy.resx index 0b3761a8ad..3e6b8e42d4 100644 --- a/apps/browser/store/locales/hu/copy.resx +++ b/apps/browser/store/locales/hu/copy.resx @@ -1,17 +1,17 @@  - @@ -118,36 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Ingyenes jelszókezelő + Bitwarden Password Manager - Egy biztonságos és ingyenes jelszókezelő az összes eszközre. + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - A Bitwarden, Inc. a 8bit Solutions LLC anyavállalata. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -A VERGE, A US NEWS & WORLD REPORT, a CNET ÉS MÁSOK LEGJOBB JELSZÓKEZELŐJE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Korlátlan számú jelszavak kezelése, tárolása, védelme és megosztása korlátlan eszközökön bárhonnan. A Bitwarden nyílt forráskódú jelszókezelési megoldásokat kínál mindenkinek, legyen az otthon, a munkahelyen vagy útközben. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Hozzunk létre erős, egyedi és véletlenszerű jelszavakat a biztonsági követelmények alapján minden webhelyre, amelyet gyakran látogatunk. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -A Bitwarden Send gyorsan továbbítja a titkosított információkat-fájlokat és egyszerű szöveget közvetlenül bárkinek. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -A Bitwarden csapatokat és vállalati terveket kínál a vállalatok számára, így biztonságosan megoszthatja jelszavait kollégáival. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Miért válasszuk a Bitwardent: -Világszínvonalú titkosítási jelszavak fejlett végpontok közötti titkosítással (AES-256 bit, titkosított hashtag és PBKDF2 SHA-256) védettek, így az adatok biztonságban és titokban maradnak. +More reasons to choose Bitwarden: -Beépített jelszógenerátor A biztonsági követelmények alapján erős, egyedi és véletlenszerű jelszavakat hozhat létre minden gyakran látogatott webhelyen. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Globális fordítások +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -A Bitwarden fordítások 40 nyelven léteznek és globális közösségünknek köszönhetően egyre bővülnek. Többplatformos alkalmazások Biztonságos és megoszthatja az érzékeny adatokat a Bitwarden Széfben bármely böngészőből, mobileszközről vagy asztali operációs rendszerből stb. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - Egy biztonságos és ingyenes jelszókezelő az összes eszközre + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. A széf szinkronizálása és elérése több eszközön. diff --git a/apps/browser/store/locales/id/copy.resx b/apps/browser/store/locales/id/copy.resx index b52252a342..b0791fa3b1 100644 --- a/apps/browser/store/locales/id/copy.resx +++ b/apps/browser/store/locales/id/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Pengelola Sandi Gratis + Bitwarden Password Manager - Pengelola sandi yang aman dan gratis untuk semua perangkat Anda + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Pengelola sandi yang aman dan gratis untuk semua perangkat Anda + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sinkronkan dan akses brankas Anda dari beberapa perangkat diff --git a/apps/browser/store/locales/it/copy.resx b/apps/browser/store/locales/it/copy.resx index 56bf9a907c..bcbbe10512 100644 --- a/apps/browser/store/locales/it/copy.resx +++ b/apps/browser/store/locales/it/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Password Manager Gratis + Bitwarden Password Manager - Un password manager sicuro e gratis per tutti i tuoi dispositivi + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. è la società madre di 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NOMINATO MIGLIOR PASSWORD MANAGER DA THE VERGE, U.S. NEWS & WORLD REPORT, CNET, E ALTRO. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Gestisci, archivia, proteggi, e condividi password illimitate su dispositivi illimitati da qualsiasi luogo. Bitwarden offre soluzioni di gestione delle password open-source a tutti, a casa, al lavoro, o in viaggio. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Genera password forti, uniche, e casuali in base ai requisiti di sicurezza per ogni sito web che frequenti. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send trasmette rapidamente informazioni crittate - via file e testo in chiaro - direttamente a chiunque. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offre piani Teams ed Enterprise per le aziende così puoi condividere le password in modo sicuro con i tuoi colleghi. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Perché Scegliere Bitwarden: -Crittografia Di Livello Mondiale -Le password sono protette con crittografia end-to-end avanzata (AES-256 bit, salted hashing, e PBKDF2 SHA-256) per tenere i tuoi dati al sicuro e privati. +More reasons to choose Bitwarden: -Generatore Di Password Integrato -Genera password forti, uniche e casuali in base ai requisiti di sicurezza per ogni sito web che frequenti. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Traduzioni Globali -Le traduzioni di Bitwarden esistono in 40 lingue e sono in crescita grazie alla nostra comunità globale. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Applicazioni Multipiattaforma -Proteggi e condividi i dati sensibili all'interno della tua cassaforte di Bitwarden da qualsiasi browser, dispositivo mobile, o sistema operativo desktop, e altro. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Un password manager sicuro e gratis per tutti i tuoi dispositivi + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sincronizza e accedi alla tua cassaforte da più dispositivi diff --git a/apps/browser/store/locales/ja/copy.resx b/apps/browser/store/locales/ja/copy.resx index 13ce1bc4e9..67c479fcde 100644 --- a/apps/browser/store/locales/ja/copy.resx +++ b/apps/browser/store/locales/ja/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - 無料パスワードマネージャー + Bitwarden Password Manager - あらゆる端末で使える、安全な無料パスワードマネージャー + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc.は8bit Solutions LLC.の親会社です。 + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -THE VERGEやU.S. NEWS、WORLD REPORT、CNETなどからベストパスワードマネージャーに選ばれました。 +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -端末や場所を問わずパスワードの管理・保存・保護・共有を無制限にできます。Bitwardenは自宅や職場、外出先でもパスワード管理をすべての人に提供し、プログラムコードは公開されています。 +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -よく利用するどのWebサイトでも、セキュリティ条件にそった強力でユニークなパスワードをランダムに生成することができます。 +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Sendは、暗号化した情報(ファイルや平文)をすぐに誰にでも直接送信することができます。 +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwardenは企業向けにTeamsとEnterpriseのプランを提供しており、パスワードを同僚と安全に共有することができます。 +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Bitwardenを選ぶ理由は? -・世界最高レベルの暗号化 -パスワードは高度なエンドツーエンド暗号化(AES-256 bit、salted hashing、PBKDF2 SHA-256)で保護されるので、データは安全に非公開で保たれます。 +More reasons to choose Bitwarden: -・パスワード生成機能 -よく利用するどのWebサイトでも、セキュリティ条件にそった強力でユニークなパスワードをランダムに生成することができます。 +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -・グローバルな翻訳 -Bitwardenは40ヶ国語に翻訳されており、グローバルなコミュニティのおかげで増え続けています。 +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -・クロスプラットフォームアプリケーション -あなたのBitwarden Vaultで、ブラウザ・モバイル機器・デスクトップOSなどの垣根を超えて、機密データを保護・共有することができます。 +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - あらゆる端末で使える、安全な無料パスワードマネージャー + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. 複数の端末で保管庫に同期&アクセス diff --git a/apps/browser/store/locales/ka/copy.resx b/apps/browser/store/locales/ka/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/ka/copy.resx +++ b/apps/browser/store/locales/ka/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/km/copy.resx b/apps/browser/store/locales/km/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/km/copy.resx +++ b/apps/browser/store/locales/km/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/kn/copy.resx b/apps/browser/store/locales/kn/copy.resx index 6928f557e4..f68f2c25da 100644 --- a/apps/browser/store/locales/kn/copy.resx +++ b/apps/browser/store/locales/kn/copy.resx @@ -1,17 +1,17 @@  - @@ -118,40 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - ಬಿಟ್ವರ್ಡ್ – ಉಚಿತ ಪಾಸ್ವರ್ಡ್ ನಿರ್ವಾಹಕ + Bitwarden Password Manager - ನಿಮ್ಮ ಎಲ್ಲಾ ಸಾಧನಗಳಿಗೆ ಸುರಕ್ಷಿತ ಮತ್ತು ಉಚಿತ ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕ + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - ಬಿಟ್ವಾರ್ಡೆನ್, ಇಂಕ್. 8 ಬಿಟ್ ಸೊಲ್ಯೂಷನ್ಸ್ ಎಲ್ಎಲ್ ಸಿ ಯ ಮೂಲ ಕಂಪನಿಯಾಗಿದೆ. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -ವರ್ಜ್, ಯು.ಎಸ್. ನ್ಯೂಸ್ & ವರ್ಲ್ಡ್ ರಿಪೋರ್ಟ್, ಸಿನೆಟ್ ಮತ್ತು ಹೆಚ್ಚಿನದರಿಂದ ಉತ್ತಮ ಪಾಸ್‌ವರ್ಡ್ ವ್ಯವಸ್ಥಾಪಕ ಎಂದು ಹೆಸರಿಸಲಾಗಿದೆ. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -ಎಲ್ಲಿಂದಲಾದರೂ ಅನಿಯಮಿತ ಸಾಧನಗಳಲ್ಲಿ ಅನಿಯಮಿತ ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಿ, ಸಂಗ್ರಹಿಸಿ, ಸುರಕ್ಷಿತಗೊಳಿಸಿ ಮತ್ತು ಹಂಚಿಕೊಳ್ಳಿ. ಮನೆಯಲ್ಲಿ, ಕೆಲಸದಲ್ಲಿ ಅಥವಾ ಪ್ರಯಾಣದಲ್ಲಿರಲಿ ಪ್ರತಿಯೊಬ್ಬರಿಗೂ ಬಿಟ್‌ವಾರ್ಡೆನ್ ಓಪನ್ ಸೋರ್ಸ್ ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಹಣಾ ಪರಿಹಾರಗಳನ್ನು ನೀಡುತ್ತದೆ. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -ನೀವು ಆಗಾಗ್ಗೆ ಪ್ರತಿ ವೆಬ್‌ಸೈಟ್‌ಗೆ ಸುರಕ್ಷತಾ ಅವಶ್ಯಕತೆಗಳನ್ನು ಆಧರಿಸಿ ಬಲವಾದ, ಅನನ್ಯ ಮತ್ತು ಯಾದೃಚ್ pass ಿಕ ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ರಚಿಸಿ. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -ಬಿಟ್‌ವಾರ್ಡೆನ್ ಕಳುಹಿಸಿ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಿದ ಮಾಹಿತಿಯನ್ನು ತ್ವರಿತವಾಗಿ ರವಾನಿಸುತ್ತದೆ --- ಫೈಲ್‌ಗಳು ಮತ್ತು ಸರಳ ಪಠ್ಯ - ನೇರವಾಗಿ ಯಾರಿಗಾದರೂ. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -ಬಿಟ್‌ವಾರ್ಡೆನ್ ಕಂಪೆನಿಗಳಿಗೆ ತಂಡಗಳು ಮತ್ತು ಎಂಟರ್‌ಪ್ರೈಸ್ ಯೋಜನೆಗಳನ್ನು ನೀಡುತ್ತದೆ ಆದ್ದರಿಂದ ನೀವು ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ಸಹೋದ್ಯೋಗಿಗಳೊಂದಿಗೆ ಸುರಕ್ಷಿತವಾಗಿ ಹಂಚಿಕೊಳ್ಳಬಹುದು. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -ಬಿಟ್‌ವಾರ್ಡೆನ್ ಅನ್ನು ಏಕೆ ಆರಿಸಬೇಕು: -ವಿಶ್ವ ದರ್ಜೆಯ ಗೂ ry ಲಿಪೀಕರಣ -ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ಸುಧಾರಿತ ಎಂಡ್-ಟು-ಎಂಡ್ ಎನ್‌ಕ್ರಿಪ್ಶನ್ (ಎಇಎಸ್ -256 ಬಿಟ್, ಉಪ್ಪುಸಹಿತ ಹ್ಯಾಶ್‌ಟ್ಯಾಗ್ ಮತ್ತು ಪಿಬಿಕೆಡಿಎಫ್ 2 ಎಸ್‌ಎಚ್‌ಎ -256) ನೊಂದಿಗೆ ರಕ್ಷಿಸಲಾಗಿದೆ ಆದ್ದರಿಂದ ನಿಮ್ಮ ಡೇಟಾ ಸುರಕ್ಷಿತ ಮತ್ತು ಖಾಸಗಿಯಾಗಿರುತ್ತದೆ. +More reasons to choose Bitwarden: -ಅಂತರ್ನಿರ್ಮಿತ ಪಾಸ್ವರ್ಡ್ ಜನರೇಟರ್ -ನೀವು ಆಗಾಗ್ಗೆ ಪ್ರತಿ ವೆಬ್‌ಸೈಟ್‌ಗೆ ಸುರಕ್ಷತಾ ಅವಶ್ಯಕತೆಗಳನ್ನು ಆಧರಿಸಿ ಬಲವಾದ, ಅನನ್ಯ ಮತ್ತು ಯಾದೃಚ್ pass ಿಕ ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ರಚಿಸಿ. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -ಜಾಗತಿಕ ಅನುವಾದಗಳು -ಬಿಟ್ವಾರ್ಡೆನ್ ಅನುವಾದಗಳು 40 ಭಾಷೆಗಳಲ್ಲಿ ಅಸ್ತಿತ್ವದಲ್ಲಿವೆ ಮತ್ತು ಬೆಳೆಯುತ್ತಿವೆ, ನಮ್ಮ ಜಾಗತಿಕ ಸಮುದಾಯಕ್ಕೆ ಧನ್ಯವಾದಗಳು. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -ಕ್ರಾಸ್ ಪ್ಲಾಟ್‌ಫಾರ್ಮ್ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು -ಯಾವುದೇ ಬ್ರೌಸರ್, ಮೊಬೈಲ್ ಸಾಧನ, ಅಥವಾ ಡೆಸ್ಕ್‌ಟಾಪ್ ಓಎಸ್ ಮತ್ತು ಹೆಚ್ಚಿನವುಗಳಿಂದ ನಿಮ್ಮ ಬಿಟ್‌ವಾರ್ಡನ್ ವಾಲ್ಟ್‌ನಲ್ಲಿ ಸೂಕ್ಷ್ಮ ಡೇಟಾವನ್ನು ಸುರಕ್ಷಿತಗೊಳಿಸಿ ಮತ್ತು ಹಂಚಿಕೊಳ್ಳಿ. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - ನಿಮ್ಮ ಎಲ್ಲಾ ಸಾಧನಗಳಿಗೆ ಸುರಕ್ಷಿತ ಮತ್ತು ಉಚಿತ ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕ + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. ಅನೇಕ ಸಾಧನಗಳಿಂದ ನಿಮ್ಮ ವಾಲ್ಟ್ ಅನ್ನು ಸಿಂಕ್ ಮಾಡಿ ಮತ್ತು ಪ್ರವೇಶಿಸಿ diff --git a/apps/browser/store/locales/ko/copy.resx b/apps/browser/store/locales/ko/copy.resx index 0fb5dd713f..fdfb93ad6a 100644 --- a/apps/browser/store/locales/ko/copy.resx +++ b/apps/browser/store/locales/ko/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - 무료 비밀번호 관리자 + Bitwarden Password Manager - 당신의 모든 기기에서 사용할 수 있는, 안전한 무료 비밀번호 관리자 + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc.은 8bit Solutions LLC.의 모회사입니다. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -VERGE, U.S. NEWS, WORLD REPORT, CNET 등에서 최고의 비밀번호 관리자라고 평가했습니다! +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -관리하고, 보관하고, 보호하고, 어디에서든 어떤 기기에서나 무제한으로 비밀번호를 공유하세요. Bitwarden은 모두에게 오픈소스 비밀번호 관리 솔루션을 제공합니다. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -강하고, 독특하고, 랜덤한 비밀번호를 모든 웹사이트의 보안 요구사항에 따라 생성할 수 있습니다. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send는 빠르게 암호화된 파일과 텍스트를 모두에게 전송할 수 있습니다. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden은 회사들을 위해 팀과 기업 플랜을 제공해서 동료에게 안전하게 비밀번호를 공유할 수 있습니다. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Bitwarden을 선택하는 이유: -세계 최고의 암호화 -비밀번호는 고급 종단간 암호화 (AES-256 bit, salted hashtag, 그리고 PBKDF2 SHA-256)을 이용하여 보호되기 때문에 데이터를 안전하게 보관할 수 있습니다. +More reasons to choose Bitwarden: -내장 비밀번호 생성기 -강하고, 독특하고, 랜덤한 비밀번호를 모든 웹사이트의 보안 요구사항에 따라 생성할 수 있습니다. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -언어 지원 -Bitwarden 번역은 전 세계의 커뮤니티 덕분에 40개의 언어를 지원하고 더 성장하고 있습니다. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -크로스 플랫폼 애플리케이션 -Bitwarden 보관함에 있는 민감한 정보를 어떠한 브라우저, 모바일 기기, 데스크톱 OS 등을 이용하여 보호하고 공유하세요. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - 당신의 모든 기기에서 사용할 수 있는, 안전한 무료 비밀번호 관리자 + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. 여러 기기에서 보관함에 접근하고 동기화할 수 있습니다. diff --git a/apps/browser/store/locales/lt/copy.resx b/apps/browser/store/locales/lt/copy.resx index 92009c5c6d..d83c6ca99a 100644 --- a/apps/browser/store/locales/lt/copy.resx +++ b/apps/browser/store/locales/lt/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – nemokamas slaptažodžių tvarkyklė + Bitwarden Password Manager - Saugi ir nemokama slaptažodžių tvarkyklė visiems įrenginiams + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. yra patronuojančioji 8bit Solutions LLC įmonė. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -GERIAUSIU SLAPTAŽODŽIŲ TVARKYTOJU PRIPAŽINTAS THE VERGE, U.S. NEWS & WORLD REPORT, CNET IR KT. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Tvarkyk, laikyk, saugok ir bendrink neribotą skaičių slaptažodžių neribotuose įrenginiuose iš bet kurios vietos. Bitwarden teikia atvirojo kodo slaptažodžių valdymo sprendimus visiems – tiek namuose, tiek darbe, ar keliaujant. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generuok stiprius, unikalius ir atsitiktinius slaptažodžius pagal saugos reikalavimus kiekvienai lankomai svetainei. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send greitai perduoda užšifruotą informaciją – failus ir paprastą tekstą – tiesiogiai bet kam. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden siūlo komandoms ir verslui planus įmonėms, kad galėtum saugiai dalytis slaptažodžiais su kolegomis. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Kodėl rinktis Bitwarden: -Pasaulinės klasės šifravimas -Slaptažodžiai yra saugomi su pažangiu šifravimu nuo galo iki galo (AES-256 bitų, sūdytu šifravimu ir PBKDF2 SHA-256), todėl tavo duomenys išliks saugūs ir privatūs. +More reasons to choose Bitwarden: -Integruotas slaptažodžių generatorius -Generuok stiprius, unikalius ir atsitiktinius slaptažodžius pagal saugos reikalavimus kiekvienai dažnai lankomai svetainei. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Visuotiniai vertimai -Mūsų pasaulinės bendruomenės dėka Bitwarden vertimai egzistuoja 40 kalbose ir vis daugėja. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Įvairių platformų programos -Apsaugok ir bendrink neskelbtinus duomenis savo Bitwarden Vault iš bet kurios naršyklės, mobiliojo įrenginio ar darbalaukio OS ir kt. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Saugi ir nemokama slaptažodžių tvarkyklė visiems įrenginiams + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Pasiekite savo saugyklą iš kelių įrenginių diff --git a/apps/browser/store/locales/lv/copy.resx b/apps/browser/store/locales/lv/copy.resx index aec5e836c1..e64cc2eb3a 100644 --- a/apps/browser/store/locales/lv/copy.resx +++ b/apps/browser/store/locales/lv/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Bezmaksas Paroļu Pārvaldnieks + Bitwarden Password Manager - Drošs un bezmaksas paroļu pārvaldnieks priekš visām jūsu ierīcēm. + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. ir 8bit Solutions LLC mātesuzņēmums. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -THE VERGE, U.S. NEWS & WORLD REPORT, CNET UN CITI ATZINA PAR LABĀKO PAROĻU PĀRVALDNIEKU. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Pārvaldi, uzglabā, aizsargā un kopīgo neierobežotu skaitu paroļu neierobežotā skaitā ierīču no jebkuras vietas. Bitwarden piedāvā atvērtā koda paroļu pārvaldības risinājumus ikvienam - gan mājās, gan darbā, gan ceļā. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Ģenerē spēcīgas, unikālas un nejaušas paroles, pamatojoties uz drošības prasībām, katrai bieži apmeklētai vietnei. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send ātri pārsūta šifrētu informāciju - failus un atklātu tekstu - tieši jebkuram. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden piedāvā Teams un Enterprise plānus uzņēmumiem, lai tu varētu droši kopīgot paroles ar kolēģiem. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Kāpēc izvēlēties Bitwarden: -Pasaules klases šifrēšana -Paroles tiek aizsargātas ar modernu end-to-end šifrēšanu (AES-256 bitu, sālītu šifrēšanu un PBKDF2 SHA-256), lai tavi dati paliktu droši un privāti. +More reasons to choose Bitwarden: -Iebūvēts paroļu ģenerators -Ģenerē spēcīgas, unikālas un nejaušas paroles, pamatojoties uz drošības prasībām katrai bieži apmeklētai vietnei. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Globālie tulkojumi -Bitwarden tulkojumi ir pieejami 40 valodās, un to skaits turpina pieaugt, pateicoties mūsu globālajai kopienai. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Starpplatformu lietojumprogrammas -Nodrošini un kopīgo sensitīvus datus savā Bitwarden Seifā no jebkuras pārlūkprogrammas, mobilās ierīces vai darbvirsmas operētājsistēmas un daudz ko citu. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Drošs un bezmaksas paroļu pārvaldnieks priekš visām jūsu ierīcēm. + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sinhronizē un piekļūsti savai glabātavai no vairākām ierīcēm diff --git a/apps/browser/store/locales/ml/copy.resx b/apps/browser/store/locales/ml/copy.resx index cf9b631227..e22993d5b7 100644 --- a/apps/browser/store/locales/ml/copy.resx +++ b/apps/browser/store/locales/ml/copy.resx @@ -1,17 +1,17 @@  - @@ -118,27 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - സൗജന്യ പാസ്സ്‌വേഡ് മാനേജർ + Bitwarden Password Manager - നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങൾക്കും സുരക്ഷിതവും സൗജന്യവുമായ പാസ്‌വേഡ് മാനേജർ + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - നിങ്ങളുടെ എല്ലാ ലോഗിനുകളും പാസ്‌വേഡുകളും സംഭരിക്കുന്നതിനുള്ള ഏറ്റവും എളുപ്പവും സുരക്ഷിതവുമായ മാർഗ്ഗമാണ് Bitwarden, ഒപ്പം നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളും തമ്മിൽ സമന്വയിപ്പിക്കുകയും ചെയ്യുന്നു. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -പാസ്‌വേഡ് മോഷണം ഗുരുതരമായ പ്രശ്‌നമാണ്. നിങ്ങൾ ഉപയോഗിക്കുന്ന വെബ്‌സൈറ്റുകളും അപ്ലിക്കേഷനുകളും എല്ലാ ദിവസവും ആക്രമണത്തിലാണ്. സുരക്ഷാ ലംഘനങ്ങൾ സംഭവിക്കുകയും നിങ്ങളുടെ പാസ്‌വേഡുകൾ മോഷ്‌ടിക്കപ്പെടുകയും ചെയ്യുന്നു. അപ്ലിക്കേഷനുകളിലും വെബ്‌സൈറ്റുകളിലും ഉടനീളം സമാന പാസ്‌വേഡുകൾ നിങ്ങൾ വീണ്ടും ഉപയോഗിക്കുമ്പോൾ ഹാക്കർമാർക്ക് നിങ്ങളുടെ ഇമെയിൽ, ബാങ്ക്, മറ്റ് പ്രധാനപ്പെട്ട അക്കൗണ്ടുകൾ എന്നിവ എളുപ്പത്തിൽ ആക്‌സസ്സുചെയ്യാനാകും. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങളിലും സമന്വയിപ്പിക്കുന്ന ഒരു എൻ‌ക്രിപ്റ്റ് ചെയ്ത വാൾട്ടിൽ Bitwarden നിങ്ങളുടെ എല്ലാ ലോഗിനുകളും സംഭരിക്കുന്നു. നിങ്ങളുടെ ഉപകരണം വിടുന്നതിനുമുമ്പ് ഇത് പൂർണ്ണമായും എൻ‌ക്രിപ്റ്റ് ചെയ്‌തിരിക്കുന്നതിനാൽ, നിങ്ങളുടെ ഡാറ്റ നിങ്ങൾക്ക് മാത്രമേ ആക്‌സസ് ചെയ്യാൻ കഴിയൂ . Bitwarden ടീമിന് പോലും നിങ്ങളുടെ ഡാറ്റ വായിക്കാൻ കഴിയില്ല. നിങ്ങളുടെ ഡാറ്റ AES-256 ബിറ്റ് എൻ‌ക്രിപ്ഷൻ, സാൾട്ടിങ് ഹാഷിംഗ്, PBKDF2 SHA-256 എന്നിവ ഉപയോഗിച്ച് അടച്ചിരിക്കുന്നു. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -100% ഓപ്പൺ സോഴ്‌സ് സോഫ്റ്റ്വെയറാണ് Bitwarden . Bitwarden സോഴ്‌സ് കോഡ് GitHub- ൽ ഹോസ്റ്റുചെയ്‌തിരിക്കുന്നു, മാത്രമല്ല എല്ലാവർക്കും ഇത് അവലോകനം ചെയ്യാനും ഓഡിറ്റുചെയ്യാനും ബിറ്റ് വാർഡൻ കോഡ്ബേസിലേക്ക് സംഭാവന ചെയ്യാനും സ്വാതന്ത്ര്യമുണ്ട്. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. + +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. + +Use Bitwarden to secure your workforce and share sensitive information with colleagues. +More reasons to choose Bitwarden: +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. + +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - നിങ്ങളുടെ എല്ലാ ഉപകരണങ്ങൾക്കും സുരക്ഷിതവും സൗജന്യവുമായ പാസ്‌വേഡ് മാനേജർ. + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. ഒന്നിലധികം ഉപകരണങ്ങളിൽ നിന്ന് നിങ്ങളുടെ വാൾട് സമന്വയിപ്പിച്ച് ആക്‌സസ്സുചെയ്യുക diff --git a/apps/browser/store/locales/mr/copy.resx b/apps/browser/store/locales/mr/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/mr/copy.resx +++ b/apps/browser/store/locales/mr/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/my/copy.resx b/apps/browser/store/locales/my/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/my/copy.resx +++ b/apps/browser/store/locales/my/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/nb/copy.resx b/apps/browser/store/locales/nb/copy.resx index 74a8558db6..26a09cc855 100644 --- a/apps/browser/store/locales/nb/copy.resx +++ b/apps/browser/store/locales/nb/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden — Fri passordbehandling + Bitwarden Password Manager - En sikker og fri passordbehandler for alle dine PCer og mobiler + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - En sikker og fri passordbehandler for alle dine PCer og mobiler + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Synkroniser og få tilgang til ditt hvelv fra alle dine enheter diff --git a/apps/browser/store/locales/ne/copy.resx b/apps/browser/store/locales/ne/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/ne/copy.resx +++ b/apps/browser/store/locales/ne/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/nl/copy.resx b/apps/browser/store/locales/nl/copy.resx index e0779ba777..44dd02b439 100644 --- a/apps/browser/store/locales/nl/copy.resx +++ b/apps/browser/store/locales/nl/copy.resx @@ -1,17 +1,17 @@  - @@ -118,40 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Gratis wachtwoordbeheer + Bitwarden Password Manager - Een veilige en gratis oplossing voor wachtwoordbeheer voor al je apparaten + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is het moederbedrijf van 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -BESTE WACHTWOORDBEHEERDER VOLGENS THE VERGE, U.S. NEWS & WORLD REPORT, CNET EN ANDEREN. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Beheer, bewaar, beveilig en deel een onbeperkt aantal wachtwoorden op een onbeperkt aantal apparaten, waar je ook bent. Bitwarden levert open source wachtwoordbeheeroplossingen voor iedereen, of dat nu thuis, op het werk of onderweg is. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Genereer sterke, unieke en willekeurige wachtwoorden op basis van beveiligingsvereisten voor elke website die je bezoekt. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send verzendt snel versleutelde informatie --- bestanden en platte tekst -- rechtstreeks naar iedereen. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden biedt Teams- en Enterprise-abonnementen voor bedrijven, zodat je veilig wachtwoorden kunt delen met collega's. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Waarom Bitwarden: -Versleuteling van wereldklasse -Wachtwoorden worden beschermd met geavanceerde end-to-end-codering (AES-256 bit, salted hashtag en PBKDF2 SHA-256) zodat jouw gegevens veilig en privé blijven. +More reasons to choose Bitwarden: -Ingebouwde wachtwoordgenerator -Genereer sterke, unieke en willekeurige wachtwoorden op basis van beveiligingsvereisten voor elke website die je bezoekt. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Wereldwijde vertalingen -Bitwarden-vertalingen bestaan ​​in 40 talen en groeien dankzij onze wereldwijde community. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Platformoverschrijdende toepassingen -Beveilig en deel gevoelige gegevens binnen uw Bitwarden Vault vanuit elke browser, mobiel apparaat of desktop-besturingssysteem, en meer. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - Een veilige en gratis oplossing voor wachtwoordbeheer voor al uw apparaten + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Synchroniseer en gebruik je kluis op meerdere apparaten diff --git a/apps/browser/store/locales/nn/copy.resx b/apps/browser/store/locales/nn/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/nn/copy.resx +++ b/apps/browser/store/locales/nn/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/or/copy.resx b/apps/browser/store/locales/or/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/or/copy.resx +++ b/apps/browser/store/locales/or/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/pl/copy.resx b/apps/browser/store/locales/pl/copy.resx index 5b3941cb7e..60709c7d4d 100644 --- a/apps/browser/store/locales/pl/copy.resx +++ b/apps/browser/store/locales/pl/copy.resx @@ -1,17 +1,17 @@  - @@ -118,40 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - darmowy menedżer haseł + Bitwarden Password Manager - Bezpieczny i darmowy menedżer haseł dla wszystkich Twoich urządzeń + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. jest macierzystą firmą 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAZWANY NAJLEPSZYM MENEDŻEREM HASEŁ PRZEZ THE VERGE, US NEWS & WORLD REPORT, CNET I WIĘCEJ. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Zarządzaj, przechowuj, zabezpieczaj i udostępniaj nieograniczoną liczbę haseł na nieograniczonej liczbie urządzeń z każdego miejsca. Bitwarden dostarcza rozwiązania do zarządzania hasłami z otwartym kodem źródłowym każdemu, niezależnie od tego, czy jest w domu, w pracy, czy w podróży. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generuj silne, unikalne i losowe hasła w oparciu o wymagania bezpieczeństwa dla każdej odwiedzanej strony. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Funkcja Bitwarden Send szybko przesyła zaszyfrowane informacje --- pliki i zwykły tekst -- bezpośrednio do każdego. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden oferuje plany dla zespołów i firm, dzięki czemu możesz bezpiecznie udostępniać hasła współpracownikom. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Dlaczego warto wybrać Bitwarden: -Szyfrowanie światowej klasy -Hasła są chronione za pomocą zaawansowanego szyfrowania typu end-to-end (AES-256 bitów, dodatkowy ciąg zaburzający i PBKDF2 SHA-256), dzięki czemu Twoje dane pozostają bezpieczne i prywatne. +More reasons to choose Bitwarden: -Wbudowany generator haseł -Generuj silne, unikalne i losowe hasła w oparciu o wymagania bezpieczeństwa dla każdej odwiedzanej strony. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Przetłumaczone aplikacje -Tłumaczenia Bitwarden są dostępne w 40 językach i rosną dzięki naszej globalnej społeczności. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Aplikacje wieloplatformowe -Zabezpiecz i udostępniaj poufne dane w swoim sejfie Bitwarden z dowolnej przeglądarki, urządzenia mobilnego, systemu operacyjnego i nie tylko. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - Bezpieczny i darmowy menedżer haseł dla wszystkich Twoich urządzeń + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Synchronizacja i dostęp do sejfu z różnych urządzeń diff --git a/apps/browser/store/locales/pt_BR/copy.resx b/apps/browser/store/locales/pt_BR/copy.resx index 48111fa814..8b99c436d0 100644 --- a/apps/browser/store/locales/pt_BR/copy.resx +++ b/apps/browser/store/locales/pt_BR/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Gerenciador de Senhas Gratuito + Bitwarden Password Manager - Um gerenciador de senhas gratuito e seguro para todos os seus dispositivos + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. é a empresa matriz da 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NOMEADA MELHOR GERENCIADORA DE SENHAS PELA VERGE, U.S. NEWS & WORLD REPORT, CNET, E MUITO MAIS. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Gerenciar, armazenar, proteger e compartilhar senhas ilimitadas através de dispositivos ilimitados de qualquer lugar. Bitwarden fornece soluções de gerenciamento de senhas de código aberto para todos, seja em casa, no trabalho ou em viagem. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Gere senhas fortes, únicas e aleatórias com base nos requisitos de segurança para cada site que você frequenta. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -A Bitwarden Send transmite rapidamente informações criptografadas --- arquivos e texto em formato de placa -- diretamente para qualquer pessoa. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden oferece equipes e planos empresariais para empresas para que você possa compartilhar senhas com colegas com segurança. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Por que escolher Bitwarden: -Criptografia de Classe Mundial -As senhas são protegidas com criptografia avançada de ponta a ponta (AES-256 bit, salted hashing e PBKDF2 SHA-256) para que seus dados permaneçam seguros e privados. +More reasons to choose Bitwarden: -Gerador de senhas embutido -Gerar senhas fortes, únicas e aleatórias com base nos requisitos de segurança para cada site que você freqüenta. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Traduções globais -As traduções Bitwarden existem em 40 idiomas e estão crescendo, graças à nossa comunidade global. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Aplicações multiplataforma -Proteja e compartilhe dados sensíveis dentro de seu Bitwarden Vault a partir de qualquer navegador, dispositivo móvel ou SO desktop, e muito mais. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Um gerenciador de senhas gratuito e seguro para todos os seus dispositivos + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sincronize e acesse o seu cofre através de múltiplos dispositivos diff --git a/apps/browser/store/locales/pt_PT/copy.resx b/apps/browser/store/locales/pt_PT/copy.resx index 845a94a3ca..d310629612 100644 --- a/apps/browser/store/locales/pt_PT/copy.resx +++ b/apps/browser/store/locales/pt_PT/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Gestor de Palavras-passe Gratuito + Bitwarden Password Manager - Um gestor de palavras-passe seguro e gratuito para todos os seus dispositivos + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - A Bitwarden, Inc. é a empresa-mãe da 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NOMEADO O MELHOR GESTOR DE PALAVRAS-PASSE PELO THE VERGE, U.S. NEWS & WORLD REPORT, CNET E MUITO MAIS. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Gerir, armazenar, proteger e partilhar palavras-passe ilimitadas em dispositivos ilimitados a partir de qualquer lugar. O Bitwarden fornece soluções de gestão de palavras-passe de código aberto para todos, seja em casa, no trabalho ou onde estiver. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Gera palavras-passe fortes, únicas e aleatórias com base em requisitos de segurança para todos os sites que frequenta. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -O Bitwarden Send transmite rapidamente informações encriptadas - ficheiros e texto simples - diretamente a qualquer pessoa. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -O Bitwarden oferece os planos Equipas e Empresarial destinados a empresas, para que possa partilhar de forma segura as palavras-passe com os seus colegas. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Razões para escolher o Bitwarden: -Encriptação de classe mundial -As palavras-passe são protegidas com encriptação avançada de ponta a ponta (AES-256 bit, salted hashtag e PBKDF2 SHA-256) para que os seus dados permaneçam seguros e privados. +More reasons to choose Bitwarden: -Gerador de palavras-passe incorporado -Gera palavras-passe fortes, únicas e aleatórias com base nos requisitos de segurança para todos os sites que frequenta. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Traduções globais -O Bitwarden está traduzido em 40 idiomas e está a crescer, graças à nossa comunidade global. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Aplicações multiplataforma -Proteja e partilhe dados confidenciais no seu cofre Bitwarden a partir de qualquer navegador, dispositivo móvel ou sistema operativo de computador, e muito mais. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Um gestor de palavras-passe seguro e gratuito para todos os seus dispositivos + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sincronize e aceda ao seu cofre através de vários dispositivos diff --git a/apps/browser/store/locales/ro/copy.resx b/apps/browser/store/locales/ro/copy.resx index 0e12b289af..7b0070fad2 100644 --- a/apps/browser/store/locales/ro/copy.resx +++ b/apps/browser/store/locales/ro/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Manager de parole gratuit + Bitwarden Password Manager - Un manager de parole sigur și gratuit pentru toate dispozitivele dvs. + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. este compania mamă a 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NUMIT CEL MAI BUN MANAGER DE PAROLE DE CĂTRE THE VERGE, U.S. NEWS & WORLD REPORT, CNET ȘI MULȚI ALȚII. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Gestionați, stocați, securizați și partajați un număr nelimitat de parole pe un număr nelimitat de dispozitive, de oriunde. Bitwarden oferă soluții open source de gestionare a parolelor pentru toată lumea, fie că se află acasă, la serviciu sau în mișcare. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generați parole puternice, unice și aleatorii, bazate pe cerințe de securitate pentru fiecare site web pe care îl frecventați. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send transmite rapid informații criptate --- fișiere și text simple -- direct către oricine. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden oferă planuri Teams și Enterprise pentru companii, astfel încât să puteți partaja în siguranță parolele cu colegii. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -De ce să alegeți Bitwarden: -Criptare de clasă mondială -Parolele sunt protejate cu criptare avansată end-to-end (AES-256 bit, salted hashing și PBKDF2 SHA-256), astfel încât datele dvs. să rămână sigure și private. +More reasons to choose Bitwarden: -Generator de parole încorporat -Generați parole puternice, unice și aleatorii, bazate pe cerințele de securitate pentru fiecare site web pe care îl frecventați. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Traduceri la nivel mondial -Bitwarden este deja tradus în 40 de limbi și numărul lor crește, datorită comunității noastre mondiale. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Aplicații multi-platformă -Protejați și partajați date sensibile în seiful Bitwarden de pe orice browser, dispozitiv mobil sau sistem de operare desktop și multe altele. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Un manager de parole sigur și gratuit, pentru toate dispozitivele dvs. + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sincronizează și accesează seiful dvs. de pe multiple dispozitive diff --git a/apps/browser/store/locales/ru/copy.resx b/apps/browser/store/locales/ru/copy.resx index 4e48ecbc88..212a899f76 100644 --- a/apps/browser/store/locales/ru/copy.resx +++ b/apps/browser/store/locales/ru/copy.resx @@ -1,17 +1,17 @@  - @@ -118,40 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – бесплатный менеджер паролей + Bitwarden Password Manager - Защищенный и бесплатный менеджер паролей для всех ваших устройств + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. является материнской компанией 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -НАЗВАН ЛУЧШИМ ДИСПЕТЧЕРОМ ПАРОЛЕЙ VERGE, US NEWS & WORLD REPORT, CNET И МНОГИМИ ДРУГИМИ. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Управляйте, храните, защищайте и делитесь неограниченным количеством паролей на неограниченном количестве устройств из любого места. Bitwarden предоставляет решения с открытым исходным кодом по управлению паролями для всех, дома, на работе или в дороге. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Создавайте надежные, уникальные и случайные пароли на основе требований безопасности для каждого посещаемого вами сайта. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send быстро передает зашифрованную информацию - файлы и простой текст - напрямую кому угодно. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden предлагает для компаний планы Teams и Enterprise, чтобы вы могли безопасно делиться паролями с коллегами. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Почему выбирают Bitwarden: -Шифрование мирового класса -Пароли защищены передовым сквозным шифрованием (AES-256 bit, соленый хэштег и PBKDF2 SHA-256), поэтому ваши данные остаются в безопасности и конфиденциальности. +More reasons to choose Bitwarden: -Встроенный генератор паролей -Создавайте надежные, уникальные и случайные пароли на основе требований безопасности для каждого посещаемого вами сайта. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. - Глобальные переводы - Переводы Bitwarden существуют на 40 языках и постоянно растут благодаря нашему глобальному сообществу. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. - Кросс-платформенные приложения - Защищайте и делитесь конфиденциальными данными в вашем Bitwarden Vault из любого браузера, мобильного устройства, настольной ОС и т. д. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - Защищенный и бесплатный менеджер паролей для всех ваших устройств + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Синхронизация и доступ к хранилищу с нескольких устройств diff --git a/apps/browser/store/locales/si/copy.resx b/apps/browser/store/locales/si/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/si/copy.resx +++ b/apps/browser/store/locales/si/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/sk/copy.resx b/apps/browser/store/locales/sk/copy.resx index ba2a2a5a07..de7fa7dee3 100644 --- a/apps/browser/store/locales/sk/copy.resx +++ b/apps/browser/store/locales/sk/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Bezplatný správca hesiel + Bitwarden Password Manager - Bezpečný a bezplatný správca hesiel pre všetky vaše zariadenia + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. je materská spoločnosť spoločnosti 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -OHODNOTENÝ AKO NAJLEPŠÍ SPRÁVCA HESIEL V THE VERGE, U.S. NEWS & WORLD REPORT, CNET A ĎALŠÍMI. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Spravujte, ukladajte, zabezpečte a zdieľajte neobmedzený počet hesiel naprieč neobmedzeným počtom zariadení odkiaľkoľvek. Bitwarden ponúka open source riešenie na správu hesiel komukoľvek, kdekoľvek doma, v práci alebo na ceste. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Vygenerujte si silné, unikátne a náhodné heslá podľa bezpečnostných požiadaviek na každej stránke, ktorú navštevujete. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send rýchlo prenesie šifrované informácie -- súbory a text -- priamo komukoľvek. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden ponúka Teams a Enterprise paušály pre firmy, aby ste mohli bezpečne zdieľať hesla s kolegami. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Prečo si vybrať Bitwarden: -Svetová trieda v šifrovaní -Heslá sú chránené pokročilým end-to-end šifrovaním (AES-256 bit, salted hash a PBKDF2 SHA-256), takže Vaše dáta zostanú bezpečné a súkromné. +More reasons to choose Bitwarden: -Vstavaný generátor hesiel -Vygenerujte si silné, unikátne a náhodné heslá podľa bezpečnostných požiadaviek na každej stránke, ktorú navštevujete. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Svetová lokalizácia -Vďaka našej globálnej komunite má Bitwarden neustále rastúcu lokalizáciu už do 40 jazykov. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Aplikácie pre rôzne platformy -Zabezpečte a zdieľajte súkromné dáta prostredníctvom Bitwarden trezora z ktoréhokoľvek prehliadača, mobilného zariadenia, alebo stolného počítača a ďalších. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Bezpečný a bezplatný správca hesiel pre všetky vaše zariadenia + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Synchronizujte a pristupujte k vášmu trezoru z viacerých zariadení diff --git a/apps/browser/store/locales/sl/copy.resx b/apps/browser/store/locales/sl/copy.resx index 83288e3872..80886de48a 100644 --- a/apps/browser/store/locales/sl/copy.resx +++ b/apps/browser/store/locales/sl/copy.resx @@ -1,17 +1,17 @@  - @@ -118,40 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - brezplačni upravljalnik gesel + Bitwarden Password Manager - Varen in brezplačen upravljalnik gesel za vse vaše naprave + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. je matično podjetje podjetja 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAJBOŠJI UPRAVLJALNIK GESEL PO MNEJU THE VERGE, U.S. NEWS & WORLD REPORT, CNET IN DRUGIH. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Upravljajte, shranjujte, varujte in delite neomejeno število gesel na neomejenem številu naprav, kjerkoli. Bitwarden ponuja odprtokodne rešitve za upravljanje gesel vsem, tako doma kot v službi ali na poti. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Ustvarite močna, edinstvena in naključna gesla, skladna z varnostnimi zahtevami za vsako spletno mesto, ki ga obiščete. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Z Bitwarden Send hitro prenesite šifrirane informacije --- datoteke in navadno besedilo -- neposredno komurkoli. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden ponuja storitvi za organizacije Teams in Enterprise, s katerima lahko gesla varno delite s sodelavci. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Zakaj izbrati Bitwarden: -Vrhunsko šifriranje -Gesla so zaščitena z naprednim šifriranjem (AES-256, soljene hash-vrednosti in PBKDF2 SHA-256), tako da vaši podatki ostanejo varni in zasebni. +More reasons to choose Bitwarden: -Vgrajeni generator gesel -Ustvarite močna, edinstvena in naključna gesla v skladu z varnostnimi zahtevami za vsako spletno mesto, ki ga obiščete. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Prevodi za ves svet -Bitwarden je preveden že v 40 jezikov, naša globalna skupnost pa ves čas posodabljan in ustvarja nove prevede. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Deluje na vseh platformah -Varujte in delite svoje občutljive podatke znotraj vašega Bitwarden trezorja v katerem koli brskalniku, mobilni napravi, namiznem računalniku in drugje. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - Varen in brezplačen upravljalnik gesel za vse vaše naprave + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sinhronizirajte svoj trezor gesel in dostopajte do njega z več naprav diff --git a/apps/browser/store/locales/sr/copy.resx b/apps/browser/store/locales/sr/copy.resx index 9bfe799035..9c34d5812a 100644 --- a/apps/browser/store/locales/sr/copy.resx +++ b/apps/browser/store/locales/sr/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Бесплатни Менаџер Лозинке + Bitwarden Password Manager - Сигурни и бесплатни менаџер лозинке за сва Ваша уређаја + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. је матична компанија фирме 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -Именован као најбољи управљач лозинкама од стране новинских сајтова као што су THE VERGE, U.S. NEWS & WORLD REPORT, CNET, и других. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Управљајте, чувајте, обезбедите, и поделите неограничен број лозинки са неограниченог броја уређаја где год да се налазите. Bitwarden свима доноси решења за управљање лозинкама која су отвореног кода, било да сте код куће, на послу, или на путу. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Генеришите јаке, јединствене, и насумичне лозинке у зависности од безбедносних захтева за сваки сајт који често посећујете. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send брзо преноси шифроване информације--- датотеке и обичан текст-- директно и свима. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden нуди планове за компаније и предузећа како бисте могли безбедно да делите лозинке са вашим колегама. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Зашто изабрати Bitwarden: -Шифровање светске класе -Лозинке су заштићене напредним шифровањем од једног до другог краја (AES-256 bit, salted hashing, и PBKDF2 SHA-256) како би ваши подаци остали безбедни и приватни. +More reasons to choose Bitwarden: -Уграђен генератор лозинки -Генеришите јаке, јединствене, и насумичне лозинке у зависности од безбедносних захтева за сваки сајт који често посећујете. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Глобално преведен -Bitwarden преводи постоје за 40 језика и стално се унапређују, захваљујући нашој глобалној заједници. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Вишеплатформне апликације -Обезбедите и поделите осетљиве податке у вашем Bitwarden сефу из било ког претраживача, мобилног уређаја, или desktop оперативног система, и других. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Сигурни и бесплатни менаџер лозинке за сва Ваша уређаја + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Синхронизујте и приступите сефу са више уређаја diff --git a/apps/browser/store/locales/sv/copy.resx b/apps/browser/store/locales/sv/copy.resx index 8b3cb2a402..6406ab013e 100644 --- a/apps/browser/store/locales/sv/copy.resx +++ b/apps/browser/store/locales/sv/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Gratis lösenordshanterare + Bitwarden Password Manager - En säker och gratis lösenordshanterare för alla dina enheter + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. är moderbolag till 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -UTNÄMND TILL DEN BÄSTA LÖSENORDSHANTERAREN AV THE VERGE, U.S. NEWS & WORLD REPORT, CNET MED FLERA. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Hantera, lagra, säkra och dela ett obegränsat antal lösenord mellan ett obegränsat antal enheter var som helst ifrån. Bitwarden levererar lösningar för lösenordshantering med öppen källkod till alla, vare sig det är hemma, på jobbet eller på språng. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generera starka, unika och slumpmässiga lösenord baserat på säkerhetskrav för varje webbplats du besöker. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send överför snabbt krypterad information --- filer och klartext -- direkt till vem som helst. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden erbjuder abonnemang för team och företag så att du säkert kan dela lösenord med kollegor. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Varför välja Bitwarden: -Kryptering i världsklass -Lösenord skyddas med avancerad end-to-end-kryptering (AES-256 bitar, saltad hashtag och PBKDF2 SHA-256) så att dina data förblir säkra och privata. +More reasons to choose Bitwarden: -Inbyggd lösenordsgenerator -Generera starka, unika och slumpmässiga lösenord baserat på säkerhetskrav för varje webbplats du besöker. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Globala översättningar -Översättningar av Bitwarden finns på 40 språk och antalet växer tack vare vår globala gemenskap. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Plattformsoberoende program -Säkra och dela känsliga data i ditt Bitwardenvalv från alla webbläsare, mobiler och datorer. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - En säker och gratis lösenordshanterare för alla dina enheter + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Synkronisera och kom åt ditt valv från flera enheter diff --git a/apps/browser/store/locales/te/copy.resx b/apps/browser/store/locales/te/copy.resx index 191198691d..82e4eb1d88 100644 --- a/apps/browser/store/locales/te/copy.resx +++ b/apps/browser/store/locales/te/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Free Password Manager + Bitwarden Password Manager - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. is the parent company of 8bit Solutions LLC. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Why Choose Bitwarden: + +More reasons to choose Bitwarden: World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Built-in Password Generator -Generate strong, unique, and random passwords based on security requirements for every website you frequent. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Global Translations -Bitwarden translations exist in 40 languages and are growing, thanks to our global community. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. -Cross-Platform Applications +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - A secure and free password manager for all of your devices + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Sync and access your vault from multiple devices diff --git a/apps/browser/store/locales/th/copy.resx b/apps/browser/store/locales/th/copy.resx index 9c8965b01f..f784b1884b 100644 --- a/apps/browser/store/locales/th/copy.resx +++ b/apps/browser/store/locales/th/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – โปรแกรมจัดการรหัสผ่านฟรี + Bitwarden Password Manager - โปรแกรมจัดการรหัสผ่านที่ปลอดภัยและฟรี สำหรับอุปกรณ์ทั้งหมดของคุณ + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. เป็นบริษัทแม่ของ 8bit Solutions LLC + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -ได้รับการระบุชื่อเป็น โปรแกรมจัดการรหัสผ่านที่ดีที่สุด โดย The Verge, U.S. News & World Report, CNET, และที่อื่นๆ +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -สามารถจัดการ จัดเก็บ ปกป้อง และแชร์รหัสผ่านไม่จำกัดจำนวนระหว่างอุปกรณ์ต่างๆ โดยไม่จำกัดจำนวนจากที่ไหนก็ได้ Bitwarden เสนอโซลูชันจัดการรหัสผ่านโอเพนซอร์สให้กับทุกคน ไม่ว่าจะอยู่ที่บ้าน ที่ทำงาน หรือนอกสถานที่ +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -สามารถส่มสร้างรหัสผ่านที่ปลอดภัยและไม่ซ้ำกัน ตามเงื่อนไขความปลอดภัยที่กำหนดได้ สำหรับเว็บไซต์ทุกแห่งที่คุณใช้งานบ่อย +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send สามารถส่งข้อมูลที่ถูกเข้ารหัส --- ไฟล์ หรือ ข้อความ -- ตรงไปยังใครก็ได้ได้อย่างรวดเร็ว +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden มีแผนแบบ Teams และ Enterprise สำหรับบริษัทต่างๆ ซึางคุณสามารถแชร์รหัสผ่านกับเพื่อนร่วมงานได้อย่างปลอดภัย +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -ทำไมควรเลือก Bitwarden: -การเข้ารหัสมาตรฐานโลก -รหัสผ่านจะได้รับการปกป้องด้วยการเข้ารหัสชั้นสูง (AES-256 บิต, salted hashtag, และ PBKDF2 SHA-256) แบบต้นทางถึงปลายทาง เพื่อให้ข้อมูลของคุณปลอดภัยและเป็นส่วนตัว +More reasons to choose Bitwarden: -มีตัวช่วยส่มสร้างรหัสผ่าน -สามารถสุ่มสร้างรหัสผ่านที่ปลอดภัยและไม่ซ้ำกัน ตามเงื่อนไขความปลอดภัยที่กำหนดได้ สำหรับเว็บไซต์ทุกแห่งที่คุณใช้งานบ่อย +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -แปลเป็นภาษาต่างๆ ทั่วโลก -Bitwarden ได้รับการแปลเป็นภาษาต่างๆ กว่า 40 ภาษา และกำลังเพิ่มขึ้นเรื่อยๆ ด้วยความสนับสนุนจากชุมชนผู้ใช้งานทั่วโลก +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -แอปพลิเคชันข้ามแพลตฟอร์ม -ปกป้องและแชร์ข้อมูลอ่อนไหวในตู้เซฟ Bitwarden จากเว็บเบราว์เซอร์ อุปกรณ์มือถือ หรือเดสท็อป หรือช่องทางอื่นๆ +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - โปรแกรมจัดการรหัสผ่านที่ปลอดภัยและฟรี สำหรับอุปกรณ์ทั้งหมดของคุณ + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. ซิงค์และเข้าถึงตู้นิรภัยของคุณจากหลายอุปกรณ์ diff --git a/apps/browser/store/locales/tr/copy.resx b/apps/browser/store/locales/tr/copy.resx index 1fc3e2a34b..539aad3aee 100644 --- a/apps/browser/store/locales/tr/copy.resx +++ b/apps/browser/store/locales/tr/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Ücretsiz Parola Yöneticisi + Bitwarden Password Manager - Tüm aygıtlarınız için güvenli ve ücretsiz bir parola yöneticisi + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc., 8bit Solutions LLC’nin ana şirketidir. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -THE VERGE, U.S. NEWS & WORLD REPORT, CNET VE BİRÇOK MEDYA KURULUŞUNA GÖRE EN İYİ PAROLA YÖNETİCİSİ. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Sınırsız sayıda parolayı istediğiniz kadar cihazda yönetin, saklayın, koruyun ve paylaşın. Bitwarden; herkesin evde, işte veya yolda kullanabileceği açık kaynaklı parola yönetim çözümleri sunuyor. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Sık kullandığınız web siteleri için güvenlik gereksinimlerinize uygun, güçlü, benzersiz ve rastgele parolalar oluşturabilirsiniz. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send, şifrelenmiş bilgileri (dosyalar ve düz metinler) herkese hızlı bir şekilde iletmenizi sağlıyor. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden, parolaları iş arkadaşlarınızla güvenli bir şekilde paylaşabilmeniz için şirketlere yönelik Teams ve Enterprise paketleri de sunuyor. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Neden Bitwarden? -Üst düzey şifreleme -Parolalarınız gelişmiş uçtan uca şifreleme (AES-256 bit, salted hashing ve PBKDF2 SHA-256) ile korunuyor, böylece verileriniz güvende ve gizli kalıyor. +More reasons to choose Bitwarden: -Dahili parola oluşturucu -Sık kullandığınız web siteleri için güvenlik gereksinimlerinize uygun, güçlü, benzersiz ve rastgele parolalar oluşturabilirsiniz. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Çeviriler -Bitwarden 40 dilde kullanılabiliyor ve gönüllü topluluğumuz sayesinde çeviri sayısı giderek artıyor. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Her platformla uyumlu uygulamalar -Bitwarden kasanızdaki hassas verilere her tarayıcıdan, mobil cihazdan veya masaüstü işletim sisteminden ulaşabilir ve onları paylaşabilirsiniz. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Tüm cihazarınız için güvenli ve ücretsiz bir parola yöneticisi + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Hesabınızı senkronize ederek kasanıza tüm cihazlarınızdan ulaşın diff --git a/apps/browser/store/locales/uk/copy.resx b/apps/browser/store/locales/uk/copy.resx index d59cd7f103..5a7de18363 100644 --- a/apps/browser/store/locales/uk/copy.resx +++ b/apps/browser/store/locales/uk/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Безплатний менеджер паролів + Bitwarden Password Manager - Захищений, безплатний менеджер паролів для всіх ваших пристроїв + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - 8bit Solutions LLC є дочірньою компанією Bitwarden, Inc. + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -НАЙКРАЩИЙ МЕНЕДЖЕР ПАРОЛІВ ЗА ВЕРСІЄЮ THE VERGE, U.S. NEWS & WORLD REPORT, CNET, А ТАКОЖ ІНШИХ ВИДАНЬ. +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Зберігайте, захищайте, керуйте і надавайте доступ до паролів на різних пристроях де завгодно. Bitwarden пропонує рішення для керування паролями на основі відкритого програмного коду особистим та корпоративним користувачам на всіх пристроях. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Генеруйте надійні, випадкові та унікальні паролі, які відповідають вимогам безпеки, для кожного вебсайту та сервісу. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Швидко відправляйте будь-кому зашифровану інформацію, як-от файли чи звичайний текст, за допомогою функції Bitwarden Send. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden пропонує командні та корпоративні тарифні плани для компаній, щоб ви могли безпечно обмінюватися паролями з колегами. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Чому варто обрати Bitwarden: -Всесвітньо визнані стандарти шифрування -Паролі захищаються з використанням розширеного наскрізного шифрування (AES-256 bit, хешування з сіллю та PBKDF2 SHA-256), тому ваші дані завжди захищені та приватні. +More reasons to choose Bitwarden: -Вбудований генератор паролів -Генеруйте надійні, випадкові та унікальні паролі, які відповідають вимогам безпеки, для кожного вебсайту та сервісу. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Переклад багатьма мовами -Завдяки нашій глобальній спільноті, Bitwarden перекладено 40 мовами, і їх кількість зростає. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Програми для різних платформ -Зберігайте і діліться важливими даними, а також користуйтеся іншими можливостями у вашому сховищі Bitwarden в будь-якому браузері, мобільному пристрої, чи комп'ютерній операційній системі. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Захищений, безплатний менеджер паролів для всіх ваших пристроїв + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Синхронізуйте й отримуйте доступ до свого сховища на різних пристроях diff --git a/apps/browser/store/locales/vi/copy.resx b/apps/browser/store/locales/vi/copy.resx index 220d50bdfa..e0403d1f32 100644 --- a/apps/browser/store/locales/vi/copy.resx +++ b/apps/browser/store/locales/vi/copy.resx @@ -1,17 +1,17 @@  - @@ -118,41 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - Trình quản lý mật khẩu miễn phí + Bitwarden Password Manager - Một trình quản lý mật khẩu an toàn và miễn phí cho mọi thiết bị của bạn + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc là công ty mẹ của 8bit Solutions LLC + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -ĐƯỢC ĐÁNH GIÁ LÀ TRÌNH QUẢN LÝ MẬT KHẨU TỐT NHẤT BỞI NHÀ BÁO LỚN NHƯ THE VERGE, CNET, U.S. NEWS & WORLD REPORT VÀ HƠN NỮA +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -Quản lý, lưu trữ, bảo mật và chia sẻ mật khẩu không giới hạn trên các thiết bị không giới hạn mọi lúc, mọi nơi. Bitwarden cung cấp các giải pháp quản lý mật khẩu mã nguồn mở cho tất cả mọi người, cho dù ở nhà, tại cơ quan hay khi đang di chuyển. +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -Tạo mật khẩu mạnh, không bị trùng và ngẫu nhiên dựa trên các yêu cầu bảo mật cho mọi trang web bạn thường xuyên sử dụng. +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Tính năng 'Bitwarden Send' nhanh chóng truyền thông tin được mã hóa --- tệp và văn bản - trực tiếp đến bất kỳ ai. +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden cung cấp các gói 'Nhóm' và 'Doanh nghiệp' cho các công ty để bạn có thể chia sẻ mật khẩu với đồng nghiệp một cách an toàn. +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -Tại sao bạn nên chọn Bitwarden: -Mã hóa tốt nhất thế giới -Mật khẩu được bảo vệ bằng mã hóa đầu cuối (end-to-end encryption) tiên tiến như AES-256 bit, salted hashtag, và PBKDF2 SHA-256 nên dữ liệu của bạn luôn an toàn và riêng tư. +More reasons to choose Bitwarden: -Trình tạo mật khẩu tích hợp -Tạo mật khẩu mạnh, không bị trùng lặp, và ngẫu nhiên dựa trên các yêu cầu bảo mật cho mọi trang web mà bạn thường xuyên sử dụng. +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -Bản dịch ngôn ngữ từ cộng đồng -Bitwarden đã có bản dịch 40 ngôn ngữ và đang phát triển nhờ vào cộng đồng toàn cầu của chúng tôi. +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -Ứng dụng đa nền tảng -Bảo mật và chia sẻ dữ liệu nhạy cảm trong kho lưu trữ Bitwarden của bạn từ bất kỳ trình duyệt, điện thoại thông minh hoặc hệ điều hành máy tính nào, và hơn thế nữa. +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! - Một trình quản lý mật khẩu an toàn và miễn phí cho mọi thiết bị của bạn + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. Đồng bộ hóa và truy cập vào kho lưu trữ của bạn từ nhiều thiết bị diff --git a/apps/browser/store/locales/zh_CN/copy.resx b/apps/browser/store/locales/zh_CN/copy.resx index e424ef743a..94543f8f6f 100644 --- a/apps/browser/store/locales/zh_CN/copy.resx +++ b/apps/browser/store/locales/zh_CN/copy.resx @@ -1,17 +1,17 @@  - @@ -118,40 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – 免费密码管理器 + Bitwarden Password Manager - 安全免费的跨平台密码管理器 + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. 是 8bit Solutions LLC 的母公司。 + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -被 THE VERGE、U.S. NEWS & WORLD REPORT、CNET 等评为最佳的密码管理器。 +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -从任何地方,不限制设备,管理、存储、保护和共享无限的密码。Bitwarden 为每个人提供开源的密码管理解决方案,无论是在家里,在工作中,还是在旅途中。 +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -基于安全要求,为您经常访问的每个网站生成强大、唯一和随机的密码。 +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send 快速传输加密的信息---文件和文本---直接给任何人。 +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden 为公司提供团队和企业计划,因此您可以安全地与同事共享密码。 +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -为何选择 Bitwarden: -世界级的加密技术 -密码受到先进的端到端加密(AES-256 位、盐化标签和 PBKDF2 SHA-256)的保护,为您的数据保持安全和隐密。 +More reasons to choose Bitwarden: -内置密码生成器 -基于安全要求,为您经常访问的每个网站生成强大、唯一和随机的密码。 +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -全球翻译 -Bitwarden 的翻译有 40 种语言,而且还在不断增加,感谢我们的全球社区。 +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -跨平台应用程序 -从任何浏览器、移动设备或桌面操作系统,以及更多的地方,在您的 Bitwarden 密码库中保护和分享敏感数据。 +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - 安全免费的跨平台密码管理器 + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. 从多台设备同步和访问密码库 diff --git a/apps/browser/store/locales/zh_TW/copy.resx b/apps/browser/store/locales/zh_TW/copy.resx index be39fdca06..ab37ed5f7b 100644 --- a/apps/browser/store/locales/zh_TW/copy.resx +++ b/apps/browser/store/locales/zh_TW/copy.resx @@ -1,17 +1,17 @@  - @@ -118,40 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – 免費密碼管理工具 + Bitwarden Password Manager - 安全、免費、跨平台的密碼管理工具 + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - Bitwarden, Inc. 是 8bit Solutions LLC 的母公司。 + Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! -被 THE VERGE、U.S. NEWS & WORLD REPORT、CNET 等評為最佳的密碼管理器。 +SECURE YOUR DIGITAL LIFE +Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. -從任何地方,不限制設備,管理、存儲、保護和共享無限的密碼。Bitwarden 為每個人提供開源的密碼管理解決方案,無論是在家裡,在工作中,還是在旅途中。 +ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE +Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. -基於安全要求,為您經常訪問的每個網站生成強大、唯一和隨機的密碼。 +EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE +Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. -Bitwarden Send 快速傳輸加密的信息---文檔和文本---直接給任何人。 +EMPOWER YOUR TEAMS WITH BITWARDEN +Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. -Bitwarden 為公司提供團隊和企業計劃,因此您可以安全地與同事共享密碼。 +Use Bitwarden to secure your workforce and share sensitive information with colleagues. -為何選擇 Bitwarden: -世界級的加密技術 -密碼受到先進的端到端加密(AES-256 位、鹽化標籤和 PBKDF2 SHA-256)的保護,為您的資料保持安全和隱密。 +More reasons to choose Bitwarden: -內置密碼生成器 -基於安全要求,為您經常訪問的每個網站生成強大、唯一和隨機的密碼。 +World-Class Encryption +Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. -全球翻譯 -Bitwarden 的翻譯有 40 種語言,而且還在不斷增加,感謝我們的全球社區。 +3rd-party Audits +Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. -跨平台應用程式 -從任何瀏覽器、行動裝置或桌面作業系統,以及更多的地方,在您的 Bitwarden 密碼庫中保護和分享敏感資料。 +Advanced 2FA +Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. + +Bitwarden Send +Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. + +Built-in Generator +Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. + +Global Translations +Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. + +Cross-Platform Applications +Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. + +Bitwarden secures more than just passwords +End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! + - 安全、免費、跨平台的密碼管理工具 + At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. 在多部裝置上同步和存取密碼庫 diff --git a/apps/browser/store/windows/AppxManifest.xml b/apps/browser/store/windows/AppxManifest.xml index f57b3db988..df02ea085c 100644 --- a/apps/browser/store/windows/AppxManifest.xml +++ b/apps/browser/store/windows/AppxManifest.xml @@ -11,7 +11,7 @@ Version="0.0.0.0"/> - Bitwarden Extension - Free Password Manager + Bitwarden Password Manager 8bit Solutions LLC Assets/icon_50.png @@ -30,10 +30,10 @@ @@ -41,7 +41,7 @@ + DisplayName="Bitwarden Password Manager"> diff --git a/apps/browser/test.setup.ts b/apps/browser/test.setup.ts index 1031268186..4800b4c17f 100644 --- a/apps/browser/test.setup.ts +++ b/apps/browser/test.setup.ts @@ -68,6 +68,8 @@ const tabs = { const scripting = { executeScript: jest.fn(), + registerContentScripts: jest.fn(), + unregisterContentScripts: jest.fn(), }; const windows = { @@ -124,6 +126,19 @@ const offscreen = { }, }; +const permissions = { + contains: jest.fn((permissions, callback) => { + callback(true); + }), +}; + +const webNavigation = { + onCommitted: { + addListener: jest.fn(), + removeListener: jest.fn(), + }, +}; + // set chrome global.chrome = { i18n, @@ -137,4 +152,6 @@ global.chrome = { privacy, extension, offscreen, + permissions, + webNavigation, } as any; diff --git a/apps/browser/tsconfig.json b/apps/browser/tsconfig.json index 694246f59a..505f1533ae 100644 --- a/apps/browser/tsconfig.json +++ b/apps/browser/tsconfig.json @@ -9,6 +9,7 @@ "allowJs": true, "sourceMap": true, "baseUrl": ".", + "lib": ["ES2021.String"], "paths": { "@bitwarden/admin-console": ["../../libs/admin-console/src"], "@bitwarden/angular/*": ["../../libs/angular/src/*"], diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js index 3b5724b198..2756ab4395 100644 --- a/apps/browser/webpack.config.js +++ b/apps/browser/webpack.config.js @@ -166,8 +166,6 @@ const mainConfig = { "content/notificationBar": "./src/autofill/content/notification-bar.ts", "content/contextMenuHandler": "./src/autofill/content/context-menu-handler.ts", "content/content-message-handler": "./src/autofill/content/content-message-handler.ts", - "content/fido2/trigger-fido2-content-script-injection": - "./src/vault/fido2/content/trigger-fido2-content-script-injection.ts", "content/fido2/content-script": "./src/vault/fido2/content/content-script.ts", "content/fido2/page-script": "./src/vault/fido2/content/page-script.ts", "notification/bar": "./src/autofill/notification/bar.ts", @@ -277,6 +275,8 @@ if (manifestVersion == 2) { mainConfig.entry.background = "./src/platform/background.ts"; mainConfig.entry["content/lp-suppress-import-download-script-append-mv2"] = "./src/tools/content/lp-suppress-import-download-script-append.mv2.ts"; + mainConfig.entry["content/fido2/page-script-append-mv2"] = + "./src/vault/fido2/content/page-script-append.mv2.ts"; configs.push(mainConfig); } else { diff --git a/apps/cli/src/bw.ts b/apps/cli/src/bw.ts index ebae308a81..437f807bc6 100644 --- a/apps/cli/src/bw.ts +++ b/apps/cli/src/bw.ts @@ -18,11 +18,13 @@ import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; +import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service"; import { OrganizationUserServiceImplementation } from "@bitwarden/common/admin-console/services/organization-user/organization-user.service.implementation"; import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; +import { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service"; @@ -58,10 +60,10 @@ import { } from "@bitwarden/common/platform/biometrics/biometric-state.service"; import { KeySuffixOptions, LogLevelType } from "@bitwarden/common/platform/enums"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; +import { MessageSender } from "@bitwarden/common/platform/messaging"; import { Account } from "@bitwarden/common/platform/models/domain/account"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { AppIdService } from "@bitwarden/common/platform/services/app-id.service"; -import { BroadcasterService } from "@bitwarden/common/platform/services/broadcaster.service"; import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; @@ -73,7 +75,6 @@ import { KeyGenerationService } from "@bitwarden/common/platform/services/key-ge import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; -import { NoopMessagingService } from "@bitwarden/common/platform/services/noop-messaging.service"; import { StateService } from "@bitwarden/common/platform/services/state.service"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { @@ -153,7 +154,7 @@ global.DOMParser = new jsdom.JSDOM().window.DOMParser; const packageJson = require("../package.json"); export class Main { - messagingService: NoopMessagingService; + messagingService: MessageSender; storageService: LowdbStorageService; secureStorageService: NodeEnvSecureStorageService; memoryStorageService: MemoryStorageService; @@ -210,7 +211,6 @@ export class Main { organizationService: OrganizationService; providerService: ProviderService; twoFactorService: TwoFactorService; - broadcasterService: BroadcasterService; folderApiService: FolderApiService; userVerificationApiService: UserVerificationApiService; organizationApiService: OrganizationApiServiceAbstraction; @@ -232,6 +232,7 @@ export class Main { stateEventRunnerService: StateEventRunnerService; biometricStateService: BiometricStateService; billingAccountProfileStateService: BillingAccountProfileStateService; + providerApiService: ProviderApiServiceAbstraction; constructor() { let p = null; @@ -295,7 +296,7 @@ export class Main { stateEventRegistrarService, ); - this.messagingService = new NoopMessagingService(); + this.messagingService = MessageSender.EMPTY; this.accountService = new AccountServiceImplementation( this.messagingService, @@ -308,9 +309,7 @@ export class Main { this.singleUserStateProvider, ); - this.derivedStateProvider = new DefaultDerivedStateProvider( - this.memoryStorageForStateProviders, - ); + this.derivedStateProvider = new DefaultDerivedStateProvider(storageServiceProvider); this.stateProvider = new DefaultStateProvider( this.activeUserStateProvider, @@ -419,8 +418,6 @@ export class Main { this.searchService = new SearchService(this.logService, this.i18nService, this.stateProvider); - this.broadcasterService = new BroadcasterService(); - this.collectionService = new CollectionService( this.cryptoService, this.i18nService, @@ -483,6 +480,7 @@ export class Main { this.masterPasswordService, this.cryptoService, this.apiService, + this.stateProvider, ); this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService( @@ -543,13 +541,13 @@ export class Main { this.encryptService, this.cipherFileUploadService, this.configService, + this.stateProvider, ); this.folderService = new FolderService( this.cryptoService, this.i18nService, this.cipherService, - this.stateService, this.stateProvider, ); @@ -633,6 +631,7 @@ export class Main { this.avatarService, async (expired: boolean) => await this.logout(), this.billingAccountProfileStateService, + this.tokenService, ); this.totpService = new TotpService(this.cryptoFunctionService, this.logService); @@ -691,6 +690,8 @@ export class Main { this.eventUploadService, this.authService, ); + + this.providerApiService = new ProviderApiService(this.apiService); } async run() { diff --git a/apps/cli/src/commands/edit.command.ts b/apps/cli/src/commands/edit.command.ts index 3d4f9529ad..e64ff8b551 100644 --- a/apps/cli/src/commands/edit.command.ts +++ b/apps/cli/src/commands/edit.command.ts @@ -86,8 +86,7 @@ export class EditCommand { cipherView = CipherExport.toView(req, cipherView); const encCipher = await this.cipherService.encrypt(cipherView); try { - await this.cipherService.updateWithServer(encCipher); - const updatedCipher = await this.cipherService.get(cipher.id); + const updatedCipher = await this.cipherService.updateWithServer(encCipher); const decCipher = await updatedCipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher), ); @@ -111,8 +110,7 @@ export class EditCommand { cipher.collectionIds = req; try { - await this.cipherService.saveCollectionsWithServer(cipher); - const updatedCipher = await this.cipherService.get(cipher.id); + const updatedCipher = await this.cipherService.saveCollectionsWithServer(cipher); const decCipher = await updatedCipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher), ); diff --git a/apps/cli/src/locales/en/messages.json b/apps/cli/src/locales/en/messages.json index e12b30af2c..8364e0b328 100644 --- a/apps/cli/src/locales/en/messages.json +++ b/apps/cli/src/locales/en/messages.json @@ -49,5 +49,14 @@ }, "unsupportedEncryptedImport": { "message": "Importing encrypted files is currently not supported." + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/cli/src/vault/create.command.ts b/apps/cli/src/vault/create.command.ts index b813227109..78ee04e73c 100644 --- a/apps/cli/src/vault/create.command.ts +++ b/apps/cli/src/vault/create.command.ts @@ -80,8 +80,7 @@ export class CreateCommand { private async createCipher(req: CipherExport) { const cipher = await this.cipherService.encrypt(CipherExport.toView(req)); try { - await this.cipherService.createWithServer(cipher); - const newCipher = await this.cipherService.get(cipher.id); + const newCipher = await this.cipherService.createWithServer(cipher); const decCipher = await newCipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(newCipher), ); @@ -142,12 +141,11 @@ export class CreateCommand { } try { - await this.cipherService.saveAttachmentRawWithServer( + const updatedCipher = await this.cipherService.saveAttachmentRawWithServer( cipher, fileName, new Uint8Array(fileBuf).buffer, ); - const updatedCipher = await this.cipherService.get(cipher.id); const decCipher = await updatedCipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher), ); diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 5fd26f32ba..4f0d05581c 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -24,7 +24,7 @@ "**/node_modules/argon2/package.json", "**/node_modules/argon2/lib/binding/napi-v3/argon2.node" ], - "electronVersion": "28.2.8", + "electronVersion": "28.3.1", "generateUpdatesFilesForAllChannels": true, "publish": { "provider": "generic", diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 0dc23b04b1..4bb0ab2d93 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2024.4.1", + "version": "2024.4.2", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index 07fcc5d3b8..f3958d7c87 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -3,6 +3,7 @@ import { FormBuilder } from "@angular/forms"; import { BehaviorSubject, firstValueFrom, Observable, Subject } from "rxjs"; import { concatMap, debounceTime, filter, map, switchMap, takeUntil, tap } from "rxjs/operators"; +import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -13,6 +14,7 @@ import { DeviceType } from "@bitwarden/common/enums"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; @@ -20,11 +22,13 @@ import { BiometricStateService } from "@bitwarden/common/platform/biometrics/bio import { ThemeType, KeySuffixOptions } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; +import { UserId } from "@bitwarden/common/types/guid"; import { DialogService } from "@bitwarden/components"; import { SetPinComponent } from "../../auth/components/set-pin.component"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; +import { NativeMessagingManifestService } from "../services/native-messaging-manifest.service"; @Component({ selector: "app-settings", @@ -60,6 +64,7 @@ export class SettingsComponent implements OnInit { showAppPreferences = true; currentUserEmail: string; + currentUserId: UserId; availableVaultTimeoutActions$: Observable; vaultTimeoutPolicyCallout: Observable<{ @@ -122,6 +127,9 @@ export class SettingsComponent implements OnInit { private desktopSettingsService: DesktopSettingsService, private biometricStateService: BiometricStateService, private desktopAutofillSettingsService: DesktopAutofillSettingsService, + private authRequestService: AuthRequestServiceAbstraction, + private logService: LogService, + private nativeMessagingManifestService: NativeMessagingManifestService, ) { const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop; @@ -207,6 +215,7 @@ export class SettingsComponent implements OnInit { return; } this.currentUserEmail = await this.stateService.getEmail(); + this.currentUserId = (await this.stateService.getUserId()) as UserId; this.availableVaultTimeoutActions$ = this.refreshTimeoutSettings$.pipe( switchMap(() => this.vaultTimeoutSettingsService.availableVaultTimeoutActions$()), @@ -249,7 +258,8 @@ export class SettingsComponent implements OnInit { requirePasswordOnStart: await firstValueFrom( this.biometricStateService.requirePasswordOnStart$, ), - approveLoginRequests: (await this.stateService.getApproveLoginRequests()) ?? false, + approveLoginRequests: + (await this.authRequestService.getAcceptAuthRequests(this.currentUserId)) ?? false, clearClipboard: await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$), minimizeOnCopyToClipboard: await this.stateService.getMinimizeOnCopyToClipboard(), enableFavicons: await firstValueFrom(this.domainSettingsService.showFavicons$), @@ -622,11 +632,20 @@ export class SettingsComponent implements OnInit { } await this.stateService.setEnableBrowserIntegration(this.form.value.enableBrowserIntegration); - this.messagingService.send( - this.form.value.enableBrowserIntegration - ? "enableBrowserIntegration" - : "disableBrowserIntegration", + + const errorResult = await this.nativeMessagingManifestService.generate( + this.form.value.enableBrowserIntegration, ); + if (errorResult !== null) { + this.logService.error("Error in browser integration: " + errorResult); + await this.dialogService.openSimpleDialog({ + title: { key: "browserIntegrationErrorTitle" }, + content: { key: "browserIntegrationErrorDesc" }, + acceptButtonText: { key: "ok" }, + cancelButtonText: null, + type: "danger", + }); + } if (!this.form.value.enableBrowserIntegration) { this.form.controls.enableBrowserIntegrationFingerprint.setValue(false); @@ -641,15 +660,28 @@ export class SettingsComponent implements OnInit { this.form.value.enableDuckDuckGoBrowserIntegration, ); + // Adding to cover users on a previous version of DDG + await this.stateService.setEnableDuckDuckGoBrowserIntegration( + this.form.value.enableDuckDuckGoBrowserIntegration, + ); + if (!this.form.value.enableBrowserIntegration) { await this.stateService.setDuckDuckGoSharedKey(null); } - this.messagingService.send( - this.form.value.enableDuckDuckGoBrowserIntegration - ? "enableDuckDuckGoBrowserIntegration" - : "disableDuckDuckGoBrowserIntegration", + const errorResult = await this.nativeMessagingManifestService.generateDuckDuckGo( + this.form.value.enableDuckDuckGoBrowserIntegration, ); + if (errorResult !== null) { + this.logService.error("Error in DDG browser integration: " + errorResult); + await this.dialogService.openSimpleDialog({ + title: { key: "browserIntegrationUnsupportedTitle" }, + content: errorResult.message, + acceptButtonText: { key: "ok" }, + cancelButtonText: null, + type: "warning", + }); + } } async saveBrowserIntegrationFingerprint() { @@ -665,7 +697,10 @@ export class SettingsComponent implements OnInit { } async updateApproveLoginRequests() { - await this.stateService.setApproveLoginRequests(this.form.value.approveLoginRequests); + await this.authRequestService.setAcceptAuthRequests( + this.form.value.approveLoginRequests, + this.currentUserId, + ); } ngOnDestroy() { diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index b2b44e6b21..ad99a3a447 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -3,14 +3,11 @@ import { NgZone, OnDestroy, OnInit, - SecurityContext, Type, ViewChild, ViewContainerRef, } from "@angular/core"; -import { DomSanitizer } from "@angular/platform-browser"; import { Router } from "@angular/router"; -import { IndividualConfig, ToastrService } from "ngx-toastr"; import { firstValueFrom, Subject, takeUntil } from "rxjs"; import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; @@ -49,7 +46,7 @@ import { CollectionService } from "@bitwarden/common/vault/abstractions/collecti import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { DeleteAccountComponent } from "../auth/delete-account.component"; import { LoginApprovalComponent } from "../auth/login/login-approval.component"; @@ -129,9 +126,8 @@ export class AppComponent implements OnInit, OnDestroy { private cipherService: CipherService, private authService: AuthService, private router: Router, - private toastrService: ToastrService, + private toastService: ToastService, private i18nService: I18nService, - private sanitizer: DomSanitizer, private ngZone: NgZone, private vaultTimeoutService: VaultTimeoutService, private vaultTimeoutSettingsService: VaultTimeoutSettingsService, @@ -294,7 +290,7 @@ export class AppComponent implements OnInit, OnDestroy { ); break; case "showToast": - this.showToast(message); + this.toastService._showToast(message); break; case "copiedToClipboard": if (!message.clearing) { @@ -674,34 +670,6 @@ export class AppComponent implements OnInit, OnDestroy { }); } - private showToast(msg: any) { - let message = ""; - - const options: Partial = {}; - - if (typeof msg.text === "string") { - message = msg.text; - } else if (msg.text.length === 1) { - message = msg.text[0]; - } else { - msg.text.forEach( - (t: string) => - (message += "

" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "

"), - ); - options.enableHtml = true; - } - if (msg.options != null) { - if (msg.options.trustedHtml === true) { - options.enableHtml = true; - } - if (msg.options.timeout != null && msg.options.timeout > 0) { - options.timeOut = msg.options.timeout; - } - } - - this.toastrService.show(message, msg.title, options, "toast-" + msg.type); - } - private routeToVault(action: string, cipherType: CipherType) { if (!this.router.url.includes("vault")) { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index d1a83d468c..ae2e1ba97c 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -12,6 +12,7 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; +import { UserKeyInitService } from "@bitwarden/common/platform/services/user-key-init.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service"; import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; @@ -35,6 +36,7 @@ export class InitService { private nativeMessagingService: NativeMessagingService, private themingService: AbstractThemingService, private encryptService: EncryptService, + private userKeyInitService: UserKeyInitService, @Inject(DOCUMENT) private document: Document, ) {} @@ -42,6 +44,8 @@ export class InitService { return async () => { this.nativeMessagingService.init(); await this.stateService.init({ runMigrations: false }); // Desktop will run them in main process + this.userKeyInitService.listenForActiveUserChangesToSetUserKey(); + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises this.syncService.fullSync(true); diff --git a/apps/desktop/src/app/services/native-messaging-manifest.service.ts b/apps/desktop/src/app/services/native-messaging-manifest.service.ts new file mode 100644 index 0000000000..6cc58a581b --- /dev/null +++ b/apps/desktop/src/app/services/native-messaging-manifest.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from "@angular/core"; + +@Injectable() +export class NativeMessagingManifestService { + constructor() {} + + async generate(create: boolean): Promise { + return ipc.platform.nativeMessaging.manifests.generate(create); + } + async generateDuckDuckGo(create: boolean): Promise { + return ipc.platform.nativeMessaging.manifests.generateDuckDuckGo(create); + } +} diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 8e412d4977..d1d51c0f1c 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -1,4 +1,5 @@ import { APP_INITIALIZER, NgModule } from "@angular/core"; +import { Subject, merge } from "rxjs"; import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { @@ -14,6 +15,7 @@ import { SYSTEM_THEME_OBSERVABLE, SafeInjectionToken, STATE_FACTORY, + INTRAPROCESS_MESSAGING_SUBJECT, } from "@bitwarden/angular/services/injection-tokens"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; @@ -23,7 +25,6 @@ import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/ab import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; -import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; @@ -42,6 +43,9 @@ import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/ import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; +import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; +// eslint-disable-next-line no-restricted-imports -- Used for dependency injection +import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; @@ -63,11 +67,12 @@ import { ELECTRON_SUPPORTS_SECURE_STORAGE, ElectronPlatformUtilsService, } from "../../platform/services/electron-platform-utils.service"; -import { ElectronRendererMessagingService } from "../../platform/services/electron-renderer-messaging.service"; +import { ElectronRendererMessageSender } from "../../platform/services/electron-renderer-message.sender"; import { ElectronRendererSecureStorageService } from "../../platform/services/electron-renderer-secure-storage.service"; import { ElectronRendererStorageService } from "../../platform/services/electron-renderer-storage.service"; import { ElectronStateService } from "../../platform/services/electron-state.service"; import { I18nRendererService } from "../../platform/services/i18n.renderer.service"; +import { fromIpcMessaging } from "../../platform/utils/from-ipc-messaging"; import { fromIpcSystemTheme } from "../../platform/utils/from-ipc-system-theme"; import { EncryptedMessageHandlerService } from "../../services/encrypted-message-handler.service"; import { NativeMessageHandlerService } from "../../services/native-message-handler.service"; @@ -76,6 +81,7 @@ import { SearchBarService } from "../layout/search/search-bar.service"; import { DesktopFileDownloadService } from "./desktop-file-download.service"; import { InitService } from "./init.service"; +import { NativeMessagingManifestService } from "./native-messaging-manifest.service"; import { RendererCryptoFunctionService } from "./renderer-crypto-function.service"; const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK"); @@ -137,9 +143,24 @@ const safeProviders: SafeProvider[] = [ deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY, GlobalStateProvider], }), safeProvider({ - provide: MessagingServiceAbstraction, - useClass: ElectronRendererMessagingService, - deps: [BroadcasterServiceAbstraction], + provide: MessageSender, + useFactory: (subject: Subject>) => + MessageSender.combine( + new ElectronRendererMessageSender(), // Communication with main process + new SubjectMessageSender(subject), // Communication with ourself + ), + deps: [INTRAPROCESS_MESSAGING_SUBJECT], + }), + safeProvider({ + provide: MessageListener, + useFactory: (subject: Subject>) => + new MessageListener( + merge( + subject.asObservable(), // For messages from the same context + fromIpcMessaging(), // For messages from the main process + ), + ), + deps: [INTRAPROCESS_MESSAGING_SUBJECT], }), safeProvider({ provide: AbstractStorageService, @@ -249,6 +270,11 @@ const safeProviders: SafeProvider[] = [ provide: DesktopAutofillSettingsService, deps: [StateProvider], }), + safeProvider({ + provide: NativeMessagingManifestService, + useClass: NativeMessagingManifestService, + deps: [], + }), ]; @NgModule({ diff --git a/apps/desktop/src/app/tools/generator.component.spec.ts b/apps/desktop/src/app/tools/generator.component.spec.ts index 53f919a596..51b5bf93a2 100644 --- a/apps/desktop/src/app/tools/generator.component.spec.ts +++ b/apps/desktop/src/app/tools/generator.component.spec.ts @@ -10,6 +10,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { GeneratorComponent } from "./generator.component"; @@ -54,6 +55,10 @@ describe("GeneratorComponent", () => { provide: LogService, useValue: mock(), }, + { + provide: CipherService, + useValue: mock(), + }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index ee32c045c9..afdfc90d76 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Verander Hoofwagwoord" }, - "changeMasterPasswordConfirmation": { - "message": "U kan u hoofwagwoord op die bitwarden.com-webkluis verander. Wil u die webwerf nou besoek?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Vingerafdrukfrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Blaaierintegrasie word nie ondersteun nie" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Ongelukkig word blaaierintegrasie tans slegs in die weergawe vir die Mac-toepwinkel ondersteun." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 104a9f7780..7869b0894b 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "تغيير كلمة المرور الرئيسية" }, - "changeMasterPasswordConfirmation": { - "message": "يمكنك تغيير كلمة المرور الرئيسية من خزنة الويب في bitwarden.com. هل تريد زيارة الموقع الآن؟" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "عبارة بصمة الإصبع", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "تكامل المتصفح غير مدعوم" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "للأسف، لا يتم دعم تكامل المتصفح إلا في إصدار متجر تطبيقات ماك في الوقت الحالي." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index f404c7f95a..d4cea4f06e 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -717,7 +717,7 @@ "message": "Bildiriş server URL-si" }, "iconsUrl": { - "message": "Nişan server URL-si" + "message": "İkon server URL-si" }, "environmentSaved": { "message": "Mühit URL-ləri saxlanıldı." @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Ana parolu dəyişdir" }, - "changeMasterPasswordConfirmation": { - "message": "Ana parolunuzu bitwarden.com veb anbarında dəyişdirə bilərsiniz. İndi saytı ziyarət etmək istəyirsiniz?" + "continueToWebApp": { + "message": "Veb tətbiqlə davam edilsin?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Ana parolunuzu Bitwarden veb tətbiqində dəyişdirə bilərsiniz." }, "fingerprintPhrase": { "message": "Barmaq izi ifadəsi", @@ -920,52 +923,52 @@ "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "enableFavicon": { - "message": "Veb sayt nişanlarını göstər" + "message": "Veb sayt ikonlarını göstər" }, "faviconDesc": { "message": "Hər girişin yanında tanına bilən bir təsvir göstər." }, "enableMinToTray": { - "message": "Bildiriş nişanına kiçildin" + "message": "Bildiriş sahəsi ikonuna kiçilt" }, "enableMinToTrayDesc": { - "message": "Pəncərə kiçildiləndə, bildiriş sahəsində bir nişan göstər." + "message": "Pəncərə kiçildiləndə, bunun əvəzinə bildiriş sahəsində bir ikon göstər." }, "enableMinToMenuBar": { - "message": "Menyu sətrinə kiçilt" + "message": "Menyu çubuğuna kiçilt" }, "enableMinToMenuBarDesc": { - "message": "Pəncərəni kiçildəndə, menyu sətrində bir nişan göstər." + "message": "Pəncərəni kiçildəndə, bunun əvəzinə menyu çubuğunda bir ikon göstər." }, "enableCloseToTray": { - "message": "Bildiriş nişanına bağla" + "message": "Bildiriş ikonu üçün bağla" }, "enableCloseToTrayDesc": { - "message": "Pəncərə bağlananda, bildiriş sahəsində bir nişan göstər." + "message": "Pəncərə bağladılanda, bunun əvəzinə bildiriş sahəsində bir ikon göstər." }, "enableCloseToMenuBar": { - "message": "Menyu sətrini bağla" + "message": "Menyu çubuğunda bağla" }, "enableCloseToMenuBarDesc": { - "message": "Pəncərəni bağlananda, menyu sətrində bir nişan göstər." + "message": "Pəncərəni bağladılanda, bunun əvəzinə menyu çubuğunda bir ikon göstər." }, "enableTray": { - "message": "Bildiriş sahəsi nişanını fəallaşdır" + "message": "Bildiriş sahəsi ikonunu göstər" }, "enableTrayDesc": { - "message": "Bildiriş sahəsində həmişə bir nişan göstər." + "message": "Bildiriş sahəsində həmişə bir ikon göstər." }, "startToTray": { - "message": "Bildiriş sahəsi nişanı kimi başlat" + "message": "Bildiriş sahəsi ikonu kimi başlat" }, "startToTrayDesc": { - "message": "Tətbiq ilk başladılanda, yalnız bildiriş sahəsi nişanı görünsün." + "message": "Tətbiq ilk başladılanda, sistem bildiriş sahəsində yalnız ikon olaraq görünsün." }, "startToMenuBar": { - "message": "Menyu sətrini başlat" + "message": "Menyu çubuğunda başlat" }, "startToMenuBarDesc": { - "message": "Tətbiq ilk başladılanda, menyu sətri sadəcə nişan kimi görünsün." + "message": "Tətbiq ilk başladılanda, menyu çubuğunda yalnız ikon olaraq görünsün." }, "openAtLogin": { "message": "Giriş ediləndə avtomatik başlat" @@ -977,7 +980,7 @@ "message": "\"Dock\"da həmişə göstər" }, "alwaysShowDockDesc": { - "message": "Menyu sətrinə kiçildiləndə belə Bitwarden nişanını \"Dock\"da göstər." + "message": "Menyu çubuğuna kiçildiləndə belə Bitwarden ikonunu Yuvada göstər." }, "confirmTrayTitle": { "message": "Bildiriş sahəsi nişanını ləğv et" @@ -1450,16 +1453,16 @@ "message": "Hesabınız bağlandı və bütün əlaqəli datalar silindi." }, "preferences": { - "message": "Tercihlər" + "message": "Tərcihlər" }, "enableMenuBar": { - "message": "Menyu sətri nişanını fəallaşdır" + "message": "Menyu çubuğu ikonunu göstər" }, "enableMenuBarDesc": { - "message": "Menyu sətrində həmişə bir nişan göstər." + "message": "Menyu çubuğunda həmişə bir ikon göstər." }, "hideToMenuBar": { - "message": "Menyu sətrini gizlət" + "message": "Menyu çubuğunda gizlət" }, "selectOneCollection": { "message": "Ən azı bir kolleksiya seçməlisiniz." @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Brauzer inteqrasiyası dəstəklənmir" }, + "browserIntegrationErrorTitle": { + "message": "Brauzer inteqrasiyasını fəallaşdırma xətası" + }, + "browserIntegrationErrorDesc": { + "message": "Brauzer inteqrasiyasını fəallaşdırarkən bir xəta baş verdi." + }, "browserIntegrationMasOnlyDesc": { "message": "Təəssüf ki, brauzer inteqrasiyası indilik yalnız Mac App Store versiyasında dəstəklənir." }, @@ -2021,7 +2030,7 @@ "message": "Eyni vaxtda 5-dən çox hesaba giriş edilə bilməz." }, "accountPreferences": { - "message": "Tercihlər" + "message": "Tərcihlər" }, "appPreferences": { "message": "Tətbiq ayarları (bütün hesablar)" @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Parol silindi" + }, + "errorAssigningTargetCollection": { + "message": "Hədəf kolleksiyaya təyin etmə xətası." + }, + "errorAssigningTargetFolder": { + "message": "Hədəf qovluğa təyin etmə xətası." } } diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 2bc33f2b28..53e3ec2d12 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Змяніць асноўны пароль" }, - "changeMasterPasswordConfirmation": { - "message": "Вы можаце змяніць свой асноўны пароль у вэб-сховішчы на bitwarden.com. Перайсці на вэб-сайт зараз?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Фраза адбітка пальца", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Інтэграцыя з браўзерам не падтрымліваецца" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "На жаль, інтэграцыя з браўзерам зараз падтрымліваецца толькі ў версіі для Mac App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 9f6d5bdd36..f4886c420f 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Промяна на главната парола" }, - "changeMasterPasswordConfirmation": { - "message": "Главната парола на трезор може да се промени чрез сайта bitwarden.com. Искате ли да го посетите?" + "continueToWebApp": { + "message": "Продължаване към уеб приложението?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Може да промените главната си парола в уеб приложението на Битуорден." }, "fingerprintPhrase": { "message": "Уникална фраза", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Интеграцията с браузър не се поддържа" }, + "browserIntegrationErrorTitle": { + "message": "Грешка при включването на интеграцията с браузъра" + }, + "browserIntegrationErrorDesc": { + "message": "Възникна грешка при включването на интеграцията с браузъра." + }, "browserIntegrationMasOnlyDesc": { "message": "За жалост в момента интеграцията с браузър не се поддържа във версията за магазина на Mac." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Секретният ключ е премахнат" + }, + "errorAssigningTargetCollection": { + "message": "Грешка при задаването на целева колекция." + }, + "errorAssigningTargetFolder": { + "message": "Грешка при задаването на целева папка." } } diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 22893aadab..abd2c1cfae 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "মূল পাসওয়ার্ড পরিবর্তন" }, - "changeMasterPasswordConfirmation": { - "message": "আপনি bitwarden.com ওয়েব ভল্ট থেকে মূল পাসওয়ার্ডটি পরিবর্তন করতে পারেন। আপনি কি এখনই ওয়েবসাইটটি দেখতে চান?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "ফিঙ্গারপ্রিন্ট ফ্রেজ", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 6adb5bbce3..825bd6344e 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Promijenite glavnu lozinku" }, - "changeMasterPasswordConfirmation": { - "message": "Možete da promjenite svoju glavnu lozinku na bitwarden.com web trezoru. Da li želite da posjetite web stranicu sada?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Jedinstvena fraza", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Nažalost, za sada je integracija sa preglednikom podržana samo u Mac App Store verziji aplikacije." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index c76111b53c..6c48d6cb0b 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Canvia la contrasenya mestra" }, - "changeMasterPasswordConfirmation": { - "message": "Podeu canviar la contrasenya mestra a la caixa forta web de bitwarden.com. Voleu visitar el lloc web ara?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Frase d'empremta digital", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "La integració en el navegador no és compatible" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Malauradament, la integració del navegador només és compatible amb la versió de Mac App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Clau de pas suprimida" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index e7ba56e81c..550b10a31c 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Změnit hlavní heslo" }, - "changeMasterPasswordConfirmation": { - "message": "Hlavní heslo si můžete změnit na webové stránce bitwarden.com. Chcete tuto stránku nyní otevřít?" + "continueToWebApp": { + "message": "Pokračovat do webové aplikace?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Hlavní heslo můžete změnit ve webové aplikaci Bitwardenu." }, "fingerprintPhrase": { "message": "Fráze otisku prstu", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Integrace prohlížeče není podporována" }, + "browserIntegrationErrorTitle": { + "message": "Chyba při povolování integrace prohlížeče" + }, + "browserIntegrationErrorDesc": { + "message": "Vyskytla se chyba při povolování integrace prohlížeče." + }, "browserIntegrationMasOnlyDesc": { "message": "Integrace prohlížeče je podporována jen ve verzi pro Mac App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Přístupový klíč byl odebrán" + }, + "errorAssigningTargetCollection": { + "message": "Chyba při přiřazování cílové kolekce." + }, + "errorAssigningTargetFolder": { + "message": "Chyba při přiřazování cílové složky." } } diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index e87b805d0b..b1cc9e63d3 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 1f994cf8eb..f2a84a3c29 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Skift hovedadgangskode" }, - "changeMasterPasswordConfirmation": { - "message": "Man kan ændre sin hovedadgangskode via bitwarden.com web-boksen. Besøg webstedet nu?" + "continueToWebApp": { + "message": "Fortsæt til web-app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Hovedadgangskoden kan ændres via Bitwarden web-appen." }, "fingerprintPhrase": { "message": "Fingeraftrykssætning", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browserintegration understøttes ikke" }, + "browserIntegrationErrorTitle": { + "message": "Fejl ved aktivering af webbrowserintegration" + }, + "browserIntegrationErrorDesc": { + "message": "En fejl opstod under aktivering af webbrowserintegration." + }, "browserIntegrationMasOnlyDesc": { "message": "Desværre understøttes browserintegration indtil videre kun i Mac App Store-versionen." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Adgangsnøgle fjernet" + }, + "errorAssigningTargetCollection": { + "message": "Fejl ved tildeling af målsamling." + }, + "errorAssigningTargetFolder": { + "message": "Fejl ved tildeling af målmappe." } } diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index a07ad93b15..e5e3945abc 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Master-Passwort ändern" }, - "changeMasterPasswordConfirmation": { - "message": "Du kannst dein Master-Passwort im bitwarden.com-Web-Tresor ändern. Möchtest du die Seite jetzt öffnen?" + "continueToWebApp": { + "message": "Weiter zur Web-App?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Du kannst dein Master-Passwort in der Bitwarden Web-App ändern." }, "fingerprintPhrase": { "message": "Fingerabdruck-Phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser-Integration wird nicht unterstützt" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Leider wird die Browser-Integration derzeit nur in der Mac App Store Version unterstützt." }, @@ -2698,9 +2707,15 @@ "message": "Hardwarebeschleunigung aktivieren und neu starten" }, "removePasskey": { - "message": "Passkey löschen" + "message": "Passkey entfernen" }, "passkeyRemoved": { - "message": "Passkey gelöscht" + "message": "Passkey entfernt" + }, + "errorAssigningTargetCollection": { + "message": "Fehler beim Zuweisen der Ziel-Sammlung." + }, + "errorAssigningTargetFolder": { + "message": "Fehler beim Zuweisen des Ziel-Ordners." } } diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 63b1f21c2e..41e6a62a2f 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Αλλαγή Κύριου Κωδικού" }, - "changeMasterPasswordConfirmation": { - "message": "Μπορείτε να αλλάξετε τον κύριο κωδικό στο bitwarden.com. Θέλετε να επισκεφθείτε την ιστοσελίδα τώρα;" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Φράση Δακτυλικών Αποτυπωμάτων", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Η ενσωμάτωση του περιηγητή δεν υποστηρίζεται" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Δυστυχώς η ενσωμάτωση του προγράμματος περιήγησης υποστηρίζεται μόνο στην έκδοση Mac App Store για τώρα." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 394a5951e9..ff9cbc97cc 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2688,6 +2697,9 @@ "message": "Common formats", "description": "Label indicating the most common import formats" }, + "success": { + "message": "Success" + }, "troubleshooting": { "message": "Troubleshooting" }, @@ -2702,5 +2714,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 53958bca57..2658610df3 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index f6011c301f..0542da9ddc 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 772eb70985..1c4cc4f0be 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index e3dcd0dc4c..ec5da44293 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Cambiar contraseña maestra" }, - "changeMasterPasswordConfirmation": { - "message": "Puedes cambiar tu contraseña maestra en la caja fuerte web de bitwarden.com. ¿Quieres visitar ahora el sitio web?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Frase de huella digital", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "La integración con el navegador no está soportada" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Por desgracia la integración del navegador sólo está soportada por ahora en la versión de la Mac App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 2b54df2a91..3850cc1d85 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Muuda ülemparooli" }, - "changeMasterPasswordConfirmation": { - "message": "Saad oma ülemparooli muuta bitwarden.com veebihoidlas. Soovid seda kohe teha?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Sõrmejälje fraas", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Brauseri integratsioon ei ole toetatud" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Paraku on brauseri integratsioon hetkel toetatud ainult Mac App Store'i versioonis." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index d66d5265e1..b21108b6ad 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Aldatu pasahitz nagusia" }, - "changeMasterPasswordConfirmation": { - "message": "Zure pasahitz nagusia alda dezakezu bitwarden.com webgunean. Orain joan nahi duzu webgunera?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Hatz-marka digitalaren esaldia", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Ez da nabigatzailearen integrazioa onartzen" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Zoritxarrez, Mac App Storeren bertsioan soilik onartzen da oraingoz nabigatzailearen integrazioa." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index c62bb99b2d..08356d410d 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "تغییر کلمه عبور اصلی" }, - "changeMasterPasswordConfirmation": { - "message": "شما می‌توانید کلمه عبور اصلی خود را در bitwarden.com تغییر دهید. آیا می‌خواهید از سایت بازدید کنید؟" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "عبارت اثر انگشت", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "ادغام مرورگر پشتیبانی نمی‌شود" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "متأسفانه در حال حاضر ادغام مرورگر فقط در نسخه Mac App Store پشتیبانی می‌شود." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 206588f3c3..fca24c197a 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Vaihda pääsalasana" }, - "changeMasterPasswordConfirmation": { - "message": "Voit vaihtaa pääsalasanasi bitwarden.com-verkkoholvissa. Haluatko käydä sivustolla nyt?" + "continueToWebApp": { + "message": "Avataanko verkkosovellus?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Voit vaihtaa pääsalasanasi Bitwardenin verkkosovelluksessa." }, "fingerprintPhrase": { "message": "Tunnistelauseke", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Selainintegraatiota ei tueta" }, + "browserIntegrationErrorTitle": { + "message": "Virhe otettaessa selainintegrointia käyttöön" + }, + "browserIntegrationErrorDesc": { + "message": "Otettaessa selainintegraatiota käyttöön tapahtui virhe." + }, "browserIntegrationMasOnlyDesc": { "message": "Valitettavasti selainintegraatiota tuetaan toistaiseksi vain Mac App Store -versiossa." }, @@ -2689,7 +2698,7 @@ "description": "Label indicating the most common import formats" }, "troubleshooting": { - "message": "Vianselvitys" + "message": "Vianetsintä" }, "disableHardwareAccelerationRestart": { "message": "Poista laitteistokiihdytys käytöstä ja käynnistä sovellus uudelleen" @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Suojausavain poistettiin" + }, + "errorAssigningTargetCollection": { + "message": "Virhe määritettäessä kohdekokoelmaa." + }, + "errorAssigningTargetFolder": { + "message": "Virhe määritettäessä kohdekansiota." } } diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 170559fc64..6d5f85fca8 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Palitan ang master password" }, - "changeMasterPasswordConfirmation": { - "message": "Maaari mong palitan ang iyong master password sa bitwarden.com web vault. Gusto mo bang bisitahin ang website ngayon?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Hulmabig ng Hilik ng Dako", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Hindi suportado ang pagsasama ng browser" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Sa kasamaang palad ang pagsasama ng browser ay suportado lamang sa bersyon ng Mac App Store para sa ngayon." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 20353d2d86..1097624b14 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Changer le mot de passe principal" }, - "changeMasterPasswordConfirmation": { - "message": "Vous pouvez changer votre mot de passe principal depuis le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" + "continueToWebApp": { + "message": "Poursuivre vers l'application web ?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Vous pouvez modifier votre mot de passe principal sur l'application web de Bitwarden." }, "fingerprintPhrase": { "message": "Phrase d'empreinte", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Intégration dans le navigateur non supportée" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Malheureusement l'intégration avec le navigateur est uniquement supportée dans la version Mac App Store pour le moment." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Clé d'identification (passkey) retirée" + }, + "errorAssigningTargetCollection": { + "message": "Erreur lors de l'assignation de la collection cible." + }, + "errorAssigningTargetFolder": { + "message": "Erreur lors de l'assignation du dossier cible." } } diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index f96260c005..90648699c0 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index cc5a0f011d..73599c012e 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "החלף סיסמה ראשית" }, - "changeMasterPasswordConfirmation": { - "message": "באפשרותך לשנות את הסיסמה הראשית שלך דרך הכספת באתר bitwarden.com. האם ברצונך לפתוח את האתר כעת?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "סיסמת טביעת אצבע", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "שילוב הדפדפן אינו נתמך" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "למצער, אינטגרצייה עם הדפדפן בשלב זה נתמכת רק על ידי גרסת חנות האפליקציות של מקינטוש." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 57ef1d32ef..4f8bb9b4bb 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 1e501cee78..220c8bfab2 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Promjeni glavnu lozinku" }, - "changeMasterPasswordConfirmation": { - "message": "Svoju glavnu lozinku možeš promijeniti na web trezoru. Želiš li sada posjetiti bitwarden.com?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Jedinstvena fraza", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Integracija preglednika nije podržana" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Nažalost, za sada je integracija s preglednikom podržana samo u Mac App Store verziji aplikacije." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 0b443c9a6b..149d48284e 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Mesterjelszó módosítása" }, - "changeMasterPasswordConfirmation": { - "message": "A mesterjelszó megváltoztatható a bitwarden.com webes széfben. Szeretnénk felkeresni a webhelyet mos?" + "continueToWebApp": { + "message": "Tovább a webes alkalmazáshoz?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "A mesterjelszó a Bitwarden webalkalmazásban módosítható." }, "fingerprintPhrase": { "message": "Azonosítókifejezés", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "A böngésző integráció nem támogatott." }, + "browserIntegrationErrorTitle": { + "message": "Böngésző integráció engedélyezése" + }, + "browserIntegrationErrorDesc": { + "message": "Hiba történt a böngésző integrációjának engedélyezése közben." + }, "browserIntegrationMasOnlyDesc": { "message": "Sajnos a böngésző integrációt egyelőre csak a Mac App Store verzió támogatja." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Hiba történt a célgyűjtemény hozzárendelése során." + }, + "errorAssigningTargetFolder": { + "message": "Hiba történt a célmappa hozzárendelése során." } } diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 008b6b369a..3194b0f7d3 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -404,7 +404,7 @@ "message": "Panjang" }, "passwordMinLength": { - "message": "Minimum password length" + "message": "Panjang kata sandi minimum" }, "uppercase": { "message": "Huruf Kapital (A-Z)" @@ -545,7 +545,7 @@ "message": "Diperlukan pengetikan ulang kata sandi utama." }, "masterPasswordMinlength": { - "message": "Master password must be at least $VALUE$ characters long.", + "message": "Kata sandi utama minimal harus $VALUE$ karakter.", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -561,7 +561,7 @@ "message": "Akun baru Anda telah dibuat! Sekarang Anda bisa masuk." }, "youSuccessfullyLoggedIn": { - "message": "You successfully logged in" + "message": "Anda berhasil masuk" }, "youMayCloseThisWindow": { "message": "You may close this window" @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Ubah Kata Sandi Utama" }, - "changeMasterPasswordConfirmation": { - "message": "Anda dapat mengubah kata sandi utama Anda di brankas web bitwarden.com. Anda ingin mengunjungi situs web sekarang?" + "continueToWebApp": { + "message": "Lanjutkan ke aplikasi web?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Anda bisa mengganti kata sandi utama Anda di aplikasi web Bitwarden." }, "fingerprintPhrase": { "message": "Frase Fingerprint", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Integrasi browser tidak didukung" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Sayangnya integrasi browser hanya didukung di versi Mac App Store untuk saat ini." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index a3a6f771fe..08ae2d9da8 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Cambia password principale" }, - "changeMasterPasswordConfirmation": { - "message": "Puoi cambiare la tua password principale sulla cassaforte online di bitwarden.com. Vuoi visitare ora il sito?" + "continueToWebApp": { + "message": "Passa al sito web?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Puoi modificare la tua password principale sul sito web di Bitwarden." }, "fingerprintPhrase": { "message": "Frase impronta", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "L'integrazione del browser non è supportata" }, + "browserIntegrationErrorTitle": { + "message": "Errore durante l'attivazione dell'integrazione del browser" + }, + "browserIntegrationErrorDesc": { + "message": "Si è verificato un errore durante l'attivazione dell'integrazione del browser." + }, "browserIntegrationMasOnlyDesc": { "message": "Purtroppo l'integrazione del browser è supportata solo nella versione nell'App Store per ora." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey rimossa" + }, + "errorAssigningTargetCollection": { + "message": "Errore nell'assegnazione della raccolta di destinazione." + }, + "errorAssigningTargetFolder": { + "message": "Errore nell'assegnazione della cartella di destinazione." } } diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index f0a95d4e35..b07eab20cc 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "マスターパスワードの変更" }, - "changeMasterPasswordConfirmation": { - "message": "マスターパスワードは bitwarden.com ウェブ保管庫で変更できます。ウェブサイトを開きますか?" + "continueToWebApp": { + "message": "ウェブアプリに進みますか?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Bitwarden ウェブアプリでマスターパスワードを変更できます。" }, "fingerprintPhrase": { "message": "パスフレーズ", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "ブラウザー統合はサポートされていません" }, + "browserIntegrationErrorTitle": { + "message": "ブラウザー連携を有効にする際にエラーが発生しました" + }, + "browserIntegrationErrorDesc": { + "message": "ブラウザー統合の有効化中にエラーが発生しました。" + }, "browserIntegrationMasOnlyDesc": { "message": "残念ながら、ブラウザ統合は、Mac App Storeのバージョンでのみサポートされています。" }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "パスキーを削除しました" + }, + "errorAssigningTargetCollection": { + "message": "ターゲットコレクションの割り当てに失敗しました。" + }, + "errorAssigningTargetFolder": { + "message": "ターゲットフォルダーの割り当てに失敗しました。" } } diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index f96260c005..90648699c0 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index f96260c005..90648699c0 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index 281d64cbc2..162cba3a75 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "ಮಾಸ್ಟರ್ ಪಾಸ್ವರ್ಡ್ ಬದಲಾಯಿಸಿ" }, - "changeMasterPasswordConfirmation": { - "message": "ನಿಮ್ಮ ಮಾಸ್ಟರ್ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ನೀವು bitwarden.com ವೆಬ್ ವಾಲ್ಟ್‌ನಲ್ಲಿ ಬದಲಾಯಿಸಬಹುದು. ನೀವು ಈಗ ವೆಬ್‌ಸೈಟ್‌ಗೆ ಭೇಟಿ ನೀಡಲು ಬಯಸುವಿರಾ?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಫ್ರೇಸ್", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "ದುರದೃಷ್ಟವಶಾತ್ ಬ್ರೌಸರ್ ಏಕೀಕರಣವನ್ನು ಇದೀಗ ಮ್ಯಾಕ್ ಆಪ್ ಸ್ಟೋರ್ ಆವೃತ್ತಿಯಲ್ಲಿ ಮಾತ್ರ ಬೆಂಬಲಿಸಲಾಗುತ್ತದೆ." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index a09d53b1dd..09b1767af0 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "마스터 비밀번호 변경" }, - "changeMasterPasswordConfirmation": { - "message": "bitwarden.com 웹 보관함에서 마스터 비밀번호를 바꿀 수 있습니다. 지금 웹 사이트를 방문하시겠습니까?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "지문 구절", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "브라우저와 연결이 지원되지 않음" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "브라우저와 연결은 현재 Mac App Store 버전에서만 지원됩니다." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index dbc2e13d1c..de77aa8fbf 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Keisti pagrindinį slaptažodį" }, - "changeMasterPasswordConfirmation": { - "message": "Pagrindinį slaptažodį galite pakeisti bitwarden.com žiniatinklio saugykloje. Ar norite dabar apsilankyti svetainėje?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Piršto antspaudo frazė", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Naršyklės integravimas nepalaikomas" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Deja, bet naršyklės integravimas kol kas palaikomas tik Mac App Store versijoje." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 0a3501dded..521a0afcf2 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Mainīt galveno paroli" }, - "changeMasterPasswordConfirmation": { - "message": "Galveno paroli ir iespējams mainīt bitwarden.com tīmekļa glabātavā. Vai tagad apmeklēt tīmekļvietni?" + "continueToWebApp": { + "message": "Pāriet uz tīmekļa lietotni?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Savu galveno paroli var mainīt Bitwarden tīmekļa lietotnē." }, "fingerprintPhrase": { "message": "Atpazīšanas vārdkopa", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Sasaistīšana ar pārlūku nav atbalstīta" }, + "browserIntegrationErrorTitle": { + "message": "Kļūda pārlūga saistīšanas iespējošanā" + }, + "browserIntegrationErrorDesc": { + "message": "Atgadījās kļūda pārlūka saistīšanas iespējošanas laikā." + }, "browserIntegrationMasOnlyDesc": { "message": "Diemžēl sasaistīšāna ar pārlūku pagaidām ir nodrošināta tikai Mac App Store laidienā." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Piekļuves atslēga noņemta" + }, + "errorAssigningTargetCollection": { + "message": "Kļūda mērķa krājuma piešķiršanā." + }, + "errorAssigningTargetFolder": { + "message": "Kļūda mērķa mapes piešķiršanā." } } diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index d5e3bddf8e..ed458379b8 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Promjena glavne lozinke" }, - "changeMasterPasswordConfirmation": { - "message": "Možete promijeniti svoju glavnu lozinku u trezoru na internet strani bitwarden.com. Da li želite da posjetite internet lokaciju sada?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fraza računa", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 6b1137d232..b94b9d1b79 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "പ്രാഥമിക പാസ്‌വേഡ് മാറ്റുക" }, - "changeMasterPasswordConfirmation": { - "message": "തങ്ങൾക്കു Bitwarden വെബ് വാൾട്ടിൽ പ്രാഥമിക പാസ്‌വേഡ് മാറ്റാൻ സാധിക്കും.വെബ്സൈറ്റ് ഇപ്പോൾ സന്ദർശിക്കാൻ ആഗ്രഹിക്കുന്നുവോ?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "ഫിംഗർപ്രിന്റ് ഫ്രേസ്‌", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index f96260c005..90648699c0 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 2626e93c24..5142c8e61f 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 8e8e2e2cbb..e190cfc236 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Endre hovedpassordet" }, - "changeMasterPasswordConfirmation": { - "message": "Du kan endre superpassordet ditt på bitwarden.net-netthvelvet. Vil du besøke det nettstedet nå?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingeravtrykksfrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Nettleserintegrasjon støttes ikke" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Nettleserintegrasjon støttes dessverre bare i Mac App Store-versjonen for øyeblikket." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index e7d586023e..bd58a18b0d 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 9c4ba78036..3ca0730710 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Hoofdwachtwoord wijzigen" }, - "changeMasterPasswordConfirmation": { - "message": "Je kunt je hoofdwachtwoord wijzigen in de kluis op bitwarden.com. Wil je de website nu bezoeken?" + "continueToWebApp": { + "message": "Doorgaan naar web-app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Je kunt je hoofdwachtwoord wijzigen in de Bitwarden-webapp." }, "fingerprintPhrase": { "message": "Vingerafdrukzin", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browserintegratie niet ondersteund" }, + "browserIntegrationErrorTitle": { + "message": "Fout bij inschakelen van de browserintegratie" + }, + "browserIntegrationErrorDesc": { + "message": "Er is iets misgegaan bij het tijdens het inschakelen van de browserintegratie." + }, "browserIntegrationMasOnlyDesc": { "message": "Helaas wordt browserintegratie momenteel alleen ondersteund in de Mac App Store-versie." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey verwijderd" + }, + "errorAssigningTargetCollection": { + "message": "Fout bij toewijzen doelverzameling." + }, + "errorAssigningTargetFolder": { + "message": "Fout bij toewijzen doelmap." } } diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index ea55378f5b..12e11b32c1 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Endre hovudpassord" }, - "changeMasterPasswordConfirmation": { - "message": "Du kan endre hovudpassordet ditt i Bitwarden sin nettkvelv. Vil du gå til nettstaden no?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index c6c0c2fb0c..7363558551 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index a0626a6c90..df7a158a3a 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Zmień hasło główne" }, - "changeMasterPasswordConfirmation": { - "message": "Hasło główne możesz zmienić na stronie sejfu bitwarden.com. Czy chcesz przejść do tej strony?" + "continueToWebApp": { + "message": "Kontynuować do aplikacji internetowej?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Możesz zmienić swoje hasło główne w aplikacji internetowej Bitwarden." }, "fingerprintPhrase": { "message": "Unikalny identyfikator konta", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Połączenie z przeglądarką nie jest obsługiwane" }, + "browserIntegrationErrorTitle": { + "message": "Błąd podczas włączania integracji z przeglądarką" + }, + "browserIntegrationErrorDesc": { + "message": "Wystąpił błąd podczas włączania integracji z przeglądarką." + }, "browserIntegrationMasOnlyDesc": { "message": "Połączenie z przeglądarką jest obsługiwane tylko z wersją aplikacji ze sklepu Mac App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey został usunięty" + }, + "errorAssigningTargetCollection": { + "message": "Wystąpił błąd podczas przypisywania kolekcji." + }, + "errorAssigningTargetFolder": { + "message": "Wystąpił błąd podczas przypisywania folderu." } } diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index c8f8316e6d..f651e0b060 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Alterar Senha Mestra" }, - "changeMasterPasswordConfirmation": { - "message": "Você pode alterar a sua senha mestra no cofre web em bitwarden.com. Você deseja visitar o site agora?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Frase biométrica", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Integração com o navegador não suportado" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Infelizmente, por ora, a integração do navegador só é suportada na versão da Mac App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 5fbd7636d1..4e58dad2cf 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Alterar palavra-passe mestra" }, - "changeMasterPasswordConfirmation": { - "message": "Pode alterar o seu endereço de e-mail no cofre do site bitwarden.com. Deseja visitar o site agora?" + "continueToWebApp": { + "message": "Continuar para a aplicação Web?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Pode alterar a sua palavra-passe mestra na aplicação Web Bitwarden." }, "fingerprintPhrase": { "message": "Frase de impressão digital", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Integração com o navegador não suportada" }, + "browserIntegrationErrorTitle": { + "message": "Erro ao ativar a integração do navegador" + }, + "browserIntegrationErrorDesc": { + "message": "Ocorreu um erro ao ativar a integração do navegador." + }, "browserIntegrationMasOnlyDesc": { "message": "Infelizmente, a integração do navegador só é suportada na versão da Mac App Store por enquanto." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Chave de acesso removida" + }, + "errorAssigningTargetCollection": { + "message": "Erro ao atribuir a coleção de destino." + }, + "errorAssigningTargetFolder": { + "message": "Erro ao atribuir a pasta de destino." } } diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 7af8f7ec16..3fe73f28a7 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Schimbare parolă principală" }, - "changeMasterPasswordConfirmation": { - "message": "Puteți modifica parola principală pe saitul web bitwarden.com. Doriți să vizitați saitul acum?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Frază amprentă", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Integrarea browserului nu este acceptată" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Din păcate, integrarea browserului este acceptată numai în versiunea Mac App Store pentru moment." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 0e28c2cf90..cc182812a6 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Изменить мастер-пароль" }, - "changeMasterPasswordConfirmation": { - "message": "Вы можете изменить свой мастер-пароль на bitwarden.com. Перейти на сайт сейчас?" + "continueToWebApp": { + "message": "Перейти к веб-приложению?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Изменить мастер-пароль можно в веб-приложении Bitwarden." }, "fingerprintPhrase": { "message": "Фраза отпечатка", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Интеграция с браузером не поддерживается" }, + "browserIntegrationErrorTitle": { + "message": "Ошибка при включении интеграции с браузером" + }, + "browserIntegrationErrorDesc": { + "message": "Произошла ошибка при включении интеграции с браузером." + }, "browserIntegrationMasOnlyDesc": { "message": "К сожалению, интеграция браузера пока поддерживается только в версии Mac App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey удален" + }, + "errorAssigningTargetCollection": { + "message": "Ошибка при назначении целевой коллекции." + }, + "errorAssigningTargetFolder": { + "message": "Ошибка при назначении целевой папки." } } diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index 60e8aea93c..261ae1c9b8 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 240b883254..6ef52a83ee 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -445,7 +445,7 @@ "message": "Vyhnúť sa zameniteľným znakom" }, "searchCollection": { - "message": "Search collection" + "message": "Vyhľadať zbierku" }, "searchFolder": { "message": "Search folder" @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Zmeniť hlavné heslo" }, - "changeMasterPasswordConfirmation": { - "message": "Svoje hlavné heslo môžete zmeniť vo webovom trezore bitwarden.com. Chcete teraz navštíviť túto stránku?" + "continueToWebApp": { + "message": "Pokračovať vo webovej aplikácii?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Hlavné heslo si môžete zmeniť vo webovej aplikácii Bitwarden." }, "fingerprintPhrase": { "message": "Fráza odtlačku", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Integrácia v prehliadači nie je podporovaná" }, + "browserIntegrationErrorTitle": { + "message": "Chyba pri povoľovaní integrácie v prehliadači" + }, + "browserIntegrationErrorDesc": { + "message": "Pri povoľovaní integrácie v prehliadači sa vyskytla chyba." + }, "browserIntegrationMasOnlyDesc": { "message": "Bohužiaľ, integrácia v prehliadači je zatiaľ podporovaná iba vo verzii Mac App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Prístupový kľúč bol odstránený" + }, + "errorAssigningTargetCollection": { + "message": "Chyba pri priraďovaní cieľovej kolekcie." + }, + "errorAssigningTargetFolder": { + "message": "Chyba pri priraďovaní cieľového priečinka." } } diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 528274cf29..8c9c158c87 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Spremeni glavno geslo" }, - "changeMasterPasswordConfirmation": { - "message": "Svoje glavno geslo lahko spremenite v bitwarden.com spletnem trezorju. Želite obiskati spletno stran zdaj?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Identifikacijsko geslo", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 77b5f7221d..edafdb55a2 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Промени главну лозинку" }, - "changeMasterPasswordConfirmation": { - "message": "Можете променити главну лозинку у Вашем сефу на bitwarden.com. Да ли желите да посетите веб страницу сада?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Сигурносна Фраза Сефа", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Интеграција са претраживачем није подржана" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Нажалост, интеграција прегледача за сада је подржана само у верзији Mac App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Приступачни кључ је уклоњен" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 4c41cee471..6342424fd4 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Ändra huvudlösenord" }, - "changeMasterPasswordConfirmation": { - "message": "Du kan ändra ditt huvudlösenord i Bitwardens webbvalv. Vill du besöka webbplatsen nu?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingeravtrycksfras", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Webbläsarintegration stöds inte" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Tyvärr stöds webbläsarintegration för tillfället endast i versionen från Mac App Store." }, @@ -2689,7 +2698,7 @@ "description": "Label indicating the most common import formats" }, "troubleshooting": { - "message": "Troubleshooting" + "message": "Felsökning" }, "disableHardwareAccelerationRestart": { "message": "Disable hardware acceleration and restart" @@ -2698,9 +2707,15 @@ "message": "Enable hardware acceleration and restart" }, "removePasskey": { - "message": "Remove passkey" + "message": "Ta bort nyckel" }, "passkeyRemoved": { - "message": "Passkey removed" + "message": "Nyckel borttagen" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index f96260c005..90648699c0 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Change master password" }, - "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index efbfc86b33..5b6a1b9d0b 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "เปลี่ยนรหัสผ่านหลัก" }, - "changeMasterPasswordConfirmation": { - "message": "คุณสามารถเปลี่ยนรหัสผ่านหลักของคุณได้ที่เว็บ bitwarden.com คุณต้องการไปที่เว็บไซต์ตอนนี้ไหม?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint Phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Browser integration not supported" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 36d62ed2a5..42a8f207c7 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Ana parolayı değiştir" }, - "changeMasterPasswordConfirmation": { - "message": "Ana parolanızı bitwarden.com web kasası üzerinden değiştirebilirsiniz. Siteyi şimdi ziyaret etmek ister misiniz?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Parmak izi ifadesi", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Tarayıcı entegrasyonu desteklenmiyor" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Ne yazık ki tarayıcı entegrasyonu şu anda sadece Mac App Store sürümünde destekleniyor." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index 377fd23b0b..ac7b7c3243 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Змінити головний пароль" }, - "changeMasterPasswordConfirmation": { - "message": "Ви можете змінити головний пароль в сховищі на bitwarden.com. Хочете перейти на вебсайт зараз?" + "continueToWebApp": { + "message": "Продовжити у вебпрограмі?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "Ви можете змінити головний пароль у вебпрограмі Bitwarden." }, "fingerprintPhrase": { "message": "Фраза відбитка", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Інтеграція з браузером не підтримується" }, + "browserIntegrationErrorTitle": { + "message": "Помилка увімкнення інтеграції з браузером" + }, + "browserIntegrationErrorDesc": { + "message": "Під час увімкнення інтеграції з браузером сталася помилка." + }, "browserIntegrationMasOnlyDesc": { "message": "На жаль, зараз інтеграція з браузером підтримується лише у версії для Mac з App Store." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Ключ доступу вилучено" + }, + "errorAssigningTargetCollection": { + "message": "Помилка призначення цільової збірки." + }, + "errorAssigningTargetFolder": { + "message": "Помилка призначення цільової теки." } } diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 138773d40a..aac9995db1 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "Thay đổi mật khẩu chính" }, - "changeMasterPasswordConfirmation": { - "message": "Bạn có thể thay đổi mật khẩu chính trong kho bitwarden nền web. Bạn có muốn truy cập trang web bây giờ?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "Fingerprint Phrase", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "Không hỗ trợ tích hợp trình duyệt" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "Rất tiếc, tính năng tích hợp trình duyệt hiện chỉ được hỗ trợ trong phiên bản App Store trên Mac." }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "Passkey removed" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 8725fa0f21..0560466cf8 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "修改主密码" }, - "changeMasterPasswordConfirmation": { - "message": "您可以在 bitwarden.com 网页密码库修改您的主密码。现在要访问吗?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "指纹短语", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "不支持浏览器集成" }, + "browserIntegrationErrorTitle": { + "message": "启用浏览器集成时出错" + }, + "browserIntegrationErrorDesc": { + "message": "启用浏览器集成时出错。" + }, "browserIntegrationMasOnlyDesc": { "message": "很遗憾,目前仅 Mac App Store 版本支持浏览器集成。" }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "通行密钥已移除" + }, + "errorAssigningTargetCollection": { + "message": "分配目标集合时出错。" + }, + "errorAssigningTargetFolder": { + "message": "分配目标文件夹时出错。" } } diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index c93f236976..9eb12e23cf 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -800,8 +800,11 @@ "changeMasterPass": { "message": "變更主密碼" }, - "changeMasterPasswordConfirmation": { - "message": "您可以在 bitwarden.com 網頁版密碼庫變更主密碼。現在要前往嗎?" + "continueToWebApp": { + "message": "Continue to web app?" + }, + "changeMasterPasswordOnWebConfirmation": { + "message": "You can change your master password on the Bitwarden web app." }, "fingerprintPhrase": { "message": "指紋短語", @@ -1629,6 +1632,12 @@ "browserIntegrationUnsupportedTitle": { "message": "不支援瀏覽器整合" }, + "browserIntegrationErrorTitle": { + "message": "Error enabling browser integration" + }, + "browserIntegrationErrorDesc": { + "message": "An error has occurred while enabling browser integration." + }, "browserIntegrationMasOnlyDesc": { "message": "很遺憾,目前僅 Mac App Store 版本支援瀏覽器整合功能。" }, @@ -2702,5 +2711,11 @@ }, "passkeyRemoved": { "message": "金鑰已移除" + }, + "errorAssigningTargetCollection": { + "message": "Error assigning target collection." + }, + "errorAssigningTargetFolder": { + "message": "Error assigning target folder." } } diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 67f08839c5..bffd2002ff 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -1,7 +1,7 @@ import * as path from "path"; import { app } from "electron"; -import { firstValueFrom } from "rxjs"; +import { Subject, firstValueFrom } from "rxjs"; import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service"; import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; @@ -11,6 +11,9 @@ import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwar import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { DefaultBiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; +import { Message, MessageSender } from "@bitwarden/common/platform/messaging"; +// eslint-disable-next-line no-restricted-imports -- For dependency creation +import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service"; @@ -18,7 +21,6 @@ import { KeyGenerationService } from "@bitwarden/common/platform/services/key-ge import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; -import { NoopMessagingService } from "@bitwarden/common/platform/services/noop-messaging.service"; /* eslint-disable import/no-restricted-paths -- We need the implementation to inject, but generally this should not be accessed */ import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider"; @@ -59,7 +61,7 @@ export class Main { storageService: ElectronStorageService; memoryStorageService: MemoryStorageService; memoryStorageForStateProviders: MemoryStorageServiceForStateProviders; - messagingService: ElectronMainMessagingService; + messagingService: MessageSender; stateService: StateService; environmentService: DefaultEnvironmentService; mainCryptoFunctionService: MainCryptoFunctionService; @@ -131,7 +133,7 @@ export class Main { this.i18nService = new I18nMainService("en", "./locales/", globalStateProvider); const accountService = new AccountServiceImplementation( - new NoopMessagingService(), + MessageSender.EMPTY, this.logService, globalStateProvider, ); @@ -155,7 +157,7 @@ export class Main { activeUserStateProvider, singleUserStateProvider, globalStateProvider, - new DefaultDerivedStateProvider(this.memoryStorageForStateProviders), + new DefaultDerivedStateProvider(storageServiceProvider), ); this.environmentService = new DefaultEnvironmentService(stateProvider, accountService); @@ -223,7 +225,13 @@ export class Main { this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain); this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.desktopSettingsService); - this.messagingService = new ElectronMainMessagingService(this.windowMain, (message) => { + const messageSubject = new Subject>(); + this.messagingService = MessageSender.combine( + new SubjectMessageSender(messageSubject), // For local messages + new ElectronMainMessagingService(this.windowMain), + ); + + messageSubject.asObservable().subscribe((message) => { this.messagingMain.onMessage(message); }); @@ -291,12 +299,20 @@ export class Main { this.powerMonitorMain.init(); await this.updaterMain.init(); - if ( - (await this.stateService.getEnableBrowserIntegration()) || - (await firstValueFrom( - this.desktopAutofillSettingsService.enableDuckDuckGoBrowserIntegration$, - )) - ) { + const [browserIntegrationEnabled, ddgIntegrationEnabled] = await Promise.all([ + this.stateService.getEnableBrowserIntegration(), + firstValueFrom(this.desktopAutofillSettingsService.enableDuckDuckGoBrowserIntegration$), + ]); + + if (browserIntegrationEnabled || ddgIntegrationEnabled) { + // Re-register the native messaging host integrations on startup, in case they are not present + if (browserIntegrationEnabled) { + this.nativeMessagingMain.generateManifests().catch(this.logService.error); + } + if (ddgIntegrationEnabled) { + this.nativeMessagingMain.generateDdgManifests().catch(this.logService.error); + } + this.nativeMessagingMain.listen(); } diff --git a/apps/desktop/src/main/menu/menu.account.ts b/apps/desktop/src/main/menu/menu.account.ts index 142431ae0d..f3c9b08531 100644 --- a/apps/desktop/src/main/menu/menu.account.ts +++ b/apps/desktop/src/main/menu/menu.account.ts @@ -65,10 +65,10 @@ export class AccountMenu implements IMenubarMenu { id: "changeMasterPass", click: async () => { const result = await dialog.showMessageBox(this._window, { - title: this.localize("changeMasterPass"), - message: this.localize("changeMasterPass"), - detail: this.localize("changeMasterPasswordConfirmation"), - buttons: [this.localize("yes"), this.localize("no")], + title: this.localize("continueToWebApp"), + message: this.localize("continueToWebApp"), + detail: this.localize("changeMasterPasswordOnWebConfirmation"), + buttons: [this.localize("continue"), this.localize("cancel")], cancelId: 1, defaultId: 0, noLink: true, diff --git a/apps/desktop/src/main/messaging.main.ts b/apps/desktop/src/main/messaging.main.ts index 256d551560..a9f80b7d20 100644 --- a/apps/desktop/src/main/messaging.main.ts +++ b/apps/desktop/src/main/messaging.main.ts @@ -75,22 +75,6 @@ export class MessagingMain { case "getWindowIsFocused": this.windowIsFocused(); break; - case "enableBrowserIntegration": - this.main.nativeMessagingMain.generateManifests(); - this.main.nativeMessagingMain.listen(); - break; - case "enableDuckDuckGoBrowserIntegration": - this.main.nativeMessagingMain.generateDdgManifests(); - this.main.nativeMessagingMain.listen(); - break; - case "disableBrowserIntegration": - this.main.nativeMessagingMain.removeManifests(); - this.main.nativeMessagingMain.stop(); - break; - case "disableDuckDuckGoBrowserIntegration": - this.main.nativeMessagingMain.removeDdgManifests(); - this.main.nativeMessagingMain.stop(); - break; default: break; } diff --git a/apps/desktop/src/main/native-messaging.main.ts b/apps/desktop/src/main/native-messaging.main.ts index 05e987e20b..d3dd25c644 100644 --- a/apps/desktop/src/main/native-messaging.main.ts +++ b/apps/desktop/src/main/native-messaging.main.ts @@ -22,7 +22,55 @@ export class NativeMessagingMain { private windowMain: WindowMain, private userPath: string, private exePath: string, - ) {} + ) { + ipcMain.handle( + "nativeMessaging.manifests", + async (_event: any, options: { create: boolean }) => { + if (options.create) { + this.listen(); + try { + await this.generateManifests(); + } catch (e) { + this.logService.error("Error generating manifests: " + e); + return e; + } + } else { + this.stop(); + try { + await this.removeManifests(); + } catch (e) { + this.logService.error("Error removing manifests: " + e); + return e; + } + } + return null; + }, + ); + + ipcMain.handle( + "nativeMessaging.ddgManifests", + async (_event: any, options: { create: boolean }) => { + if (options.create) { + this.listen(); + try { + await this.generateDdgManifests(); + } catch (e) { + this.logService.error("Error generating duckduckgo manifests: " + e); + return e; + } + } else { + this.stop(); + try { + await this.removeDdgManifests(); + } catch (e) { + this.logService.error("Error removing duckduckgo manifests: " + e); + return e; + } + } + return null; + }, + ); + } listen() { ipc.config.id = "bitwarden"; @@ -76,7 +124,7 @@ export class NativeMessagingMain { ipc.server.emit(socket, "message", message); } - generateManifests() { + async generateManifests() { const baseJson = { name: "com.8bit.bitwarden", description: "Bitwarden desktop <-> browser bridge", @@ -84,6 +132,10 @@ export class NativeMessagingMain { type: "stdio", }; + if (!existsSync(baseJson.path)) { + throw new Error(`Unable to find binary: ${baseJson.path}`); + } + const firefoxJson = { ...baseJson, ...{ allowed_extensions: ["{446900e4-71c2-419f-a6a7-df9c091e268b}"] }, @@ -92,8 +144,11 @@ export class NativeMessagingMain { ...baseJson, ...{ allowed_origins: [ + // Chrome extension "chrome-extension://nngceckbapebfimnlniiiahkandclblb/", + // Edge extension "chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/", + // Opera extension "chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/", ], }, @@ -102,27 +157,17 @@ export class NativeMessagingMain { switch (process.platform) { case "win32": { const destination = path.join(this.userPath, "browsers"); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.writeManifest(path.join(destination, "firefox.json"), firefoxJson); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.writeManifest(path.join(destination, "chrome.json"), chromeJson); + await this.writeManifest(path.join(destination, "firefox.json"), firefoxJson); + await this.writeManifest(path.join(destination, "chrome.json"), chromeJson); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.createWindowsRegistry( - "HKLM\\SOFTWARE\\Mozilla\\Firefox", - "HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden", - path.join(destination, "firefox.json"), - ); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.createWindowsRegistry( - "HKCU\\SOFTWARE\\Google\\Chrome", - "HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden", - path.join(destination, "chrome.json"), - ); + const nmhs = this.getWindowsNMHS(); + for (const [key, value] of Object.entries(nmhs)) { + let manifestPath = path.join(destination, "chrome.json"); + if (key === "Firefox") { + manifestPath = path.join(destination, "firefox.json"); + } + await this.createWindowsRegistry(value, manifestPath); + } break; } case "darwin": { @@ -136,38 +181,30 @@ export class NativeMessagingMain { manifest = firefoxJson; } - this.writeManifest(p, manifest).catch((e) => - this.logService.error(`Error writing manifest for ${key}. ${e}`), - ); + await this.writeManifest(p, manifest); } else { - this.logService.warning(`${key} not found skipping.`); + this.logService.warning(`${key} not found, skipping.`); } } break; } case "linux": if (existsSync(`${this.homedir()}/.mozilla/`)) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.writeManifest( + await this.writeManifest( `${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`, firefoxJson, ); } if (existsSync(`${this.homedir()}/.config/google-chrome/`)) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.writeManifest( + await this.writeManifest( `${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`, chromeJson, ); } if (existsSync(`${this.homedir()}/.config/microsoft-edge/`)) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.writeManifest( + await this.writeManifest( `${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`, chromeJson, ); @@ -178,20 +215,23 @@ export class NativeMessagingMain { } } - generateDdgManifests() { + async generateDdgManifests() { const manifest = { name: "com.8bit.bitwarden", description: "Bitwarden desktop <-> DuckDuckGo bridge", path: this.binaryPath(), type: "stdio", }; + + if (!existsSync(manifest.path)) { + throw new Error(`Unable to find binary: ${manifest.path}`); + } + switch (process.platform) { case "darwin": { /* eslint-disable-next-line no-useless-escape */ const path = `${this.homedir()}/Library/Containers/com.duckduckgo.macos.browser/Data/Library/Application\ Support/NativeMessagingHosts/com.8bit.bitwarden.json`; - this.writeManifest(path, manifest).catch((e) => - this.logService.error(`Error writing manifest for DuckDuckGo. ${e}`), - ); + await this.writeManifest(path, manifest); break; } default: @@ -199,86 +239,50 @@ export class NativeMessagingMain { } } - removeManifests() { + async removeManifests() { switch (process.platform) { - case "win32": - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - fs.unlink(path.join(this.userPath, "browsers", "firefox.json")); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - fs.unlink(path.join(this.userPath, "browsers", "chrome.json")); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.deleteWindowsRegistry( - "HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden", - ); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.deleteWindowsRegistry( - "HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden", - ); + case "win32": { + await this.removeIfExists(path.join(this.userPath, "browsers", "firefox.json")); + await this.removeIfExists(path.join(this.userPath, "browsers", "chrome.json")); + + const nmhs = this.getWindowsNMHS(); + for (const [, value] of Object.entries(nmhs)) { + await this.deleteWindowsRegistry(value); + } break; + } case "darwin": { const nmhs = this.getDarwinNMHS(); for (const [, value] of Object.entries(nmhs)) { - const p = path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json"); - if (existsSync(p)) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - fs.unlink(p); - } + await this.removeIfExists( + path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json"), + ); } break; } - case "linux": - if ( - existsSync(`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`) - ) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - fs.unlink(`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`); - } - - if ( - existsSync( - `${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`, - ) - ) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - fs.unlink( - `${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`, - ); - } - - if ( - existsSync( - `${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`, - ) - ) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - fs.unlink( - `${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`, - ); - } + case "linux": { + await this.removeIfExists( + `${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`, + ); + await this.removeIfExists( + `${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`, + ); + await this.removeIfExists( + `${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`, + ); break; + } default: break; } } - removeDdgManifests() { + async removeDdgManifests() { switch (process.platform) { case "darwin": { /* eslint-disable-next-line no-useless-escape */ const path = `${this.homedir()}/Library/Containers/com.duckduckgo.macos.browser/Data/Library/Application\ Support/NativeMessagingHosts/com.8bit.bitwarden.json`; - if (existsSync(path)) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - fs.unlink(path); - } + await this.removeIfExists(path); break; } default: @@ -286,6 +290,16 @@ export class NativeMessagingMain { } } + private getWindowsNMHS() { + return { + Firefox: "HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden", + Chrome: "HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden", + Chromium: "HKCU\\SOFTWARE\\Chromium\\NativeMessagingHosts\\com.8bit.bitwarden", + // Edge uses the same registry key as Chrome as a fallback, but it's has its own separate key as well. + "Microsoft Edge": "HKCU\\SOFTWARE\\Microsoft\\Edge\\NativeMessagingHosts\\com.8bit.bitwarden", + }; + } + private getDarwinNMHS() { /* eslint-disable no-useless-escape */ return { @@ -305,10 +319,13 @@ export class NativeMessagingMain { } private async writeManifest(destination: string, manifest: object) { + this.logService.debug(`Writing manifest: ${destination}`); + if (!existsSync(path.dirname(destination))) { await fs.mkdir(path.dirname(destination)); } - fs.writeFile(destination, JSON.stringify(manifest, null, 2)).catch(this.logService.error); + + await fs.writeFile(destination, JSON.stringify(manifest, null, 2)); } private binaryPath() { @@ -327,39 +344,26 @@ export class NativeMessagingMain { return regedit; } - private async createWindowsRegistry(check: string, location: string, jsonFile: string) { + private async createWindowsRegistry(location: string, jsonFile: string) { const regedit = this.getRegeditInstance(); - const list = util.promisify(regedit.list); const createKey = util.promisify(regedit.createKey); const putValue = util.promisify(regedit.putValue); this.logService.debug(`Adding registry: ${location}`); - // Check installed - try { - await list(check); - } catch { - this.logService.warning(`Not finding registry ${check} skipping.`); - return; - } + await createKey(location); - try { - await createKey(location); + // Insert path to manifest + const obj: any = {}; + obj[location] = { + default: { + value: jsonFile, + type: "REG_DEFAULT", + }, + }; - // Insert path to manifest - const obj: any = {}; - obj[location] = { - default: { - value: jsonFile, - type: "REG_DEFAULT", - }, - }; - - return putValue(obj); - } catch (error) { - this.logService.error(error); - } + return putValue(obj); } private async deleteWindowsRegistry(key: string) { @@ -385,4 +389,10 @@ export class NativeMessagingMain { return homedir(); } } + + private async removeIfExists(path: string) { + if (existsSync(path)) { + await fs.unlink(path); + } + } } diff --git a/apps/desktop/src/main/power-monitor.main.ts b/apps/desktop/src/main/power-monitor.main.ts index 067a380ba0..8cad5c1d9e 100644 --- a/apps/desktop/src/main/power-monitor.main.ts +++ b/apps/desktop/src/main/power-monitor.main.ts @@ -1,6 +1,7 @@ import { powerMonitor } from "electron"; -import { ElectronMainMessagingService } from "../services/electron-main-messaging.service"; +import { MessageSender } from "@bitwarden/common/platform/messaging"; + import { isSnapStore } from "../utils"; // tslint:disable-next-line @@ -10,7 +11,7 @@ const IdleCheckInterval = 30 * 1000; // 30 seconds export class PowerMonitorMain { private idle = false; - constructor(private messagingService: ElectronMainMessagingService) {} + constructor(private messagingService: MessageSender) {} init() { // ref: https://github.com/electron/electron/issues/13767 diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index 0531345131..11b38bd273 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2024.4.1", + "version": "2024.4.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2024.4.1", + "version": "2024.4.2", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-native": "file:../desktop_native", diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index 6527c21521..a65dab016c 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2024.4.1", + "version": "2024.4.2", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/apps/desktop/src/platform/preload.ts b/apps/desktop/src/platform/preload.ts index 1f6bd200e0..771d25ef0a 100644 --- a/apps/desktop/src/platform/preload.ts +++ b/apps/desktop/src/platform/preload.ts @@ -74,6 +74,13 @@ const nativeMessaging = { onMessage: (callback: (message: LegacyMessageWrapper | Message) => void) => { ipcRenderer.on("nativeMessaging", (_event, message) => callback(message)); }, + + manifests: { + generate: (create: boolean): Promise => + ipcRenderer.invoke("nativeMessaging.manifests", { create }), + generateDuckDuckGo: (create: boolean): Promise => + ipcRenderer.invoke("nativeMessaging.ddgManifests", { create }), + }, }; const crypto = { @@ -117,12 +124,21 @@ export default { sendMessage: (message: { command: string } & any) => ipcRenderer.send("messagingService", message), - onMessage: (callback: (message: { command: string } & any) => void) => { - ipcRenderer.on("messagingService", (_event, message: any) => { - if (message.command) { - callback(message); - } - }); + onMessage: { + addListener: (callback: (message: { command: string } & any) => void) => { + ipcRenderer.addListener("messagingService", (_event, message: any) => { + if (message.command) { + callback(message); + } + }); + }, + removeListener: (callback: (message: { command: string } & any) => void) => { + ipcRenderer.removeListener("messagingService", (_event, message: any) => { + if (message.command) { + callback(message); + } + }); + }, }, launchUri: (uri: string) => ipcRenderer.invoke("launchUri", uri), diff --git a/apps/desktop/src/platform/services/electron-renderer-message.sender.ts b/apps/desktop/src/platform/services/electron-renderer-message.sender.ts new file mode 100644 index 0000000000..037c303b3b --- /dev/null +++ b/apps/desktop/src/platform/services/electron-renderer-message.sender.ts @@ -0,0 +1,12 @@ +import { MessageSender, CommandDefinition } from "@bitwarden/common/platform/messaging"; +import { getCommand } from "@bitwarden/common/platform/messaging/internal"; + +export class ElectronRendererMessageSender implements MessageSender { + send( + commandDefinition: CommandDefinition | string, + payload: object | T = {}, + ): void { + const command = getCommand(commandDefinition); + ipc.platform.sendMessage(Object.assign({}, { command: command }, payload)); + } +} diff --git a/apps/desktop/src/platform/services/electron-renderer-messaging.service.ts b/apps/desktop/src/platform/services/electron-renderer-messaging.service.ts deleted file mode 100644 index 192efc1dc6..0000000000 --- a/apps/desktop/src/platform/services/electron-renderer-messaging.service.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; - -export class ElectronRendererMessagingService implements MessagingService { - constructor(private broadcasterService: BroadcasterService) { - ipc.platform.onMessage((message) => this.sendMessage(message.command, message, false)); - } - - send(subscriber: string, arg: any = {}) { - this.sendMessage(subscriber, arg, true); - } - - private sendMessage(subscriber: string, arg: any = {}, toMain: boolean) { - const message = Object.assign({}, { command: subscriber }, arg); - this.broadcasterService.send(message); - if (toMain) { - ipc.platform.sendMessage(message); - } - } -} diff --git a/apps/desktop/src/platform/utils/from-ipc-messaging.ts b/apps/desktop/src/platform/utils/from-ipc-messaging.ts new file mode 100644 index 0000000000..254a215ceb --- /dev/null +++ b/apps/desktop/src/platform/utils/from-ipc-messaging.ts @@ -0,0 +1,15 @@ +import { fromEventPattern, share } from "rxjs"; + +import { Message } from "@bitwarden/common/platform/messaging"; +import { tagAsExternal } from "@bitwarden/common/platform/messaging/internal"; + +/** + * Creates an observable that when subscribed to will listen to messaging events through IPC. + * @returns An observable stream of messages. + */ +export const fromIpcMessaging = () => { + return fromEventPattern>( + (handler) => ipc.platform.onMessage.addListener(handler), + (handler) => ipc.platform.onMessage.removeListener(handler), + ).pipe(tagAsExternal, share()); +}; diff --git a/apps/desktop/src/scss/plugins.scss b/apps/desktop/src/scss/plugins.scss deleted file mode 100644 index c156456809..0000000000 --- a/apps/desktop/src/scss/plugins.scss +++ /dev/null @@ -1,95 +0,0 @@ -@import "~ngx-toastr/toastr"; - -@import "variables.scss"; - -.toast-container { - .toast-close-button { - @include themify($themes) { - color: themed("toastTextColor"); - } - font-size: 18px; - margin-right: 4px; - } - - .ngx-toastr { - @include themify($themes) { - color: themed("toastTextColor"); - } - align-items: center; - background-image: none !important; - border-radius: $border-radius; - box-shadow: 0 0 8px rgba(0, 0, 0, 0.35); - display: flex; - padding: 15px; - - .toast-close-button { - position: absolute; - right: 5px; - top: 0; - } - - &:hover { - box-shadow: 0 0 10px rgba(0, 0, 0, 0.6); - } - - .icon i::before { - float: left; - font-style: normal; - font-family: $icomoon-font-family; - font-size: 25px; - line-height: 20px; - padding-right: 15px; - } - - .toast-message { - p { - margin-bottom: 0.5rem; - - &:last-child { - margin-bottom: 0; - } - } - } - - &.toast-danger, - &.toast-error { - @include themify($themes) { - background-color: themed("dangerColor"); - } - - .icon i::before { - content: map_get($icons, "error"); - } - } - - &.toast-warning { - @include themify($themes) { - background-color: themed("warningColor"); - } - - .icon i::before { - content: map_get($icons, "exclamation-triangle"); - } - } - - &.toast-info { - @include themify($themes) { - background-color: themed("infoColor"); - } - - .icon i:before { - content: map_get($icons, "info-circle"); - } - } - - &.toast-success { - @include themify($themes) { - background-color: themed("successColor"); - } - - .icon i:before { - content: map_get($icons, "check"); - } - } - } -} diff --git a/apps/desktop/src/scss/styles.scss b/apps/desktop/src/scss/styles.scss index 033a0f8b67..54c1385dcf 100644 --- a/apps/desktop/src/scss/styles.scss +++ b/apps/desktop/src/scss/styles.scss @@ -11,7 +11,6 @@ @import "buttons.scss"; @import "misc.scss"; @import "modal.scss"; -@import "plugins.scss"; @import "environment.scss"; @import "header.scss"; @import "left-nav.scss"; diff --git a/apps/desktop/src/services/electron-main-messaging.service.ts b/apps/desktop/src/services/electron-main-messaging.service.ts index 71e1b1d7d5..ce4ffd903a 100644 --- a/apps/desktop/src/services/electron-main-messaging.service.ts +++ b/apps/desktop/src/services/electron-main-messaging.service.ts @@ -2,18 +2,17 @@ import * as path from "path"; import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme, Notification, shell } from "electron"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { ThemeType } from "@bitwarden/common/platform/enums"; +import { MessageSender, CommandDefinition } from "@bitwarden/common/platform/messaging"; +// eslint-disable-next-line no-restricted-imports -- Using implementation helper in implementation +import { getCommand } from "@bitwarden/common/platform/messaging/internal"; import { SafeUrls } from "@bitwarden/common/platform/misc/safe-urls"; import { WindowMain } from "../main/window.main"; import { RendererMenuItem } from "../utils"; -export class ElectronMainMessagingService implements MessagingService { - constructor( - private windowMain: WindowMain, - private onMessage: (message: any) => void, - ) { +export class ElectronMainMessagingService implements MessageSender { + constructor(private windowMain: WindowMain) { ipcMain.handle("appVersion", () => { return app.getVersion(); }); @@ -88,9 +87,9 @@ export class ElectronMainMessagingService implements MessagingService { }); } - send(subscriber: string, arg: any = {}) { - const message = Object.assign({}, { command: subscriber }, arg); - this.onMessage(message); + send(commandDefinition: CommandDefinition | string, arg: T | object = {}) { + const command = getCommand(commandDefinition); + const message = Object.assign({}, { command: command }, arg); if (this.windowMain.win != null) { this.windowMain.win.webContents.send("messagingService", message); } diff --git a/apps/desktop/src/vault/app/vault/vault.component.ts b/apps/desktop/src/vault/app/vault/vault.component.ts index e8aabbb20f..208bbc70f0 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.ts +++ b/apps/desktop/src/vault/app/vault/vault.component.ts @@ -8,7 +8,7 @@ import { ViewContainerRef, } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; -import { Subject, takeUntil } from "rxjs"; +import { firstValueFrom, Subject, takeUntil } from "rxjs"; import { first } from "rxjs/operators"; import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; @@ -16,13 +16,13 @@ import { ModalService } from "@bitwarden/angular/services/modal.service"; import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EventType } from "@bitwarden/common/enums"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -32,6 +32,7 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { DialogService } from "@bitwarden/components"; import { PasswordRepromptService } from "@bitwarden/vault"; +import { AuthRequestServiceAbstraction } from "../../../../../../libs/auth/src/common/abstractions"; import { SearchBarService } from "../../../app/layout/search/search-bar.service"; import { GeneratorComponent } from "../../../app/tools/generator.component"; import { invokeMenu, RendererMenuItem } from "../../../utils"; @@ -102,11 +103,12 @@ export class VaultComponent implements OnInit, OnDestroy { private eventCollectionService: EventCollectionService, private totpService: TotpService, private passwordRepromptService: PasswordRepromptService, - private stateService: StateService, private searchBarService: SearchBarService, private apiService: ApiService, private dialogService: DialogService, private billingAccountProfileStateService: BillingAccountProfileStateService, + private authRequestService: AuthRequestServiceAbstraction, + private accountService: AccountService, ) {} async ngOnInit() { @@ -224,7 +226,8 @@ export class VaultComponent implements OnInit, OnDestroy { this.searchBarService.setEnabled(true); this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault")); - const approveLoginRequests = await this.stateService.getApproveLoginRequests(); + const userId = (await firstValueFrom(this.accountService.activeAccount$)).id; + const approveLoginRequests = await this.authRequestService.getAcceptAuthRequests(userId); if (approveLoginRequests) { const authRequest = await this.apiService.getLastAuthRequest(); if (authRequest != null) { diff --git a/apps/web/package.json b/apps/web/package.json index 99828bb543..55fe0987d7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2024.4.0", + "version": "2024.4.1", "scripts": { "build:oss": "webpack", "build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js", diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html index c6bfb94557..3afb816e14 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html @@ -31,7 +31,12 @@ -

{{ "editGroupMembersDesc" | i18n }}

+

+ {{ "editGroupMembersDesc" | i18n }} + + {{ "restrictedGroupAccessDesc" | i18n }} + +

= []; group: GroupView; groupForm = this.formBuilder.group({ @@ -110,6 +125,10 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { return this.params.organizationId; } + protected get editMode(): boolean { + return this.groupId != null; + } + private destroy$ = new Subject(); private get orgCollections$() { @@ -134,7 +153,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { ); } - private get orgMembers$() { + private get orgMembers$(): Observable> { return from(this.organizationUserService.getAllUsers(this.organizationId)).pipe( map((response) => response.data.map((m) => ({ @@ -145,34 +164,55 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { listName: m.name?.length > 0 ? `${m.name} (${m.email})` : m.email, labelName: m.name || m.email, status: m.status, + userId: m.userId as UserId, })), ), ); } - private get groupDetails$() { - if (!this.editMode) { - return of(undefined); - } - - return combineLatest([ - this.groupService.get(this.organizationId, this.groupId), - this.apiService.getGroupUsers(this.organizationId, this.groupId), - ]).pipe( - map(([groupView, users]) => { - groupView.members = users; - return groupView; - }), - catchError((e: unknown) => { - if (e instanceof ErrorResponse) { - this.logService.error(e.message); - } else { - this.logService.error(e.toString()); - } + private groupDetails$: Observable = of(this.editMode).pipe( + concatMap((editMode) => { + if (!editMode) { return of(undefined); - }), - ); - } + } + + return combineLatest([ + this.groupService.get(this.organizationId, this.groupId), + this.apiService.getGroupUsers(this.organizationId, this.groupId), + ]).pipe( + map(([groupView, users]) => { + groupView.members = users; + return groupView; + }), + catchError((e: unknown) => { + if (e instanceof ErrorResponse) { + this.logService.error(e.message); + } else { + this.logService.error(e.toString()); + } + return of(undefined); + }), + ); + }), + shareReplay({ refCount: false }), + ); + + restrictGroupAccess$ = combineLatest([ + this.organizationService.get$(this.organizationId), + this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1), + this.groupDetails$, + ]).pipe( + map( + ([organization, flexibleCollectionsV1Enabled, group]) => + // Feature flag conditionals + flexibleCollectionsV1Enabled && + organization.flexibleCollections && + // Business logic conditionals + !organization.allowAdminAccessToAllCollectionItems && + group !== undefined, + ), + shareReplay({ refCount: true, bufferSize: 1 }), + ); constructor( @Inject(DIALOG_DATA) private params: GroupAddEditDialogParams, @@ -188,17 +228,25 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { private changeDetectorRef: ChangeDetectorRef, private dialogService: DialogService, private organizationService: OrganizationService, + private configService: ConfigService, + private accountService: AccountService, ) { this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info; } ngOnInit() { - this.editMode = this.loading = this.groupId != null; + this.loading = true; this.title = this.i18nService.t(this.editMode ? "editGroup" : "newGroup"); - combineLatest([this.orgCollections$, this.orgMembers$, this.groupDetails$]) + combineLatest([ + this.orgCollections$, + this.orgMembers$, + this.groupDetails$, + this.restrictGroupAccess$, + this.accountService.activeAccount$, + ]) .pipe(takeUntil(this.destroy$)) - .subscribe(([collections, members, group]) => { + .subscribe(([collections, members, group, restrictGroupAccess, activeAccount]) => { this.collections = collections; this.members = members; this.group = group; @@ -224,6 +272,18 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { }); } + // If the current user is not already in the group and cannot add themselves, remove them from the list + if (restrictGroupAccess) { + const organizationUserId = this.members.find((m) => m.userId === activeAccount.id).id; + const isAlreadyInGroup = this.groupForm.value.members.some( + (m) => m.id === organizationUserId, + ); + + if (!isAlreadyInGroup) { + this.members = this.members.filter((m) => m.id !== organizationUserId); + } + } + this.loading = false; }); } diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html index 95febbd3c5..4d81d070fb 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html @@ -385,7 +385,12 @@

{{ "secretsManagerAccessDescription" | i18n }}

- + {{ "userAccessSecretsManagerGA" | i18n }} diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index 752122de00..f1af950650 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -31,6 +31,7 @@ import { CollectionView } from "@bitwarden/common/vault/models/view/collection.v import { DialogService } from "@bitwarden/components"; import { CollectionAdminService } from "../../../../../vault/core/collection-admin.service"; +import { CollectionAdminView } from "../../../../../vault/core/views/collection-admin.view"; import { CollectionAccessSelectionView, GroupService, @@ -62,6 +63,7 @@ export interface MemberDialogParams { organizationUserId: string; allOrganizationUserEmails: string[]; usesKeyConnector: boolean; + isOnSecretsManagerStandalone: boolean; initialTab?: MemberDialogTab; numConfirmedMembers: number; } @@ -87,6 +89,7 @@ export class MemberDialogComponent implements OnDestroy { organizationUserType = OrganizationUserType; PermissionMode = PermissionMode; showNoMasterPasswordWarning = false; + isOnSecretsManagerStandalone: boolean; protected organization$: Observable; protected collectionAccessItems: AccessItemView[] = []; @@ -159,6 +162,13 @@ export class MemberDialogComponent implements OnDestroy { this.editMode = this.params.organizationUserId != null; this.tabIndex = this.params.initialTab ?? MemberDialogTab.Role; this.title = this.i18nService.t(this.editMode ? "editMember" : "inviteMember"); + this.isOnSecretsManagerStandalone = this.params.isOnSecretsManagerStandalone; + + if (this.isOnSecretsManagerStandalone) { + this.formGroup.patchValue({ + accessSecretsManager: true, + }); + } const groups$ = this.organization$.pipe( switchMap((organization) => @@ -206,25 +216,52 @@ export class MemberDialogComponent implements OnDestroy { collections: this.collectionAdminService.getAll(this.params.organizationId), userDetails: userDetails$, groups: groups$, + flexibleCollectionsV1Enabled: this.configService.getFeatureFlag$( + FeatureFlag.FlexibleCollectionsV1, + false, + ), }) .pipe(takeUntil(this.destroy$)) - .subscribe(({ organization, collections, userDetails, groups }) => { - this.setFormValidators(organization); + .subscribe( + ({ organization, collections, userDetails, groups, flexibleCollectionsV1Enabled }) => { + this.setFormValidators(organization); - this.collectionAccessItems = [].concat( - collections.map((c) => mapCollectionToAccessItemView(c)), - ); + // Groups tab: populate available groups + this.groupAccessItems = [].concat( + groups.map((g) => mapGroupToAccessItemView(g)), + ); - this.groupAccessItems = [].concat( - groups.map((g) => mapGroupToAccessItemView(g)), - ); + // Collections tab: Populate all available collections (including current user access where applicable) + this.collectionAccessItems = collections + .map((c) => + mapCollectionToAccessItemView( + c, + organization, + flexibleCollectionsV1Enabled, + userDetails == null + ? undefined + : c.users.find((access) => access.id === userDetails.id), + ), + ) + // But remove collections that we can't assign access to, unless the user is already assigned + .filter( + (item) => + !item.readonly || userDetails?.collections.some((access) => access.id == item.id), + ); - if (this.params.organizationUserId) { - this.loadOrganizationUser(userDetails, groups, collections); - } + if (userDetails != null) { + this.loadOrganizationUser( + userDetails, + groups, + collections, + organization, + flexibleCollectionsV1Enabled, + ); + } - this.loading = false; - }); + this.loading = false; + }, + ); } private setFormValidators(organization: Organization) { @@ -246,7 +283,9 @@ export class MemberDialogComponent implements OnDestroy { private loadOrganizationUser( userDetails: OrganizationUserAdminView, groups: GroupView[], - collections: CollectionView[], + collections: CollectionAdminView[], + organization: Organization, + flexibleCollectionsV1Enabled: boolean, ) { if (!userDetails) { throw new Error("Could not find user to edit."); @@ -295,13 +334,22 @@ export class MemberDialogComponent implements OnDestroy { }), ); + // Populate additional collection access via groups (rendered as separate rows from user access) this.collectionAccessItems = this.collectionAccessItems.concat( collectionsFromGroups.map(({ collection, accessSelection, group }) => - mapCollectionToAccessItemView(collection, accessSelection, group), + mapCollectionToAccessItemView( + collection, + organization, + flexibleCollectionsV1Enabled, + accessSelection, + group, + ), ), ); - const accessSelections = mapToAccessSelections(userDetails); + // Set current collections and groups the user has access to (excluding collections the current user doesn't have + // permissions to change - they are included as readonly via the CollectionAccessItems) + const accessSelections = mapToAccessSelections(userDetails, this.collectionAccessItems); const groupAccessSelections = mapToGroupAccessSelections(userDetails.groups); this.formGroup.removeControl("emails"); @@ -573,6 +621,8 @@ export class MemberDialogComponent implements OnDestroy { function mapCollectionToAccessItemView( collection: CollectionView, + organization: Organization, + flexibleCollectionsV1Enabled: boolean, accessSelection?: CollectionAccessSelectionView, group?: GroupView, ): AccessItemView { @@ -581,7 +631,8 @@ function mapCollectionToAccessItemView( id: group ? `${collection.id}-${group.id}` : collection.id, labelName: collection.name, listName: collection.name, - readonly: group !== undefined, + readonly: + group !== undefined || !collection.canEdit(organization, flexibleCollectionsV1Enabled), readonlyPermission: accessSelection ? convertToPermission(accessSelection) : undefined, viaGroupName: group?.name, }; @@ -596,16 +647,23 @@ function mapGroupToAccessItemView(group: GroupView): AccessItemView { }; } -function mapToAccessSelections(user: OrganizationUserAdminView): AccessItemValue[] { +function mapToAccessSelections( + user: OrganizationUserAdminView, + items: AccessItemView[], +): AccessItemValue[] { if (user == undefined) { return []; } - return [].concat( - user.collections.map((selection) => ({ - id: selection.id, - type: AccessItemType.Collection, - permission: convertToPermission(selection), - })), + + return ( + user.collections + // The FormControl value only represents editable collection access - exclude readonly access selections + .filter((selection) => !items.find((item) => item.id == selection.id).readonly) + .map((selection) => ({ + id: selection.id, + type: AccessItemType.Collection, + permission: convertToPermission(selection), + })) ); } diff --git a/apps/web/src/app/admin-console/organizations/members/people.component.ts b/apps/web/src/app/admin-console/organizations/members/people.component.ts index 6b632dce38..0df247d7b0 100644 --- a/apps/web/src/app/admin-console/organizations/members/people.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/people.component.ts @@ -37,6 +37,7 @@ import { import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; +import { OrganizationBillingServiceAbstraction as OrganizationBillingService } from "@bitwarden/common/billing/abstractions/organization-billing.service"; import { ProductType } from "@bitwarden/common/enums"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; @@ -93,6 +94,7 @@ export class PeopleComponent extends BasePeopleComponent { organization: Organization; status: OrganizationUserStatusType = null; orgResetPasswordPolicyEnabled = false; + orgIsOnSecretsManagerStandalone = false; protected canUseSecretsManager$: Observable; @@ -119,6 +121,7 @@ export class PeopleComponent extends BasePeopleComponent { private groupService: GroupService, private collectionService: CollectionService, organizationManagementPreferencesService: OrganizationManagementPreferencesService, + private organizationBillingService: OrganizationBillingService, ) { super( apiService, @@ -187,6 +190,11 @@ export class PeopleComponent extends BasePeopleComponent { .find((p) => p.organizationId === this.organization.id); this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled; + this.orgIsOnSecretsManagerStandalone = + await this.organizationBillingService.isOnSecretsManagerStandalone( + this.organization.id, + ); + await this.load(); this.searchText = qParams.search; @@ -446,6 +454,7 @@ export class PeopleComponent extends BasePeopleComponent { organizationUserId: user != null ? user.id : null, allOrganizationUserEmails: this.allUsers?.map((user) => user.email) ?? [], usesKeyConnector: user?.usesKeyConnector, + isOnSecretsManagerStandalone: this.orgIsOnSecretsManagerStandalone, initialTab: initialTab, numConfirmedMembers: this.confirmedCount, }, diff --git a/apps/web/src/app/admin-console/organizations/settings/settings.component.html b/apps/web/src/app/admin-console/organizations/settings/settings.component.html deleted file mode 100644 index 47592df378..0000000000 --- a/apps/web/src/app/admin-console/organizations/settings/settings.component.html +++ /dev/null @@ -1,88 +0,0 @@ -
diff --git a/apps/web/src/app/admin-console/organizations/settings/settings.component.ts b/apps/web/src/app/admin-console/organizations/settings/settings.component.ts deleted file mode 100644 index ab25829d19..0000000000 --- a/apps/web/src/app/admin-console/organizations/settings/settings.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component, OnInit } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; -import { Observable, switchMap } from "rxjs"; - -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; - -@Component({ - selector: "app-org-settings", - templateUrl: "settings.component.html", -}) -export class SettingsComponent implements OnInit { - organization$: Observable; - FeatureFlag = FeatureFlag; - - constructor( - private route: ActivatedRoute, - private organizationService: OrganizationService, - ) {} - - ngOnInit() { - this.organization$ = this.route.params.pipe( - switchMap((params) => this.organizationService.get$(params.organizationId)), - ); - } -} diff --git a/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.html b/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.html new file mode 100644 index 0000000000..a287a537a4 --- /dev/null +++ b/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.html @@ -0,0 +1,34 @@ +
+
+
+

{{ "deleteProvider" | i18n }}

+
+
+ {{ "deleteProviderWarning" | i18n }} +

+ {{ name }} +

+

{{ "deleteProviderRecoverConfirmDesc" | i18n }}

+
+
+ + + {{ "cancel" | i18n }} + +
+
+
+
+
+
diff --git a/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts b/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts new file mode 100644 index 0000000000..0550820cda --- /dev/null +++ b/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts @@ -0,0 +1,61 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { firstValueFrom } from "rxjs"; + +import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; +import { ProviderVerifyRecoverDeleteRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-verify-recover-delete.request"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +@Component({ + selector: "app-verify-recover-delete-provider", + templateUrl: "verify-recover-delete-provider.component.html", +}) +// eslint-disable-next-line rxjs-angular/prefer-takeuntil +export class VerifyRecoverDeleteProviderComponent implements OnInit { + name: string; + formPromise: Promise; + + private providerId: string; + private token: string; + + constructor( + private router: Router, + private providerApiService: ProviderApiServiceAbstraction, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private route: ActivatedRoute, + private logService: LogService, + ) {} + + async ngOnInit() { + const qParams = await firstValueFrom(this.route.queryParams); + if (qParams.providerId != null && qParams.token != null && qParams.name != null) { + this.providerId = qParams.providerId; + this.token = qParams.token; + this.name = qParams.name; + } else { + await this.router.navigate(["/"]); + } + } + + async submit() { + try { + const request = new ProviderVerifyRecoverDeleteRequest(this.token); + this.formPromise = this.providerApiService.providerRecoverDeleteToken( + this.providerId, + request, + ); + await this.formPromise; + this.platformUtilsService.showToast( + "success", + this.i18nService.t("providerDeleted"), + this.i18nService.t("providerDeletedDesc"), + ); + await this.router.navigate(["/"]); + } catch (e) { + this.logService.error(e); + } + } +} diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 7a3b34969a..1da2d94c15 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -1,9 +1,7 @@ import { DOCUMENT } from "@angular/common"; -import { Component, Inject, NgZone, OnDestroy, OnInit, SecurityContext } from "@angular/core"; -import { DomSanitizer } from "@angular/platform-browser"; +import { Component, Inject, NgZone, OnDestroy, OnInit } from "@angular/core"; import { NavigationEnd, Router } from "@angular/router"; import * as jq from "jquery"; -import { IndividualConfig, ToastrService } from "ngx-toastr"; import { Subject, switchMap, takeUntil, timer } from "rxjs"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; @@ -29,7 +27,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { PolicyListService } from "./admin-console/core/policy-list.service"; import { @@ -68,14 +66,13 @@ export class AppComponent implements OnDestroy, OnInit { private cipherService: CipherService, private authService: AuthService, private router: Router, - private toastrService: ToastrService, + private toastService: ToastService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private ngZone: NgZone, private vaultTimeoutService: VaultTimeoutService, private cryptoService: CryptoService, private collectionService: CollectionService, - private sanitizer: DomSanitizer, private searchService: SearchService, private notificationsService: NotificationsService, private stateService: StateService, @@ -209,7 +206,7 @@ export class AppComponent implements OnDestroy, OnInit { break; } case "showToast": - this.showToast(message); + this.toastService._showToast(message); break; case "convertAccountToKeyConnector": // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. @@ -327,34 +324,6 @@ export class AppComponent implements OnDestroy, OnInit { }, IdleTimeout); } - private showToast(msg: any) { - let message = ""; - - const options: Partial = {}; - - if (typeof msg.text === "string") { - message = msg.text; - } else if (msg.text.length === 1) { - message = msg.text[0]; - } else { - msg.text.forEach( - (t: string) => - (message += "

" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "

"), - ); - options.enableHtml = true; - } - if (msg.options != null) { - if (msg.options.trustedHtml === true) { - options.enableHtml = true; - } - if (msg.options.timeout != null && msg.options.timeout > 0) { - options.timeOut = msg.options.timeout; - } - } - - this.toastrService.show(message, msg.title, options, "toast-" + msg.type); - } - private idleStateChanged() { if (this.isIdle) { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts index 0c5f1d706b..bd138cad29 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts @@ -103,7 +103,8 @@ export class TrialBillingStepComponent implements OnInit { planDescription, }); - this.messagingService.send("organizationCreated", organizationId); + // TODO: No one actually listening to this? + this.messagingService.send("organizationCreated", { organizationId }); } protected changedCountry() { diff --git a/apps/web/src/app/billing/individual/billing-history-view.component.html b/apps/web/src/app/billing/individual/billing-history-view.component.html index 1032558f5f..2491fc42c7 100644 --- a/apps/web/src/app/billing/individual/billing-history-view.component.html +++ b/apps/web/src/app/billing/individual/billing-history-view.component.html @@ -1,13 +1,12 @@ -
-

+
+

{{ "billingHistory" | i18n }} -

+

- {{ "loading" | i18n }} + {{ "loading" | i18n }} diff --git a/apps/web/src/app/billing/individual/user-subscription.component.html b/apps/web/src/app/billing/individual/user-subscription.component.html index 874983df84..380116e81b 100644 --- a/apps/web/src/app/billing/individual/user-subscription.component.html +++ b/apps/web/src/app/billing/individual/user-subscription.component.html @@ -170,8 +170,8 @@
-
-
-
diff --git a/apps/web/src/app/billing/individual/user-subscription.component.ts b/apps/web/src/app/billing/individual/user-subscription.component.ts index 7d8c3a0f18..fa21317c18 100644 --- a/apps/web/src/app/billing/individual/user-subscription.component.ts +++ b/apps/web/src/app/billing/individual/user-subscription.component.ts @@ -12,6 +12,10 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { DialogService } from "@bitwarden/components"; +import { + AdjustStorageDialogResult, + openAdjustStorageDialog, +} from "../shared/adjust-storage.component"; import { OffboardingSurveyDialogResultType, openOffboardingSurvey, @@ -24,7 +28,6 @@ export class UserSubscriptionComponent implements OnInit { loading = false; firstLoaded = false; adjustStorageAdd = true; - showAdjustStorage = false; showUpdateLicense = false; sub: SubscriptionResponse; selfHosted = false; @@ -144,19 +147,20 @@ export class UserSubscriptionComponent implements OnInit { } } - adjustStorage(add: boolean) { - this.adjustStorageAdd = add; - this.showAdjustStorage = true; - } - - closeStorage(load: boolean) { - this.showAdjustStorage = false; - if (load) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - } - } + adjustStorage = (add: boolean) => { + return async () => { + const dialogRef = openAdjustStorageDialog(this.dialogService, { + data: { + storageGbPrice: 4, + add: add, + }, + }); + const result = await lastValueFrom(dialogRef.closed); + if (result === AdjustStorageDialogResult.Adjusted) { + await this.load(); + } + }; + }; get subscriptionMarkedForCancel() { return ( diff --git a/apps/web/src/app/billing/organizations/change-plan.component.html b/apps/web/src/app/billing/organizations/change-plan.component.html index b9a15be5ea..a25dde4fd3 100644 --- a/apps/web/src/app/billing/organizations/change-plan.component.html +++ b/apps/web/src/app/billing/organizations/change-plan.component.html @@ -1,10 +1,18 @@ -
-
- -

{{ "changeBillingPlan" | i18n }}

-

{{ "changeBillingPlanUpgrade" | i18n }}

+
+
+ +

{{ "changeBillingPlan" | i18n }}

+

{{ "changeBillingPlanUpgrade" | i18n }}

- {{ "loading" | i18n }} + {{ "loading" | i18n }} diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts index f2fb296522..23d48d93be 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.ts +++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts @@ -15,6 +15,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { OrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/organization-create.request"; @@ -47,11 +48,7 @@ interface OnSuccessArgs { organizationId: string; } -const AllowedLegacyPlanTypes = [ - PlanType.TeamsMonthly2023, - PlanType.TeamsAnnually2023, - PlanType.EnterpriseAnnually2023, - PlanType.EnterpriseMonthly2023, +const Allowed2020PlansForLegacyProviders = [ PlanType.TeamsMonthly2020, PlanType.TeamsAnnually2020, PlanType.EnterpriseAnnually2020, @@ -147,6 +144,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { private messagingService: MessagingService, private formBuilder: FormBuilder, private organizationApiService: OrganizationApiServiceAbstraction, + private providerApiService: ProviderApiServiceAbstraction, ) { this.selfHosted = platformUtilsService.isSelfHost(); } @@ -182,7 +180,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { if (this.hasProvider) { this.formGroup.controls.businessOwned.setValue(true); this.changedOwnedBusiness(); - this.provider = await this.apiService.getProvider(this.providerId); + this.provider = await this.providerApiService.getProvider(this.providerId); const providerDefaultPlan = this.passwordManagerPlans.find( (plan) => plan.type === PlanType.TeamsAnnually, ); @@ -281,7 +279,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { (!this.currentPlan || this.currentPlan.upgradeSortOrder < plan.upgradeSortOrder) && (!this.hasProvider || plan.product !== ProductType.TeamsStarter) && ((!this.isProviderQualifiedFor2020Plan() && this.planIsEnabled(plan)) || - (this.isProviderQualifiedFor2020Plan() && AllowedLegacyPlanTypes.includes(plan.type))), + (this.isProviderQualifiedFor2020Plan() && + Allowed2020PlansForLegacyProviders.includes(plan.type))), ); result.sort((planA, planB) => planA.displaySortOrder - planB.displaySortOrder); @@ -296,7 +295,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { (plan) => plan.product === selectedProductType && ((!this.isProviderQualifiedFor2020Plan() && this.planIsEnabled(plan)) || - (this.isProviderQualifiedFor2020Plan() && AllowedLegacyPlanTypes.includes(plan.type))), + (this.isProviderQualifiedFor2020Plan() && + Allowed2020PlansForLegacyProviders.includes(plan.type))), ) || []; result.sort((planA, planB) => planA.displaySortOrder - planB.displaySortOrder); @@ -587,7 +587,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.formPromise = doSubmit(); const organizationId = await this.formPromise; this.onSuccess.emit({ organizationId: organizationId }); - this.messagingService.send("organizationCreated", organizationId); + // TODO: No one actually listening to this message? + this.messagingService.send("organizationCreated", { organizationId }); } catch (e) { this.logService.error(e); } diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html index b4fac65854..16641c0d52 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html @@ -175,23 +175,24 @@
-
- -
-
diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts index 2173d4c0ca..9326359bd8 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts @@ -18,6 +18,10 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { DialogService } from "@bitwarden/components"; +import { + AdjustStorageDialogResult, + openAdjustStorageDialog, +} from "../shared/adjust-storage.component"; import { OffboardingSurveyDialogResultType, openOffboardingSurvey, @@ -36,8 +40,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy userOrg: Organization; showChangePlan = false; showDownloadLicense = false; - adjustStorageAdd = true; - showAdjustStorage = false; hasBillingSyncToken: boolean; showAdjustSecretsManager = false; showSecretsManagerSubscribe = false; @@ -361,19 +363,22 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy this.load(); } - adjustStorage(add: boolean) { - this.adjustStorageAdd = add; - this.showAdjustStorage = true; - } - - closeStorage(load: boolean) { - this.showAdjustStorage = false; - if (load) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - } - } + adjustStorage = (add: boolean) => { + return async () => { + const dialogRef = openAdjustStorageDialog(this.dialogService, { + data: { + storageGbPrice: this.storageGbPrice, + add: add, + organizationId: this.organizationId, + interval: this.billingInterval, + }, + }); + const result = await lastValueFrom(dialogRef.closed); + if (result === AdjustStorageDialogResult.Adjusted) { + await this.load(); + } + }; + }; removeSponsorship = async () => { const confirmed = await this.dialogService.openSimpleDialog({ diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog.component.html b/apps/web/src/app/billing/shared/adjust-payment-dialog.component.html new file mode 100644 index 0000000000..0f92b023b1 --- /dev/null +++ b/apps/web/src/app/billing/shared/adjust-payment-dialog.component.html @@ -0,0 +1,25 @@ +
+ + + + + + + + + + +
diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog.component.ts b/apps/web/src/app/billing/shared/adjust-payment-dialog.component.ts new file mode 100644 index 0000000000..41d0ad7e7a --- /dev/null +++ b/apps/web/src/app/billing/shared/adjust-payment-dialog.component.ts @@ -0,0 +1,110 @@ +import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; +import { Component, Inject, ViewChild } from "@angular/core"; +import { FormGroup } from "@angular/forms"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction"; +import { PaymentMethodType } from "@bitwarden/common/billing/enums"; +import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { DialogService } from "@bitwarden/components"; + +import { PaymentComponent } from "./payment.component"; +import { TaxInfoComponent } from "./tax-info.component"; + +export interface AdjustPaymentDialogData { + organizationId: string; + currentType: PaymentMethodType; +} + +export enum AdjustPaymentDialogResult { + Adjusted = "adjusted", + Cancelled = "cancelled", +} + +@Component({ + templateUrl: "adjust-payment-dialog.component.html", +}) +export class AdjustPaymentDialogComponent { + @ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent; + @ViewChild(TaxInfoComponent, { static: true }) taxInfoComponent: TaxInfoComponent; + + organizationId: string; + currentType: PaymentMethodType; + paymentMethodType = PaymentMethodType; + + protected DialogResult = AdjustPaymentDialogResult; + protected formGroup = new FormGroup({}); + + constructor( + private dialogRef: DialogRef, + @Inject(DIALOG_DATA) protected data: AdjustPaymentDialogData, + private apiService: ApiService, + private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService, + private logService: LogService, + private organizationApiService: OrganizationApiServiceAbstraction, + private paymentMethodWarningService: PaymentMethodWarningService, + ) { + this.organizationId = data.organizationId; + this.currentType = data.currentType; + } + + submit = async () => { + const request = new PaymentRequest(); + const response = this.paymentComponent.createPaymentToken().then((result) => { + request.paymentToken = result[0]; + request.paymentMethodType = result[1]; + request.postalCode = this.taxInfoComponent.taxInfo.postalCode; + request.country = this.taxInfoComponent.taxInfo.country; + if (this.organizationId == null) { + return this.apiService.postAccountPayment(request); + } else { + request.taxId = this.taxInfoComponent.taxInfo.taxId; + request.state = this.taxInfoComponent.taxInfo.state; + request.line1 = this.taxInfoComponent.taxInfo.line1; + request.line2 = this.taxInfoComponent.taxInfo.line2; + request.city = this.taxInfoComponent.taxInfo.city; + request.state = this.taxInfoComponent.taxInfo.state; + return this.organizationApiService.updatePayment(this.organizationId, request); + } + }); + await response; + if (this.organizationId) { + await this.paymentMethodWarningService.removeSubscriptionRisk(this.organizationId); + } + this.platformUtilsService.showToast( + "success", + null, + this.i18nService.t("updatedPaymentMethod"), + ); + this.dialogRef.close(AdjustPaymentDialogResult.Adjusted); + }; + + changeCountry() { + if (this.taxInfoComponent.taxInfo.country === "US") { + this.paymentComponent.hideBank = !this.organizationId; + } else { + this.paymentComponent.hideBank = true; + if (this.paymentComponent.method === PaymentMethodType.BankAccount) { + this.paymentComponent.method = PaymentMethodType.Card; + this.paymentComponent.changeMethod(); + } + } + } +} + +/** + * Strongly typed helper to open a AdjustPaymentDialog + * @param dialogService Instance of the dialog service that will be used to open the dialog + * @param config Configuration for the dialog + */ +export function openAdjustPaymentDialog( + dialogService: DialogService, + config: DialogConfig, +) { + return dialogService.open(AdjustPaymentDialogComponent, config); +} diff --git a/apps/web/src/app/billing/shared/adjust-payment.component.html b/apps/web/src/app/billing/shared/adjust-payment.component.html deleted file mode 100644 index 724e7a44c2..0000000000 --- a/apps/web/src/app/billing/shared/adjust-payment.component.html +++ /dev/null @@ -1,19 +0,0 @@ -
-
- -

- {{ (currentType != null ? "changePaymentMethod" : "addPaymentMethod") | i18n }} -

- - - - -
-
diff --git a/apps/web/src/app/billing/shared/adjust-payment.component.ts b/apps/web/src/app/billing/shared/adjust-payment.component.ts deleted file mode 100644 index 7452344141..0000000000 --- a/apps/web/src/app/billing/shared/adjust-payment.component.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Component, EventEmitter, Input, Output, ViewChild } from "@angular/core"; - -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; -import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction"; -import { PaymentMethodType } from "@bitwarden/common/billing/enums"; -import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; - -import { PaymentComponent } from "./payment.component"; -import { TaxInfoComponent } from "./tax-info.component"; - -@Component({ - selector: "app-adjust-payment", - templateUrl: "adjust-payment.component.html", -}) -export class AdjustPaymentComponent { - @ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent; - @ViewChild(TaxInfoComponent, { static: true }) taxInfoComponent: TaxInfoComponent; - - @Input() currentType?: PaymentMethodType; - @Input() organizationId: string; - @Output() onAdjusted = new EventEmitter(); - @Output() onCanceled = new EventEmitter(); - - paymentMethodType = PaymentMethodType; - formPromise: Promise; - - constructor( - private apiService: ApiService, - private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, - private logService: LogService, - private organizationApiService: OrganizationApiServiceAbstraction, - private paymentMethodWarningService: PaymentMethodWarningService, - ) {} - - async submit() { - try { - const request = new PaymentRequest(); - this.formPromise = this.paymentComponent.createPaymentToken().then((result) => { - request.paymentToken = result[0]; - request.paymentMethodType = result[1]; - request.postalCode = this.taxInfoComponent.taxInfo.postalCode; - request.country = this.taxInfoComponent.taxInfo.country; - if (this.organizationId == null) { - return this.apiService.postAccountPayment(request); - } else { - request.taxId = this.taxInfoComponent.taxInfo.taxId; - request.state = this.taxInfoComponent.taxInfo.state; - request.line1 = this.taxInfoComponent.taxInfo.line1; - request.line2 = this.taxInfoComponent.taxInfo.line2; - request.city = this.taxInfoComponent.taxInfo.city; - request.state = this.taxInfoComponent.taxInfo.state; - return this.organizationApiService.updatePayment(this.organizationId, request); - } - }); - await this.formPromise; - if (this.organizationId) { - await this.paymentMethodWarningService.removeSubscriptionRisk(this.organizationId); - } - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("updatedPaymentMethod"), - ); - this.onAdjusted.emit(); - } catch (e) { - this.logService.error(e); - } - } - - cancel() { - this.onCanceled.emit(); - } - - changeCountry() { - if (this.taxInfoComponent.taxInfo.country === "US") { - this.paymentComponent.hideBank = !this.organizationId; - } else { - this.paymentComponent.hideBank = true; - if (this.paymentComponent.method === PaymentMethodType.BankAccount) { - this.paymentComponent.method = PaymentMethodType.Card; - this.paymentComponent.changeMethod(); - } - } - } -} diff --git a/apps/web/src/app/billing/shared/adjust-storage.component.html b/apps/web/src/app/billing/shared/adjust-storage.component.html index aa6daca335..a597a3ae5e 100644 --- a/apps/web/src/app/billing/shared/adjust-storage.component.html +++ b/apps/web/src/app/billing/shared/adjust-storage.component.html @@ -1,43 +1,35 @@ -
-
- -

{{ (add ? "addStorage" : "removeStorage") | i18n }}

-
-
- - + + + +

{{ (add ? "storageAddNote" : "storageRemoveNote") | i18n }}

+
+ + {{ (add ? "gbStorageAdd" : "gbStorageRemove") | i18n }} + + + {{ "total" | i18n }}: + {{ formGroup.get("storageAdjustment").value || 0 }} GB × + {{ storageGbPrice | currency: "$" }} = {{ adjustedStorageTotal | currency: "$" }} /{{ + interval | i18n + }} + +
-
-
- {{ "total" | i18n }}: {{ storageAdjustment || 0 }} GB × - {{ storageGbPrice | currency: "$" }} = {{ adjustedStorageTotal | currency: "$" }} /{{ - interval | i18n - }} -
- - - - {{ (add ? "storageAddNote" : "storageRemoveNote") | i18n }} - -
+ + + + + + diff --git a/apps/web/src/app/billing/shared/adjust-storage.component.ts b/apps/web/src/app/billing/shared/adjust-storage.component.ts index 25462c2829..fcdbc3437d 100644 --- a/apps/web/src/app/billing/shared/adjust-storage.component.ts +++ b/apps/web/src/app/billing/shared/adjust-storage.component.ts @@ -1,4 +1,6 @@ -import { Component, EventEmitter, Input, Output, ViewChild } from "@angular/core"; +import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; +import { Component, Inject, ViewChild } from "@angular/core"; +import { FormControl, FormGroup, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -8,27 +10,45 @@ import { StorageRequest } from "@bitwarden/common/models/request/storage.request import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { DialogService } from "@bitwarden/components"; import { PaymentComponent } from "./payment.component"; +export interface AdjustStorageDialogData { + storageGbPrice: number; + add: boolean; + organizationId?: string; + interval?: string; +} + +export enum AdjustStorageDialogResult { + Adjusted = "adjusted", + Cancelled = "cancelled", +} + @Component({ - selector: "app-adjust-storage", templateUrl: "adjust-storage.component.html", }) export class AdjustStorageComponent { - @Input() storageGbPrice = 0; - @Input() add = true; - @Input() organizationId: string; - @Input() interval = "year"; - @Output() onAdjusted = new EventEmitter(); - @Output() onCanceled = new EventEmitter(); + storageGbPrice: number; + add: boolean; + organizationId: string; + interval: string; @ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent; - storageAdjustment = 0; - formPromise: Promise; + protected DialogResult = AdjustStorageDialogResult; + protected formGroup = new FormGroup({ + storageAdjustment: new FormControl(0, [ + Validators.required, + Validators.min(0), + Validators.max(99), + ]), + }); constructor( + private dialogRef: DialogRef, + @Inject(DIALOG_DATA) protected data: AdjustStorageDialogData, private apiService: ApiService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, @@ -36,69 +56,74 @@ export class AdjustStorageComponent { private activatedRoute: ActivatedRoute, private logService: LogService, private organizationApiService: OrganizationApiServiceAbstraction, - ) {} + ) { + this.storageGbPrice = data.storageGbPrice; + this.add = data.add; + this.organizationId = data.organizationId; + this.interval = data.interval || "year"; + } - async submit() { - try { - const request = new StorageRequest(); - request.storageGbAdjustment = this.storageAdjustment; - if (!this.add) { - request.storageGbAdjustment *= -1; - } - - let paymentFailed = false; - const action = async () => { - let response: Promise; - if (this.organizationId == null) { - response = this.formPromise = this.apiService.postAccountStorage(request); - } else { - response = this.formPromise = this.organizationApiService.updateStorage( - this.organizationId, - request, - ); - } - const result = await response; - if (result != null && result.paymentIntentClientSecret != null) { - try { - await this.paymentComponent.handleStripeCardPayment( - result.paymentIntentClientSecret, - null, - ); - } catch { - paymentFailed = true; - } - } - }; - this.formPromise = action(); - await this.formPromise; - this.onAdjusted.emit(this.storageAdjustment); - if (paymentFailed) { - this.platformUtilsService.showToast( - "warning", - null, - this.i18nService.t("couldNotChargeCardPayInvoice"), - { timeout: 10000 }, - ); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["../billing"], { relativeTo: this.activatedRoute }); - } else { - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("adjustedStorage", request.storageGbAdjustment.toString()), - ); - } - } catch (e) { - this.logService.error(e); + submit = async () => { + const request = new StorageRequest(); + request.storageGbAdjustment = this.formGroup.value.storageAdjustment; + if (!this.add) { + request.storageGbAdjustment *= -1; } - } - cancel() { - this.onCanceled.emit(); - } + let paymentFailed = false; + const action = async () => { + let response: Promise; + if (this.organizationId == null) { + response = this.apiService.postAccountStorage(request); + } else { + response = this.organizationApiService.updateStorage(this.organizationId, request); + } + const result = await response; + if (result != null && result.paymentIntentClientSecret != null) { + try { + await this.paymentComponent.handleStripeCardPayment( + result.paymentIntentClientSecret, + null, + ); + } catch { + paymentFailed = true; + } + } + }; + await action(); + this.dialogRef.close(AdjustStorageDialogResult.Adjusted); + if (paymentFailed) { + this.platformUtilsService.showToast( + "warning", + null, + this.i18nService.t("couldNotChargeCardPayInvoice"), + { timeout: 10000 }, + ); + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.router.navigate(["../billing"], { relativeTo: this.activatedRoute }); + } else { + this.platformUtilsService.showToast( + "success", + null, + this.i18nService.t("adjustedStorage", request.storageGbAdjustment.toString()), + ); + } + }; get adjustedStorageTotal(): number { - return this.storageGbPrice * this.storageAdjustment; + return this.storageGbPrice * this.formGroup.value.storageAdjustment; } } + +/** + * Strongly typed helper to open an AdjustStorageDialog + * @param dialogService Instance of the dialog service that will be used to open the dialog + * @param config Configuration for the dialog + */ +export function openAdjustStorageDialog( + dialogService: DialogService, + config: DialogConfig, +) { + return dialogService.open(AdjustStorageComponent, config); +} diff --git a/apps/web/src/app/billing/shared/billing-history.component.html b/apps/web/src/app/billing/shared/billing-history.component.html index 56a8a990d4..1719a59076 100644 --- a/apps/web/src/app/billing/shared/billing-history.component.html +++ b/apps/web/src/app/billing/shared/billing-history.component.html @@ -1,65 +1,72 @@ -

{{ "invoices" | i18n }}

-

{{ "noInvoices" | i18n }}

- - - - - + + + + + + + + + +

{{ "transactions" | i18n }}

+

+ {{ "noTransactions" | i18n }} +

+ + +
+ + + + - - - - -
{{ i.date | date: "mediumDate" }} - +

{{ "invoices" | i18n }}

+

{{ "noInvoices" | i18n }}

+ + +
{{ i.date | date: "mediumDate" }} + + + + {{ "invoiceNumber" | i18n: i.number }} + {{ i.amount | currency: "$" }} + + + {{ "paid" | i18n }} + + + + {{ "unpaid" | i18n }} + +
{{ t.createdDate | date: "mediumDate" }} + + {{ "chargeNoun" | i18n }} + + {{ "refundNoun" | i18n }} + + + {{ t.details }} + - - - {{ "invoiceNumber" | i18n: i.number }} - {{ i.amount | currency: "$" }} - - - {{ "paid" | i18n }} - - - - {{ "unpaid" | i18n }} - -
-

{{ "transactions" | i18n }}

-

{{ "noTransactions" | i18n }}

- - - - - - - - - -
{{ t.createdDate | date: "mediumDate" }} - - {{ "chargeNoun" | i18n }} - - {{ "refundNoun" | i18n }} - - - {{ t.details }} - - {{ t.amount | currency: "$" }} -
-* {{ "chargesStatement" | i18n: "BITWARDEN" }} + {{ t.amount | currency: "$" }} + + + + + * {{ "chargesStatement" | i18n: "BITWARDEN" }} + diff --git a/apps/web/src/app/billing/shared/billing-shared.module.ts b/apps/web/src/app/billing/shared/billing-shared.module.ts index 2f773870aa..65a651b73d 100644 --- a/apps/web/src/app/billing/shared/billing-shared.module.ts +++ b/apps/web/src/app/billing/shared/billing-shared.module.ts @@ -4,7 +4,7 @@ import { HeaderModule } from "../../layouts/header/header.module"; import { SharedModule } from "../../shared"; import { AddCreditComponent } from "./add-credit.component"; -import { AdjustPaymentComponent } from "./adjust-payment.component"; +import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog.component"; import { AdjustStorageComponent } from "./adjust-storage.component"; import { BillingHistoryComponent } from "./billing-history.component"; import { OffboardingSurveyComponent } from "./offboarding-survey.component"; @@ -18,7 +18,7 @@ import { UpdateLicenseComponent } from "./update-license.component"; imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule], declarations: [ AddCreditComponent, - AdjustPaymentComponent, + AdjustPaymentDialogComponent, AdjustStorageComponent, BillingHistoryComponent, PaymentMethodComponent, diff --git a/apps/web/src/app/billing/shared/payment-method.component.html b/apps/web/src/app/billing/shared/payment-method.component.html index cfe98178b0..5f78294fa6 100644 --- a/apps/web/src/app/billing/shared/payment-method.component.html +++ b/apps/web/src/app/billing/shared/payment-method.component.html @@ -15,7 +15,7 @@
- +

{{ "paymentMethod" | i18n }}

@@ -102,23 +102,9 @@ {{ paymentSource.description }}

- - -

{{ "paymentChargedWithUnpaidSubscription" | i18n }}

{{ "taxInformation" | i18n }}

diff --git a/apps/web/src/app/billing/shared/payment-method.component.ts b/apps/web/src/app/billing/shared/payment-method.component.ts index d2b65968c3..fee97cb912 100644 --- a/apps/web/src/app/billing/shared/payment-method.component.ts +++ b/apps/web/src/app/billing/shared/payment-method.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit, ViewChild } from "@angular/core"; import { FormBuilder, FormControl, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; +import { lastValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -14,6 +15,10 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { DialogService } from "@bitwarden/components"; +import { + AdjustPaymentDialogResult, + openAdjustPaymentDialog, +} from "./adjust-payment-dialog.component"; import { TaxInfoComponent } from "./tax-info.component"; @Component({ @@ -25,7 +30,6 @@ export class PaymentMethodComponent implements OnInit { loading = false; firstLoaded = false; - showAdjustPayment = false; showAddCredit = false; billing: BillingPaymentResponse; org: OrganizationSubscriptionResponse; @@ -120,18 +124,18 @@ export class PaymentMethodComponent implements OnInit { } } - changePayment() { - this.showAdjustPayment = true; - } - - closePayment(load: boolean) { - this.showAdjustPayment = false; - if (load) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); + changePayment = async () => { + const dialogRef = openAdjustPaymentDialog(this.dialogService, { + data: { + organizationId: this.organizationId, + currentType: this.paymentSource !== null ? this.paymentSource.type : null, + }, + }); + const result = await lastValueFrom(dialogRef.closed); + if (result === AdjustPaymentDialogResult.Adjusted) { + await this.load(); } - } + }; async verifyBank() { if (this.loading || !this.forOrganization) { diff --git a/apps/web/src/app/core/broadcaster-messaging.service.ts b/apps/web/src/app/core/broadcaster-messaging.service.ts deleted file mode 100644 index 7c8e4eef43..0000000000 --- a/apps/web/src/app/core/broadcaster-messaging.service.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; - -@Injectable() -export class BroadcasterMessagingService implements MessagingService { - constructor(private broadcasterService: BroadcasterService) {} - - send(subscriber: string, arg: any = {}) { - const message = Object.assign({}, { command: subscriber }, arg); - this.broadcasterService.send(message); - } -} diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 9d53bc39f0..a274764756 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -22,7 +22,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; @@ -51,7 +50,6 @@ import { WebStorageServiceProvider } from "../platform/web-storage-service.provi import { WindowStorageService } from "../platform/window-storage.service"; import { CollectionAdminService } from "../vault/core/collection-admin.service"; -import { BroadcasterMessagingService } from "./broadcaster-messaging.service"; import { EventService } from "./event.service"; import { InitService } from "./init.service"; import { ModalService } from "./modal.service"; @@ -117,11 +115,6 @@ const safeProviders: SafeProvider[] = [ useClass: WebPlatformUtilsService, useAngularDecorators: true, }), - safeProvider({ - provide: MessagingServiceAbstraction, - useClass: BroadcasterMessagingService, - useAngularDecorators: true, - }), safeProvider({ provide: ModalServiceAbstraction, useClass: ModalService, diff --git a/apps/web/src/app/core/init.service.ts b/apps/web/src/app/core/init.service.ts index d5576d3bf7..dab6ed5e3d 100644 --- a/apps/web/src/app/core/init.service.ts +++ b/apps/web/src/app/core/init.service.ts @@ -11,6 +11,7 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt. import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; +import { UserKeyInitService } from "@bitwarden/common/platform/services/user-key-init.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service"; @@ -27,12 +28,14 @@ export class InitService { private cryptoService: CryptoServiceAbstraction, private themingService: AbstractThemingService, private encryptService: EncryptService, + private userKeyInitService: UserKeyInitService, @Inject(DOCUMENT) private document: Document, ) {} init() { return async () => { await this.stateService.init(); + this.userKeyInitService.listenForActiveUserChangesToSetUserKey(); setTimeout(() => this.notificationsService.init(), 3000); await this.vaultTimeoutService.init(true); diff --git a/apps/web/src/app/core/state/state.service.ts b/apps/web/src/app/core/state/state.service.ts index 54e456d34c..1ae62d8591 100644 --- a/apps/web/src/app/core/state/state.service.ts +++ b/apps/web/src/app/core/state/state.service.ts @@ -18,7 +18,6 @@ import { StateFactory } from "@bitwarden/common/platform/factories/state-factory import { StorageOptions } from "@bitwarden/common/platform/models/domain/storage-options"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { StateService as BaseStateService } from "@bitwarden/common/platform/services/state.service"; -import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; import { Account } from "./account"; import { GlobalState } from "./global-state"; @@ -57,19 +56,6 @@ export class StateService extends BaseStateService { await super.addAccount(account); } - async getEncryptedCiphers(options?: StorageOptions): Promise<{ [id: string]: CipherData }> { - options = this.reconcileOptions(options, await this.defaultInMemoryOptions()); - return await super.getEncryptedCiphers(options); - } - - async setEncryptedCiphers( - value: { [id: string]: CipherData }, - options?: StorageOptions, - ): Promise { - options = this.reconcileOptions(options, await this.defaultInMemoryOptions()); - return await super.setEncryptedCiphers(value, options); - } - override async getLastSync(options?: StorageOptions): Promise { options = this.reconcileOptions(options, await this.defaultInMemoryOptions()); return await super.getLastSync(options); diff --git a/apps/web/src/app/layouts/header/web-header.component.html b/apps/web/src/app/layouts/header/web-header.component.html index 9346763a47..e24013de6f 100644 --- a/apps/web/src/app/layouts/header/web-header.component.html +++ b/apps/web/src/app/layouts/header/web-header.component.html @@ -4,10 +4,19 @@ *ngIf=" (unassignedItemsBannerEnabled$ | async) && (unassignedItemsBannerService.showBanner$ | async) && - (unassignedItemsBannerService.bannerText$ | async) + !(unassignedItemsBannerService.loading$ | async) " > {{ unassignedItemsBannerService.bannerText$ | async | i18n }} + {{ "unassignedItemsBannerCTAPartOne" | i18n }} + {{ "adminConsole" | i18n }} + {{ "unassignedItemsBannerCTAPartTwo" | i18n }} { alert("Clicked on badge"); - } + }; } export default { @@ -31,7 +31,7 @@ export default { }, }, { - provide: MessagingService, + provide: MessageSender, useFactory: () => { return new MockMessagingService(); }, diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html index 5ddabf0557..ae22d89f7f 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html @@ -6,6 +6,7 @@ [disabled]="disabled" [checked]="checked" (change)="$event ? this.checkedToggled.next() : null" + [attr.aria-label]="'vaultItemSelect' | i18n" /> diff --git a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html index a6d7854267..d03b6dcc38 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html @@ -7,6 +7,7 @@ [disabled]="disabled" [checked]="checked" (change)="$event ? this.checkedToggled.next() : null" + [attr.aria-label]="'collectionItemSelect' | i18n" /> @@ -48,7 +49,7 @@ > -

+

{{ permissionText }}

diff --git a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.ts b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.ts index 666bec7a1a..8bf7779f88 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.ts @@ -6,6 +6,7 @@ import { CollectionView } from "@bitwarden/common/vault/models/view/collection.v import { GroupView } from "../../../admin-console/organizations/core"; import { CollectionAdminView } from "../../core/views/collection-admin.view"; +import { Unassigned } from "../../individual-vault/vault-filter/shared/models/routed-vault-filter.model"; import { convertToPermission, @@ -52,8 +53,8 @@ export class VaultCollectionRowComponent { } get permissionText() { - if (!(this.collection as CollectionAdminView).assigned) { - return "-"; + if (this.collection.id != Unassigned && !(this.collection as CollectionAdminView).assigned) { + return this.i18nService.t("noAccess"); } else { const permissionList = getPermissionList(this.organization?.flexibleCollections); return this.i18nService.t( @@ -62,6 +63,13 @@ export class VaultCollectionRowComponent { } } + get permissionTooltip() { + if (this.collection.id == Unassigned) { + return this.i18nService.t("collectionAdminConsoleManaged"); + } + return ""; + } + protected edit() { this.onEvent.next({ type: "editCollection", item: this.collection }); } diff --git a/apps/web/src/app/vault/core/collection-admin.service.ts b/apps/web/src/app/vault/core/collection-admin.service.ts index 74f825e1ac..7f78ab214a 100644 --- a/apps/web/src/app/vault/core/collection-admin.service.ts +++ b/apps/web/src/app/vault/core/collection-admin.service.ts @@ -124,6 +124,9 @@ export class CollectionAdminService { view.groups = c.groups; view.users = c.users; view.assigned = c.assigned; + view.readOnly = c.readOnly; + view.hidePasswords = c.hidePasswords; + view.manage = c.manage; } return view; diff --git a/apps/web/src/app/vault/individual-vault/collections.component.html b/apps/web/src/app/vault/individual-vault/collections.component.html index 46bd94b316..5adf9c4e58 100644 --- a/apps/web/src/app/vault/individual-vault/collections.component.html +++ b/apps/web/src/app/vault/individual-vault/collections.component.html @@ -1,64 +1,52 @@ -