diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9eace676f4f..9665d40b07e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -100,12 +100,19 @@ apps/desktop/src/services/native-message-handler.service.ts @bitwarden/team-auto ## Component Library ## .storybook @bitwarden/team-design-system libs/components @bitwarden/team-design-system -apps/browser/src/platform/popup/layout @bitwarden/team-design-system -apps/web/src/app/layouts @bitwarden/team-design-system +apps/browser/src/platform/popup/layout @bitwarden/team-design-system +apps/web/src/app/layouts @bitwarden/team-design-system ## Desktop native module ## apps/desktop/desktop_native @bitwarden/team-platform-dev +## Key management team files ## +apps/desktop/src/key-management @bitwarden/team-key-management-dev +apps/web/src/key-management @bitwarden/team-key-management-dev +apps/browser/src/key-management @bitwarden/team-key-management-dev +apps/cli/src/key-management @bitwarden/team-key-management-dev +libs/common/src/key-management @bitwarden/team-key-management-dev + ## DevOps team files ## /.github/workflows @bitwarden/dept-devops diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5b4cd52ac8e..8d4067c1167 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -138,7 +138,12 @@ jobs: eval "$(printf '\n' | /usr/bin/gnome-keyring-daemon --start)" cargo test -- --test-threads=1 - - name: Test Windows / macOS - if: ${{ matrix.os!='ubuntu-latest' }} + - name: Test macOS + if: ${{ matrix.os=='macos-latest' }} working-directory: ./apps/desktop/desktop_native run: cargo test -- --test-threads=1 + + - name: Test Windows + if: ${{ matrix.os=='windows-latest'}} + working-directory: ./apps/desktop/desktop_native/core + run: cargo test -- --test-threads=1 diff --git a/apps/browser/package.json b/apps/browser/package.json index b76a1e84126..4d008b684cb 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2024.9.0", + "version": "2024.9.1", "scripts": { "build": "cross-env MANIFEST_VERSION=3 webpack", "build:mv2": "webpack", diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 0090391fdba..e1fcf72c811 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 341406772ee..a6b2a35e133 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Müəssisə siyasət tələbləri bu ayara tətbiq edildi" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Xarakter sayını göstər" }, diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 1a00659bbfb..cf14c8e32a0 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index bfc4fc04a17..9d90293b0ca 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Изискванията на политиката за големи компании бяха приложени към тази настройка" }, + "fileSavedToDevice": { + "message": "Файлът е запазен на устройството. Можете да го намерите в мястото за сваляния на устройството." + }, "showCharacterCount": { "message": "Показване на броя знаци" }, diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index fd084ed359d..53a3ceb1a65 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index b80293fd04c..13cd7e7f54a 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 2b7bc2198de..160b625c4e3 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index c5d80fc8b33..bb3c81e0785 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -1797,13 +1797,13 @@ "message": "Nejsou k dispozici žádná hesla." }, "clearHistory": { - "message": "Clear history" + "message": "Vymazat historii" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Žádná hesla k zobrazení" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "Nedávno jste nevygenerovali heslo" }, "remove": { "message": "Odebrat" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Na toto nastavení byly uplatněny požadavky podnikových zásad" }, + "fileSavedToDevice": { + "message": "Soubor byl uložen. Můžete jej nalézt ve stažené složce v zařízení." + }, "showCharacterCount": { "message": "Zobrazit počet znaků" }, diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 128894e754e..e43e81e1e50 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 301e987dfd2..8771b323cb7 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -1797,13 +1797,13 @@ "message": "Der er ingen kodeord at vise." }, "clearHistory": { - "message": "Clear history" + "message": "Ryd historik" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Ingen adgangskoder at vise" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "Der er ikke genereret nogen adgangskode for nylig" }, "remove": { "message": "Fjern" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Virksomhedspolitikkrav er anvendt på denne indstilling" }, + "fileSavedToDevice": { + "message": "Fil gemt på enheden. Håndtér fra enhedens downloads." + }, "showCharacterCount": { "message": "Vis tegnantal" }, diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 8abbdae03ff..3af4de85e13 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Unternehmens-Richtlinienanforderungen wurden auf diese Einstellung angewandt" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Zeichenanzahl anzeigen" }, diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 5b1ee6011bc..cc008a4949b 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Οι απαιτήσεις της πολιτικής για επιχειρήσεις έχουν εφαρμοστεί σε αυτήν τη ρύθμιση" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 084445d8ce6..c3347b89095 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -194,6 +194,18 @@ "addItem": { "message": "Add item" }, + "accountEmail": { + "message": "Account email" + }, + "requestHint": { + "message": "Request hint" + }, + "requestPasswordHint": { + "message": "Request password hint" + }, + "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { + "message": "Enter your account email address and your password hint will be sent to you" + }, "passwordHint": { "message": "Password hint" }, @@ -910,7 +922,7 @@ "message": "List card items on the Tab page for easy autofill." }, "showIdentitiesInVaultView": { - "message": "Show identifies as Autofill suggestions on Vault view" + "message": "Show identities as Autofill suggestions on Vault view" }, "showIdentitiesCurrentTab": { "message": "Show identities on Tab page" @@ -1945,7 +1957,7 @@ }, "lock": { "message": "Lock", - "description": "Verb form: to make secure or inaccesible by" + "description": "Verb form: to make secure or inaccessible by" }, "trash": { "message": "Trash", @@ -4327,6 +4339,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 153c3987da3..a8efa278e58 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 9c0200ab42a..c90115ee537 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index fc98d5e2778..572372bafc2 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 43d894be6c4..947b9b0966c 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 175c5d305df..1cbb17bcf61 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 45a01a0946a..224fa273005 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 07281c5df41..927225fce52 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Yrityskäytännön vaatimuksia on sovellettu tähän asetukseen" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index cc1fb49eb96..6a8288365e0 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 66cd4bf6f85..f80a5959ef3 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -1797,13 +1797,13 @@ "message": "Aucun mot de passe à afficher." }, "clearHistory": { - "message": "Clear history" + "message": "Effacer l'historique" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Aucun mot de passe à afficher" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "Vous n'avez pas généré de mot de passe récemment" }, "remove": { "message": "Supprimer" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Les exigences de la politique d'entreprise ont été appliquées à ce paramètre" }, + "fileSavedToDevice": { + "message": "Fichier enregistré sur l'appareil. Gérez à partir des téléchargements de votre appareil." + }, "showCharacterCount": { "message": "Afficher le nombre de caractères" }, diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 50915596072..247542ec192 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 89f6719289d..02a6842efa2 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index b9bfa5b5959..6b4d3922ff2 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "इस सेटिंग पर एंटरप्राइज़ नीति आवश्यकताएँ लागू की गई हैं" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 0c6ca6bef75..88a1f158b3a 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Pravila tvrtke primijenjena su na ovu postavku" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 17c6b7e5e44..ba551e0fa3b 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Erre a beállításra a vállalkozás rendszabály követelmények lettek alkalmazva." }, + "fileSavedToDevice": { + "message": "A fájl mentésre került az eszközre. Kezeljük az eszközről a letöltéseket." + }, "showCharacterCount": { "message": "Karakterszámláló megjelenítése" }, diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 44dff3c8189..b43eddb3fdc 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 3bccac4121c..79c4d3cff74 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -1797,13 +1797,13 @@ "message": "Non ci sono password da mostrare." }, "clearHistory": { - "message": "Clear history" + "message": "Cancella cronologia" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Nessuna password da mostrare" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "Non hai generato una password di recente" }, "remove": { "message": "Rimuovi" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "I requisiti della policy aziendale sono stati applicati a questa impostazione" }, + "fileSavedToDevice": { + "message": "File salvato sul dispositivo. Gestisci dai download del dispositivo." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 40d8cef50c6..393a8311779 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -1797,13 +1797,13 @@ "message": "表示するパスワードがありません" }, "clearHistory": { - "message": "Clear history" + "message": "履歴を消去" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "パスワードがありません" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "最近パスワードを生成していません" }, "remove": { "message": "削除" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "エンタープライズポリシー要件がこの設定に適用されました" }, + "fileSavedToDevice": { + "message": "ファイルをデバイスに保存しました。デバイスのダウンロードで管理できます。" + }, "showCharacterCount": { "message": "文字数を表示" }, diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index f277d76bfab..5e3809e00df 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 81f6bacb96c..338d70eaf58 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index e8d36e6fffb..aa902ec5d7e 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 74c9103943b..cd2fff44a41 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index ebcf61ffd6d..c60b8b7b211 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 0a847e86f65..2f1f123020d 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -1797,13 +1797,13 @@ "message": "Nav paroļu, ko parādīt." }, "clearHistory": { - "message": "Clear history" + "message": "Notīrīt vēsturi" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Nav paroļu, ko parādīt" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "Pēdējā laikā nav izveidota neviena parole" }, "remove": { "message": "Noņemt" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Šim iestatījumam tika piemērotas uzņēmējdarbības nosacījumu prasības" }, + "fileSavedToDevice": { + "message": "Datne saglabāta ierīcē. Tā ir atrodama ierīces lejupielāžu mapē." + }, "showCharacterCount": { "message": "Rādīt rakstzīmju skaitu" }, diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 8a27d914ea9..bcec218774d 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 890fc14fdd0..0ecb9907504 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 81f6bacb96c..338d70eaf58 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index a5d503d3987..37486833e94 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 81f6bacb96c..338d70eaf58 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 791fd7b2374..435df8ecb4e 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -1797,13 +1797,13 @@ "message": "Er zijn geen wachtwoorden om weer te geven." }, "clearHistory": { - "message": "Clear history" + "message": "Geschiedenis wissen" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Geen wachtwoorden weer te geven" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "Je hebt onlangs geen wachtwoord gegenereerd" }, "remove": { "message": "Verwijderen" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Bedrijfsbeleidseisen zijn op deze instelling toegepast" }, + "fileSavedToDevice": { + "message": "Bestand op apparaat opgeslagen. Beheer vanaf de downloads op je apparaat." + }, "showCharacterCount": { "message": "Aantal tekens weergeven" }, diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 81f6bacb96c..338d70eaf58 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 81f6bacb96c..338d70eaf58 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 7576f8a0cab..a99655378e5 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 6abab6b104d..a88aae11814 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Os requisitos de política empresarial foram aplicados nesta configuração" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Mostrar contagem de caracteres" }, diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index c516d2d1ed1..75026c9d3e0 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -1797,13 +1797,13 @@ "message": "Não existem palavras-passe para listar." }, "clearHistory": { - "message": "Clear history" + "message": "Limpar histórico" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Não há palavras-passe para mostrar" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "Não gerou nenhuma palavra-passe recentemente" }, "remove": { "message": "Remover" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Os requisitos da política empresarial foram aplicados a esta definição" }, + "fileSavedToDevice": { + "message": "Ficheiro guardado no dispositivo. Gira-o a partir das transferências do seu dispositivo." + }, "showCharacterCount": { "message": "Mostrar contagem de caracteres" }, diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 62dda4fe0f4..989f5e1b2ee 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index fec64ad9ce1..31d419aea60 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "К этой настройке были применены требования корпоративной политики" }, + "fileSavedToDevice": { + "message": "Файл сохранен на устройстве. Управляйте им из загрузок устройства." + }, "showCharacterCount": { "message": "Показать количество символов" }, diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 9b906a57ea3..3b47e77a5bd 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 0f9088dc5e8..c3135bb9fec 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -1797,13 +1797,13 @@ "message": "Neboli nájdené žiadne heslá." }, "clearHistory": { - "message": "Clear history" + "message": "Vymazať históriu" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Žiadne heslá na zobrazenie" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "V poslednej dobe ste negenerovali žiadne heslá" }, "remove": { "message": "Odstrániť" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Na toto nastavenie boli uplatnené požiadavky pravidiel spoločnosti" }, + "fileSavedToDevice": { + "message": "Súbor sa uložil do zariadenia. Spravujte stiahnuté súbory zo zariadenia." + }, "showCharacterCount": { "message": "Zobraziť počítadlo znakov" }, diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index b9f72e0dae7..a9665d631af 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 257478519b1..c9d37830153 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Захтеви политике предузећа су примењени на ово подешавање" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 6c8715a9582..8f8d34f8bad 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -1797,10 +1797,10 @@ "message": "Det finns inga lösenord att lista." }, "clearHistory": { - "message": "Clear history" + "message": "Rensa historik" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Inga lösenord att visa" }, "noRecentlyGeneratedPassword": { "message": "You haven't generated a password recently" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Visa antal tecken" }, diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 81f6bacb96c..338d70eaf58 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 645cfc5e379..0edcb1c9f81 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 9539f49b9f9..02f67a672bc 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -266,7 +266,7 @@ "message": "Bitwarden Authenticator allows you to store authenticator keys and generate TOTP codes for 2-step verification flows. Learn more on the bitwarden.com website" }, "bitwardenSecretsManager": { - "message": "Bitwarden Sır Yöneticisi" + "message": "Bitwarden Secrets Manager" }, "continueToSecretsManagerPageDesc": { "message": "Securely store, manage, and share developer secrets with Bitwarden Secrets Manager. Learn more on the bitwarden.com website." @@ -1797,13 +1797,13 @@ "message": "Listelenecek parola yok." }, "clearHistory": { - "message": "Clear history" + "message": "Geçmişi temizle" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Gösterilecek parola yok" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "Yakın zamanda parola üretmediniz" }, "remove": { "message": "Kaldır" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Bu ayara kurumsal ilke gereksinimleri uygulandı" }, + "fileSavedToDevice": { + "message": "Dosya cihaza kaydedildi. Cihazınızın indirilenler klasöründen yönetebilirsiniz." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 47f5bb486b1..eb7affa78c7 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -1797,13 +1797,13 @@ "message": "Немає паролів." }, "clearHistory": { - "message": "Clear history" + "message": "Очистити історію" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "Немає паролів" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "Ви не генерували паролі останнім часом" }, "remove": { "message": "Вилучити" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "До цього налаштування застосовано вимоги політики компанії" }, + "fileSavedToDevice": { + "message": "Файл збережено на пристрої. Ви можете його знайти у теці завантажень." + }, "showCharacterCount": { "message": "Показати кількість символів" }, diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 559000d519a..1b059e1b9cc 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -14,7 +14,7 @@ "message": "Đăng nhập hoặc tạo tài khoản mới để truy cập kho lưu trữ của bạn." }, "inviteAccepted": { - "message": "Invitation accepted" + "message": "Lời mời được chấp nhận" }, "createAccount": { "message": "Tạo tài khoản" @@ -69,10 +69,10 @@ "message": "Gợi ý mật khẩu chính (tùy chọn)" }, "joinOrganization": { - "message": "Join organization" + "message": "Tham gia tổ chức" }, "finishJoiningThisOrganizationBySettingAMasterPassword": { - "message": "Finish joining this organization by setting a master password." + "message": "Hoàn tất gia nhập tổ chức này bằng cách đặt một mật khẩu chính." }, "tab": { "message": "Tab" @@ -114,19 +114,19 @@ "message": "Sao chép mã bảo mật" }, "copyName": { - "message": "Copy name" + "message": "Sao chép tên" }, "copyCompany": { "message": "Copy company" }, "copySSN": { - "message": "Copy Social Security number" + "message": "Số bảo hiểm xã hội" }, "copyPassportNumber": { - "message": "Copy passport number" + "message": "Sao chép số hộ chiếu" }, "copyLicenseNumber": { - "message": "Copy license number" + "message": "Sao chép số giấy phép" }, "autoFill": { "message": "Tự động điền" @@ -302,10 +302,10 @@ "message": "Chỉnh sửa thư mục" }, "newFolder": { - "message": "New folder" + "message": "Thư mục mới" }, "folderName": { - "message": "Folder name" + "message": "Tên thư mục" }, "folderHintText": { "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" @@ -400,7 +400,7 @@ "description": "deprecated. Use specialCharactersLabel instead." }, "include": { - "message": "Include", + "message": "Bao gồm", "description": "Card header for password generator include block" }, "uppercaseDescription": { @@ -420,7 +420,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "Bao gồm cả số", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -428,7 +428,7 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "Bao gồm các ký tự đặc biệt", "description": "Full description for the password generator special characters checkbox" }, "specialCharactersLabel": { @@ -684,10 +684,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ờ." }, "newAccountCreated2": { - "message": "Your new account has been created!" + "message": "Tài khoản của bạn đã được tạo thành công!" }, "youHaveBeenLoggedIn": { - "message": "You have been logged in!" + "message": "Bạn đã đăng nhập thành công!" }, "youSuccessfullyLoggedIn": { "message": "Bạn đã đăng nhập thành công" @@ -1155,7 +1155,7 @@ "message": "Bạn có thể nâng cấp làm thành viên cao cấp trong kho bitwarden nền web. Bạn có muốn truy cập trang web bây giờ?" }, "premiumPurchaseAlertV2": { - "message": "You can purchase Premium from your account settings on the Bitwarden web app." + "message": "Bạn có thể mua gói Premium từ cài đặt tài khoản trên trang Bitwarden." }, "premiumCurrentMember": { "message": "Bạn là một thành viên cao cấp!" @@ -1176,7 +1176,7 @@ } }, "premiumPriceV2": { - "message": "All for just $PRICE$ per year!", + "message": "Tất cả chỉ với $PRICE$ /năm!", "placeholders": { "price": { "content": "$1", @@ -1797,7 +1797,7 @@ "message": "Không có mật khẩu để liệt kê." }, "clearHistory": { - "message": "Clear history" + "message": "Xóa lịch sử" }, "noPasswordsToShow": { "message": "No passwords to show" @@ -1900,10 +1900,10 @@ "message": "Các chính sách của tổ chức đang ảnh hưởng đến cài đặt tạo mật khẩu của bạn." }, "passwordGenerator": { - "message": "Password generator" + "message": "Trình tạo mật khẩu" }, "usernameGenerator": { - "message": "Username generator" + "message": "Bộ tạo tên người dùng" }, "useThisPassword": { "message": "Use this password" @@ -2246,7 +2246,7 @@ "message": "Mật khẩu đã được bảo vệ" }, "copyLink": { - "message": "Copy link" + "message": "Sao chép liên kết" }, "copySendLink": { "message": "Sao chép liên kết mục Gửi", @@ -2477,7 +2477,7 @@ "message": "Mật khẩu chính của bạn không đáp ứng chính sách tổ chức của bạn. Để truy cập kho, bạn phải cập nhật mật khẩu chính của mình ngay bây giờ. Việc tiếp tục sẽ đăng xuất bạn khỏi phiên hiện tại và bắt buộc đăng nhập lại. Các phiên hoạt động trên các thiết bị khác có thể tiếp tục duy trì hoạt động trong tối đa một giờ." }, "tdeDisabledMasterPasswordRequired": { - "message": "Your organization has disabled trusted device encryption. Please set a master password to access your vault." + "message": "Tổ chức của bạn đã vô hiệu hóa mã hóa bằng thiết bị đáng tin cậy. Vui lòng đặt mật khẩu chính để truy cập Kho của bạn." }, "resetPasswordPolicyAutoEnroll": { "message": "Đăng ký tự động" @@ -2936,7 +2936,7 @@ "message": "Thay đổi phím tắt" }, "autofillKeyboardManagerShortcutsLabel": { - "message": "Manage shortcuts" + "message": "Quản lý các lối tắt" }, "autofillShortcut": { "message": "Phím tắt tự động điền" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index de2bfdf63fb..04db7f8f6ec 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -1797,13 +1797,13 @@ "message": "没有可列出的密码。" }, "clearHistory": { - "message": "Clear history" + "message": "清除历史记录" }, "noPasswordsToShow": { - "message": "No passwords to show" + "message": "没有可显示的密码" }, "noRecentlyGeneratedPassword": { - "message": "You haven't generated a password recently" + "message": "您最近还没有生成过密码" }, "remove": { "message": "移除" @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "企业策略要求已应用于此设置" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "显示字符计数" }, diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 8f6673017c0..d61e831d738 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -4309,6 +4309,9 @@ "enterprisePolicyRequirementsApplied": { "message": "Enterprise policy requirements have been applied to this setting" }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." + }, "showCharacterCount": { "message": "Show character count" }, diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index f916555fef2..2d7f46fa59a 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2024.9.0", + "version": "2024.9.1", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 12d501ba9ed..5e132774e6e 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "102.0", "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2024.9.0", + "version": "2024.9.1", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index f76c94a076b..b8bdb19aeb8 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -12,17 +12,20 @@ import { } from "@bitwarden/angular/auth/guards"; import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { generatorSwap } from "@bitwarden/angular/tools/generator/generator-swap"; +import { extensionRefreshRedirect } from "@bitwarden/angular/utils/extension-refresh-redirect"; import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, LoginComponentV2, LoginSecondaryContentComponent, + PasswordHintComponent, RegistrationFinishComponent, RegistrationStartComponent, RegistrationStartSecondaryComponent, RegistrationStartSecondaryComponentData, SetPasswordJitComponent, + UserLockIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -103,7 +106,6 @@ import { TrashComponent } from "../vault/popup/settings/trash.component"; import { VaultSettingsV2Component } from "../vault/popup/settings/vault-settings-v2.component"; import { VaultSettingsComponent } from "../vault/popup/settings/vault-settings.component"; -import { extensionRefreshRedirect } from "./extension-refresh-route-utils"; import { debounceNavigationGuard } from "./services/debounce-navigation.service"; import { TabsV2Component } from "./tabs-v2.component"; import { TabsComponent } from "./tabs.component"; @@ -212,12 +214,6 @@ const routes: Routes = [ canActivate: [unauthGuardFn(unauthRouteOverrides)], data: { state: "register" }, }, - { - path: "hint", - component: HintComponent, - canActivate: [unauthGuardFn(unauthRouteOverrides)], - data: { state: "hint" }, - }, { path: "environment", component: EnvironmentComponent, @@ -384,6 +380,41 @@ const routes: Routes = [ canActivate: [authGuard], data: { state: "update-temp-password" }, }, + ...unauthUiRefreshSwap( + HintComponent, + ExtensionAnonLayoutWrapperComponent, + { + path: "hint", + canActivate: [unauthGuardFn(unauthRouteOverrides)], + data: { + state: "hint", + }, + }, + { + path: "", + children: [ + { + path: "hint", + canActivate: [unauthGuardFn(unauthRouteOverrides)], + data: { + pageTitle: "requestPasswordHint", + pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou", + pageIcon: UserLockIcon, + showBackButton: true, + state: "hint", + }, + children: [ + { path: "", component: PasswordHintComponent }, + { + path: "", + component: EnvironmentSelectorComponent, + outlet: "environment-selector", + }, + ], + }, + ], + }, + ), ...unauthUiRefreshSwap( LoginComponent, ExtensionAnonLayoutWrapperComponent, @@ -401,7 +432,7 @@ const routes: Routes = [ data: { pageTitle: "logInToBitwarden", state: "login", - }, // TODO-rr-bw: add `satisfies DataProperties & ExtensionAnonLayoutWrapperData + }, // TODO-rr-bw: add `satisfies DataProperties & ExtensionAnonLayoutWrapperData} children: [ { path: "", component: LoginComponentV2 }, { path: "", component: LoginSecondaryContentComponent, outlet: "secondary" }, diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 84a9ee264c3..477152fff85 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -247,7 +247,7 @@ export class AppComponent implements OnInit, OnDestroy { // Displaying toasts isn't super useful on the popup due to the reloads we do. // However, it is visible for a moment on the FF sidebar logout. private async displayLogoutReason(logoutReason: LogoutReason) { - let toastOptions: ToastOptions; + let toastOptions: ToastOptions | null = null; switch (logoutReason) { case "invalidSecurityStamp": case "sessionExpired": { @@ -260,6 +260,11 @@ export class AppComponent implements OnInit, OnDestroy { } } + if (toastOptions == null) { + // We don't have anything to show for this particular reason + return; + } + this.toastService.showToast(toastOptions); } } diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index f8d3c691051..f14dafacb70 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -20,6 +20,7 @@ import { AvatarModule, ButtonModule, ToastModule } from "@bitwarden/components"; import { AccountComponent } from "../auth/popup/account-switching/account.component"; import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component"; import { EnvironmentComponent } from "../auth/popup/environment.component"; +import { ExtensionAnonLayoutWrapperComponent } from "../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component"; import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; import { LockComponent } from "../auth/popup/lock.component"; @@ -131,6 +132,7 @@ import "../platform/popup/locales"; HeaderComponent, UserVerificationDialogComponent, CurrentAccountComponent, + ExtensionAnonLayoutWrapperComponent, ], declarations: [ ActionButtonsComponent, diff --git a/apps/browser/src/tools/popup/generator/credential-generator.component.html b/apps/browser/src/tools/popup/generator/credential-generator.component.html index 0b43b0e257c..d8c49da5b1a 100644 --- a/apps/browser/src/tools/popup/generator/credential-generator.component.html +++ b/apps/browser/src/tools/popup/generator/credential-generator.component.html @@ -1 +1 @@ - + diff --git a/apps/browser/src/tools/popup/generator/credential-generator.component.ts b/apps/browser/src/tools/popup/generator/credential-generator.component.ts index f07affd2374..16938fbe79f 100644 --- a/apps/browser/src/tools/popup/generator/credential-generator.component.ts +++ b/apps/browser/src/tools/popup/generator/credential-generator.component.ts @@ -1,14 +1,11 @@ import { Component } from "@angular/core"; -import { - PassphraseSettingsComponent, - PasswordSettingsComponent, -} from "@bitwarden/generator-components"; +import { PasswordGeneratorComponent } from "@bitwarden/generator-components"; @Component({ standalone: true, selector: "credential-generator", templateUrl: "credential-generator.component.html", - imports: [PassphraseSettingsComponent, PasswordSettingsComponent], + imports: [PasswordGeneratorComponent], }) export class CredentialGeneratorComponent {} diff --git a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.html b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.html index affe9ffc04e..9322ab5113e 100644 --- a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.html +++ b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.html @@ -12,7 +12,7 @@ - + diff --git a/apps/web/src/app/auth/emergency-access/accept/accept-emergency.component.ts b/apps/web/src/app/auth/emergency-access/accept/accept-emergency.component.ts index d5ca41c42c5..cd11bc72f37 100644 --- a/apps/web/src/app/auth/emergency-access/accept/accept-emergency.component.ts +++ b/apps/web/src/app/auth/emergency-access/accept/accept-emergency.component.ts @@ -1,5 +1,6 @@ import { Component } from "@angular/core"; import { ActivatedRoute, Params, Router } from "@angular/router"; +import { firstValueFrom } from "rxjs"; import { RegisterRouteService } from "@bitwarden/auth/common"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -18,6 +19,8 @@ import { EmergencyAccessService } from "../services/emergency-access.service"; }) export class AcceptEmergencyComponent extends BaseAcceptComponent { name: string; + emergencyAccessId: string; + acceptEmergencyAccessInviteToken: string; protected requiredParameters: string[] = ["id", "name", "email", "token"]; protected failedShortMessage = "emergencyInviteAcceptFailedShort"; @@ -55,5 +58,36 @@ export class AcceptEmergencyComponent extends BaseAcceptComponent { // Fix URL encoding of space issue with Angular this.name = this.name.replace(/\+/g, " "); } + + if (qParams.id) { + this.emergencyAccessId = qParams.id; + } + + if (qParams.token) { + this.acceptEmergencyAccessInviteToken = qParams.token; + } + } + + async register() { + let queryParams: Params; + let registerRoute = await firstValueFrom(this.registerRoute$); + if (registerRoute === "/register") { + queryParams = { + email: this.email, + }; + } else if (registerRoute === "/signup") { + // We have to override the base component route as we don't need users to + // complete email verification if they are coming directly an emailed invite. + registerRoute = "/finish-signup"; + queryParams = { + email: this.email, + acceptEmergencyAccessInviteToken: this.acceptEmergencyAccessInviteToken, + emergencyAccessId: this.emergencyAccessId, + }; + } + + await this.router.navigate([registerRoute], { + queryParams: queryParams, + }); } } diff --git a/apps/web/src/app/auth/migrate-encryption/migrate-legacy-encryption.component.ts b/apps/web/src/app/auth/migrate-encryption/migrate-legacy-encryption.component.ts index 68eaae618fd..6c894f4fa85 100644 --- a/apps/web/src/app/auth/migrate-encryption/migrate-legacy-encryption.component.ts +++ b/apps/web/src/app/auth/migrate-encryption/migrate-legacy-encryption.component.ts @@ -7,9 +7,9 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se 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 { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { ToastService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { SharedModule } from "../../shared"; import { UserKeyRotationModule } from "../key-rotation/user-key-rotation.module"; @@ -31,12 +31,13 @@ export class MigrateFromLegacyEncryptionComponent { private accountService: AccountService, private keyRotationService: UserKeyRotationService, private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, private cryptoService: CryptoService, private messagingService: MessagingService, private logService: LogService, private syncService: SyncService, private toastService: ToastService, + private dialogService: DialogService, + private folderApiService: FolderApiServiceAbstraction, ) {} submit = async () => { @@ -69,6 +70,23 @@ export class MigrateFromLegacyEncryptionComponent { }); this.messagingService.send("logout"); } catch (e) { + // If the error is due to missing folders, we can delete all folders and try again + if (e.message === "All existing folders must be included in the rotation.") { + const deleteFolders = await this.dialogService.openSimpleDialog({ + type: "warning", + title: { key: "encryptionKeyUpdateCannotProceed" }, + content: { key: "keyUpdateFoldersFailed" }, + acceptButtonText: { key: "ok" }, + cancelButtonText: { key: "cancel" }, + }); + + if (deleteFolders) { + await this.folderApiService.deleteAll(); + await this.syncService.fullSync(true, true); + await this.submit(); + return; + } + } this.logService.error(e); throw e; } diff --git a/apps/web/src/app/auth/organization-invite/accept-organization.component.ts b/apps/web/src/app/auth/organization-invite/accept-organization.component.ts index 6013688df22..82f24974e25 100644 --- a/apps/web/src/app/auth/organization-invite/accept-organization.component.ts +++ b/apps/web/src/app/auth/organization-invite/accept-organization.component.ts @@ -99,8 +99,7 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { email: invite.email, }; } else if (registerRoute === "/signup") { - // We have to override the base component route b/c it is correct for other components - // that extend the base accept comp. We don't need users to complete email verification + // We have to override the base component route as we don't need users to complete email verification // if they are coming directly from an emailed org invite. registerRoute = "/finish-signup"; queryParams = { diff --git a/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts b/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts index 9d763886fb4..e0a6f6c53d5 100644 --- a/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts @@ -12,7 +12,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; @Component({ selector: "emergency-access-attachments", @@ -34,6 +34,7 @@ export class EmergencyAccessAttachmentsComponent extends BaseAttachmentsComponen dialogService: DialogService, billingAccountProfileStateService: BillingAccountProfileStateService, accountService: AccountService, + toastService: ToastService, ) { super( cipherService, @@ -48,6 +49,7 @@ export class EmergencyAccessAttachmentsComponent extends BaseAttachmentsComponen dialogService, billingAccountProfileStateService, accountService, + toastService, ); } diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html index 71a4ff119c2..f927a7ca613 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.html @@ -49,7 +49,15 @@

{{ "paymentType" | i18n }}

- + +
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 7dad7effeea..9b9e6f0cd05 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 @@ -11,12 +11,14 @@ import { } from "@bitwarden/common/billing/abstractions/organization-billing.service"; import { PaymentMethodType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.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 { ToastService } from "@bitwarden/components"; import { BillingSharedModule, PaymentComponent, TaxInfoComponent } from "../../shared"; +import { PaymentV2Component } from "../../shared/payment/payment-v2.component"; export type TrialOrganizationType = Exclude; @@ -49,6 +51,7 @@ export enum SubscriptionProduct { }) export class TrialBillingStepComponent implements OnInit { @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; + @ViewChild(PaymentV2Component) paymentV2Component: PaymentV2Component; @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; @Input() organizationInfo: OrganizationInfo; @Input() subscriptionProduct: SubscriptionProduct = SubscriptionProduct.PasswordManager; @@ -69,17 +72,22 @@ export class TrialBillingStepComponent implements OnInit { annualPlan?: PlanResponse; monthlyPlan?: PlanResponse; + deprecateStripeSourcesAPI: boolean; + constructor( private apiService: ApiService, + private configService: ConfigService, private i18nService: I18nService, private formBuilder: FormBuilder, private messagingService: MessagingService, private organizationBillingService: OrganizationBillingService, - private platformUtilsService: PlatformUtilsService, private toastService: ToastService, ) {} async ngOnInit(): Promise { + this.deprecateStripeSourcesAPI = await this.configService.getFeatureFlag( + FeatureFlag.AC2476_DeprecateStripeSourcesAPI, + ); const plans = await this.apiService.getPlans(); this.applicablePlans = plans.data.filter(this.isApplicable); this.annualPlan = this.findPlanFor(SubscriptionCadence.Annual); @@ -114,13 +122,23 @@ export class TrialBillingStepComponent implements OnInit { } protected changedCountry() { - this.paymentComponent.hideBank = this.taxInfoComponent.taxFormGroup.value.country !== "US"; - if ( - this.paymentComponent.hideBank && - this.paymentComponent.method === PaymentMethodType.BankAccount - ) { - this.paymentComponent.method = PaymentMethodType.Card; - this.paymentComponent.changeMethod(); + if (this.deprecateStripeSourcesAPI) { + this.paymentV2Component.showBankAccount = this.taxInfoComponent.country === "US"; + if ( + !this.paymentV2Component.showBankAccount && + this.paymentV2Component.selected === PaymentMethodType.BankAccount + ) { + this.paymentV2Component.select(PaymentMethodType.Card); + } + } else { + this.paymentComponent.hideBank = this.taxInfoComponent.taxFormGroup.value.country !== "US"; + if ( + this.paymentComponent.hideBank && + this.paymentComponent.method === PaymentMethodType.BankAccount + ) { + this.paymentComponent.method = PaymentMethodType.Card; + this.paymentComponent.changeMethod(); + } } } @@ -141,7 +159,15 @@ export class TrialBillingStepComponent implements OnInit { private async createOrganization(): Promise { const planResponse = this.findPlanFor(this.formGroup.value.cadence); - const paymentMethod = await this.paymentComponent.createPaymentToken(); + + let paymentMethod: [string, PaymentMethodType]; + + if (this.deprecateStripeSourcesAPI) { + const { type, token } = await this.paymentV2Component.tokenize(); + paymentMethod = [token, type]; + } else { + paymentMethod = await this.paymentComponent.createPaymentToken(); + } const organization: OrganizationInformation = { name: this.organizationInfo.name, diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html index 02071f5aa8c..766646003ba 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html @@ -7,31 +7,37 @@

{{ "upgradePlans" | i18n }}

{{ "selectAPlan" | i18n }} +
{{ "upgradeDiscount" | i18n - : (this.discountPercentageFromSub > 0 - ? discountPercentageFromSub - : this.discountPercentage) + : (selectedInterval === planIntervals.Annually + ? discountPercentageFromSub + this.discountPercentage + : this.discountPercentageFromSub) }} - +
{{ planInterval.name }} @@ -40,6 +46,7 @@
+
{{ "recommended" | i18n }}
-

+

{{ selectableProduct.nameLocalizationKey | i18n }} - + {{ "current" | i18n }}

@@ -133,10 +151,13 @@ else nonEnterprisePlans " > -

+

{{ "bitwardenPasswordManager" | i18n }}

-

{{ "enterprisePlanUpgradeMessage" | i18n }}

+

{{ "enterprisePlanUpgradeMessage" | i18n }}

  • @@ -157,7 +178,10 @@
-

+

{{ "bitwardenSecretsManager" | i18n }}

    @@ -195,25 +219,25 @@

    {{ "bitwardenPasswordManager" | i18n }}

    {{ "teamsPlanUpgradeMessage" | i18n }}

    {{ "familyPlanUpgradeMessage" | i18n }}

    • @@ -247,7 +271,7 @@

    {{ "secretsManagerSubInfo" | i18n }} - {{ "secretsManagerWithFreePasswordManagerInfo" | i18n }} + {{ "secretsManagerComplimentaryPasswordManager" | i18n }}
    @@ -308,9 +332,14 @@

    + + - {{ 0 }} + {{ storageGb }} {{ "additionalStorageGbMessage" | i18n }} × - {{ selectedPlan.PasswordManager.additionalStoragePricePerGb | currency: "$" }} + {{ additionalStoragePriceMonthly(selectedPlan) | currency: "$" }} /{{ "year" | i18n }} - {{ 0 | currency: "$" }} + {{ additionalStorageTotal(selectedPlan) | currency: "$" }} +

    + +

    + + + {{ + "providerDiscount" + | i18n: this.discountPercentageFromSub + this.discountPercentage + | lowercase + }} + + {{ + calculateTotalAppliedDiscount( + passwordManagerSeatTotal(selectedPlan) + additionalStorageTotal(selectedPlan) + ) | currency: "$" + }} +

    @@ -450,18 +497,40 @@ bitTypography="body2" *ngIf=" selectedPlan?.SecretsManager?.hasAdditionalServiceAccountOption && - additionalServiceAccount + additionalServiceAccount > 0 " > {{ additionalServiceAccount }} - {{ "additionalStorageGbMessage" | i18n }} + {{ "serviceAccounts" | i18n | lowercase }} × {{ selectedPlan?.SecretsManager?.additionalPricePerServiceAccount | currency: "$" }} /{{ "month" | i18n }} {{ additionalServiceAccountTotal(selectedPlan) | currency: "$" }}

    + +

    + + + {{ + "providerDiscount" + | i18n: this.discountPercentageFromSub + this.discountPercentage + | lowercase + }} + + {{ + calculateTotalAppliedDiscount( + additionalServiceAccountTotal(selectedPlan) + + secretsManagerSeatTotal(selectedPlan, sub.smSeats) + ) | currency: "$" + }} + +

    @@ -503,19 +572,38 @@

    - {{ 0 }} + {{ storageGb }} {{ "additionalStorageGbMessage" | i18n }} × - {{ selectedPlan.PasswordManager.additionalStoragePricePerGb | currency: "$" }} + {{ additionalStoragePriceMonthly(selectedPlan) | currency: "$" }} /{{ "month" | i18n }} - {{ 0 | currency: "$" }} + {{ + storageGb * selectedPlan.PasswordManager.additionalStoragePricePerGb | currency: "$" + }} +

    + +

    + + + {{ "providerDiscount" | i18n: this.discountPercentageFromSub | lowercase }} + + {{ calculateTotalAppliedDiscount(total) | currency: "$" }} +

    @@ -562,18 +650,41 @@ bitTypography="body2" *ngIf=" selectedPlan.SecretsManager.hasAdditionalServiceAccountOption && - additionalServiceAccount + additionalServiceAccount > 0 " > {{ additionalServiceAccount }} - {{ "additionalStorageGbMessage" | i18n }} + {{ "serviceAccounts" | i18n | lowercase }} × {{ selectedPlan.SecretsManager.additionalPricePerServiceAccount | currency: "$" }} /{{ "month" | i18n }} {{ additionalServiceAccountTotal(selectedPlan) | currency: "$" }}

    + +

    + + + {{ "providerDiscount" | i18n: this.discountPercentageFromSub | lowercase }} + + {{ + additionalServiceAccountTotal(selectedPlan) + + secretsManagerSeatTotal(selectedPlan, sub?.smSeats) | currency: "$" + }} + +

@@ -628,18 +739,40 @@ bitTypography="body2" *ngIf=" selectedPlan.SecretsManager.hasAdditionalServiceAccountOption && - additionalServiceAccount + additionalServiceAccount > 0 " > {{ additionalServiceAccount }} - {{ "additionalStorageGbMessage" | i18n }} + {{ "serviceAccounts" | i18n }} × {{ selectedPlan.SecretsManager.additionalPricePerServiceAccount | currency: "$" }} /{{ "month" | i18n }} {{ additionalServiceAccountTotal(selectedPlan) | currency: "$" }}

+ +

+ + + {{ + "providerDiscount" + | i18n: this.discountPercentageFromSub + this.discountPercentage + | lowercase + }} + + {{ + calculateTotalAppliedDiscount( + additionalServiceAccountTotal(selectedPlan) + + secretsManagerSeatTotal(selectedPlan, sub.smSeats) + ) | currency: "$" + }} + +

{{ "passwordManager" | i18n }} @@ -650,7 +783,7 @@ *ngIf="selectedPlan.PasswordManager.basePrice" > - {{ organization.seats }} + {{ sub?.seats }} {{ "members" | i18n }} × {{ (selectedPlan.isAnnual @@ -681,7 +814,7 @@ {{ "additionalUsers" | i18n }}: - {{ organization.seats || 0 }}  + {{ sub?.seats || 0 }}  {{ "members" | i18n }} × {{ selectedPlan.PasswordManager.seatPrice | currency: "$" }} @@ -743,12 +876,12 @@ bitTypography="body2" *ngIf=" selectedPlan.SecretsManager.hasAdditionalServiceAccountOption && - additionalServiceAccount + additionalServiceAccount > 0 " > {{ additionalServiceAccount }} - {{ "additionalStorageGbMessage" | i18n }} + {{ "serviceAccounts" | i18n }} × {{ selectedPlan.SecretsManager.additionalPricePerServiceAccount | currency: "$" }} /{{ "month" | i18n }} @@ -782,7 +915,7 @@ {{ "additionalUsers" | i18n }}: - {{ organization.seats }}  + {{ sub?.seats }}  {{ "members" | i18n }} × {{ selectedPlan.PasswordManager.seatPrice | currency: "$" }} @@ -798,6 +931,46 @@

+ +
+ +

+ + + {{ + "providerDiscount" + | i18n: this.discountPercentageFromSub + this.discountPercentage + | lowercase + }} + + {{ + calculateTotalAppliedDiscount(total) | currency: "$" + }} + + + + {{ "providerDiscount" | i18n: this.discountPercentageFromSub | lowercase }} + + {{ calculateTotalAppliedDiscount(total) | currency: "$" }} + +

+
+

{{ total | currency: "USD" : "$" }} - / {{ selectedPlanInterval | i18n }} + + / {{ selectedPlanInterval | i18n }}

diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts index a359c281ebe..dc9f6cce688 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts @@ -21,22 +21,27 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/models/request/organization-upgrade.request"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { PaymentMethodType, + PlanInterval, PlanType, ProductTierType, - PlanInterval, } from "@bitwarden/common/billing/enums"; import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request"; +import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request"; import { BillingResponse } from "@bitwarden/common/billing/models/response/billing.response"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService, ToastService } from "@bitwarden/components"; +import { PaymentV2Component } from "../shared/payment/payment-v2.component"; import { PaymentComponent } from "../shared/payment/payment.component"; import { TaxInfoComponent } from "../shared/tax-info.component"; @@ -80,6 +85,7 @@ interface OnSuccessArgs { }) export class ChangePlanDialogComponent implements OnInit, OnDestroy { @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; + @ViewChild(PaymentV2Component) paymentV2Component: PaymentV2Component; @ViewChild(TaxInfoComponent) taxComponent: TaxInfoComponent; @Input() acceptingSponsorship = false; @@ -155,6 +161,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { totalOpened: boolean = false; currentPlan: PlanResponse; + deprecateStripeSourcesAPI: boolean; + private destroy$ = new Subject(); constructor( @@ -171,9 +179,15 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { private messagingService: MessagingService, private formBuilder: FormBuilder, private organizationApiService: OrganizationApiServiceAbstraction, + private configService: ConfigService, + private billingApiService: BillingApiServiceAbstraction, ) {} async ngOnInit(): Promise { + this.deprecateStripeSourcesAPI = await this.configService.getFeatureFlag( + FeatureFlag.AC2476_DeprecateStripeSourcesAPI, + ); + if (this.dialogParams.organizationId) { this.currentPlanName = this.resolvePlanName(this.dialogParams.productTierType); this.sub = @@ -232,27 +246,28 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { selected: false, }, ]; - this.discountPercentageFromSub = this.sub?.customerDiscount?.percentOff; + this.discountPercentageFromSub = this.isSecretsManagerTrial() + ? 0 + : (this.sub?.customerDiscount?.percentOff ?? 0); this.setInitialPlanSelection(); this.loading = false; } setInitialPlanSelection() { - if ( - this.organization.useSecretsManager && - this.currentPlan.productTier == ProductTierType.Free - ) { - this.selectPlan(this.getPlanByType(ProductTierType.Teams)); - } else { - this.selectPlan(this.getPlanByType(ProductTierType.Enterprise)); - } + this.selectPlan(this.getPlanByType(ProductTierType.Enterprise)); } getPlanByType(productTier: ProductTierType) { return this.selectableProducts.find((product) => product.productTier === productTier); } + secretsManagerTrialDiscount() { + return this.sub?.customerDiscount?.appliesTo?.includes("sm-standalone") + ? this.discountPercentage + : this.discountPercentageFromSub + this.discountPercentage; + } + isSecretsManagerTrial(): boolean { return ( this.sub?.subscription?.items?.some((item) => @@ -262,14 +277,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } planTypeChanged() { - if ( - this.organization.useSecretsManager && - this.currentPlan.productTier == ProductTierType.Free - ) { - this.selectPlan(this.getPlanByType(ProductTierType.Teams)); - } else { - this.selectPlan(this.getPlanByType(ProductTierType.Enterprise)); - } + this.selectPlan(this.getPlanByType(ProductTierType.Enterprise)); } updateInterval(event: number) { @@ -290,6 +298,10 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { ]; } + optimizedNgForRender(index: number) { + return index; + } + protected getPlanCardContainerClasses(plan: PlanResponse, index: number) { let cardState: PlanCardState; @@ -356,6 +368,10 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { ) { return; } + + if (plan === this.currentPlan) { + return; + } this.selectedPlan = plan; this.formGroup.patchValue({ productTier: plan.productTier }); } @@ -449,6 +465,10 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { return result; } + get storageGb() { + return this.sub?.maxStorageGb - 1; + } + passwordManagerSeatTotal(plan: PlanResponse): number { if (!plan.PasswordManager.hasAdditionalSeatsOption || this.isSecretsManagerTrial()) { return 0; @@ -472,13 +492,22 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } return ( - plan.PasswordManager.additionalStoragePricePerGb * - Math.abs(this.organization.maxStorageGb || 0) + plan.PasswordManager.additionalStoragePricePerGb * Math.abs(this.sub?.maxStorageGb - 1 || 0) ); } + additionalStoragePriceMonthly(selectedPlan: PlanResponse) { + if (!selectedPlan.isAnnual) { + return selectedPlan.PasswordManager.additionalStoragePricePerGb; + } + return selectedPlan.PasswordManager.additionalStoragePricePerGb / 12; + } + additionalServiceAccountTotal(plan: PlanResponse): number { - if (!plan.SecretsManager.hasAdditionalServiceAccountOption || this.additionalServiceAccount) { + if ( + !plan.SecretsManager.hasAdditionalServiceAccountOption || + this.additionalServiceAccount == 0 + ) { return 0; } @@ -520,14 +549,23 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { if (this.selectedPlan.productTier === ProductTierType.Families) { return this.selectedPlan.PasswordManager.baseSeats; } - return this.organization.seats; + return this.sub?.seats; } get total() { if (this.organization.useSecretsManager) { - return this.passwordManagerSubtotal + this.secretsManagerSubtotal + this.taxCharges || 0; + return ( + this.passwordManagerSubtotal + + this.additionalStorageTotal(this.selectedPlan) + + this.secretsManagerSubtotal + + this.taxCharges || 0 + ); } - return this.passwordManagerSubtotal + this.taxCharges || 0; + return ( + this.passwordManagerSubtotal + + this.additionalStorageTotal(this.selectedPlan) + + this.taxCharges || 0 + ); } get teamsStarterPlanIsAvailable() { @@ -535,7 +573,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } get additionalServiceAccount() { - const baseServiceAccount = this.selectedPlan.SecretsManager?.baseServiceAccount || 0; + const baseServiceAccount = this.currentPlan.SecretsManager?.baseServiceAccount || 0; const usedServiceAccounts = this.sub?.smServiceAccounts || 0; const additionalServiceAccounts = baseServiceAccount - usedServiceAccounts; @@ -579,7 +617,16 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } changedCountry() { - if (this.paymentComponent && this.taxComponent) { + if (this.deprecateStripeSourcesAPI && this.paymentV2Component && this.taxComponent) { + this.paymentV2Component.showBankAccount = this.taxComponent.country === "US"; + + if ( + !this.paymentV2Component.showBankAccount && + this.paymentV2Component.selected === PaymentMethodType.BankAccount + ) { + this.paymentV2Component.select(PaymentMethodType.Card); + } + } else if (this.paymentComponent && this.taxComponent) { this.paymentComponent!.hideBank = this.taxComponent?.taxFormGroup?.value.country !== "US"; // Bank Account payments are only available for US customers if ( @@ -600,7 +647,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { const doSubmit = async (): Promise => { let orgId: string = null; - orgId = await this.updateOrganization(orgId); + orgId = await this.updateOrganization(); this.toastService.showToast({ variant: "success", title: null, @@ -613,7 +660,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { if (!this.acceptingSponsorship && !this.isInTrialFlow) { // 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(["/organizations/" + orgId]); + this.router.navigate(["/organizations/" + orgId + "/members"]); } if (this.isInTrialFlow) { @@ -634,10 +681,14 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { this.dialogRef.close(); }; - private async updateOrganization(orgId: string) { + private async updateOrganization() { const request = new OrganizationUpgradeRequest(); if (this.selectedPlan.productTier !== ProductTierType.Families) { - request.additionalSeats = this.organization.seats; + request.additionalSeats = this.sub?.seats; + } + if (this.sub?.maxStorageGb > this.selectedPlan.PasswordManager.baseStorageGb) { + request.additionalStorageGb = + this.sub?.maxStorageGb - this.selectedPlan.PasswordManager.baseStorageGb; } request.premiumAccessAddon = this.selectedPlan.PasswordManager.hasPremiumAccessOption && @@ -652,13 +703,33 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { this.buildSecretsManagerRequest(request); if (this.upgradeRequiresPaymentMethod || this.showPayment) { - const tokenResult = await this.paymentComponent.createPaymentToken(); - const paymentRequest = new PaymentRequest(); - paymentRequest.paymentToken = tokenResult[0]; - paymentRequest.paymentMethodType = tokenResult[1]; - paymentRequest.country = this.taxComponent.taxFormGroup?.value.country; - paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode; - await this.organizationApiService.updatePayment(this.organizationId, paymentRequest); + if (this.deprecateStripeSourcesAPI) { + const tokenizedPaymentSource = await this.paymentV2Component.tokenize(); + const updatePaymentMethodRequest = new UpdatePaymentMethodRequest(); + updatePaymentMethodRequest.paymentSource = tokenizedPaymentSource; + updatePaymentMethodRequest.taxInformation = { + country: this.taxComponent.country, + postalCode: this.taxComponent.postalCode, + taxId: this.taxComponent.taxId, + line1: this.taxComponent.line1, + line2: this.taxComponent.line2, + city: this.taxComponent.city, + state: this.taxComponent.state, + }; + + await this.billingApiService.updateOrganizationPaymentMethod( + this.organizationId, + updatePaymentMethodRequest, + ); + } else { + const tokenResult = await this.paymentComponent.createPaymentToken(); + const paymentRequest = new PaymentRequest(); + paymentRequest.paymentToken = tokenResult[0]; + paymentRequest.paymentMethodType = tokenResult[1]; + paymentRequest.country = this.taxComponent.taxFormGroup?.value.country; + paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode; + await this.organizationApiService.updatePayment(this.organizationId, paymentRequest); + } } // Backfill pub/priv key if necessary @@ -705,6 +776,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { request.additionalSmSeats = this.organization.seats; } else { request.additionalSmSeats = this.sub?.smSeats; + request.additionalServiceAccounts = this.additionalServiceAccount; } } @@ -749,6 +821,16 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { this.totalOpened = !this.totalOpened; } + calculateTotalAppliedDiscount(total: number) { + const discountPercent = + this.selectedInterval == PlanInterval.Annually + ? this.discountPercentage + this.discountPercentageFromSub + : this.discountPercentageFromSub; + + const discountedTotal = total / (1 - discountPercent / 100); + return discountedTotal; + } + get paymentSourceClasses() { if (this.billing.paymentSource == null) { return []; 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 25c8c547b2b..341324c4a2a 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 @@ -69,14 +69,25 @@ >
- {{ - "details" | i18n - }} + {{ "details" | i18n + }}{{ "providerDiscount" | i18n: customerDiscount?.percentOff }} - + {{ i.productName | i18n }} - {{ i.name }} {{ i.quantity > 1 ? "×" + i.quantity : "" }} @ {{ i.amount | currency: "$" }} @@ -91,7 +102,19 @@ {{ "freeForOneYear" | i18n }} - {{ i.quantity * i.amount | currency: "$" }} /{{ i.interval | i18n }} +
+ + {{ i.quantity * i.amount | currency: "$" }} /{{ i.interval | i18n }} + + {{ + calculateTotalAppliedDiscount(i.quantity * i.amount) | currency: "$" + }} + / {{ "year" | i18n }} +
@@ -112,7 +135,7 @@ -
+
-
- +
+ {{ + "number" | i18n + }}
@@ -38,26 +40,26 @@ height="32" />
-
- +
+ {{ + "expiration" | i18n + }}
-
-
- +
+ + {{ "securityCodeSlashCVV" | i18n }} -
+
diff --git a/apps/web/src/app/billing/shared/payment/payment.component.ts b/apps/web/src/app/billing/shared/payment/payment.component.ts index 03269c70a5a..ab81a602d23 100644 --- a/apps/web/src/app/billing/shared/payment/payment.component.ts +++ b/apps/web/src/app/billing/shared/payment/payment.component.ts @@ -5,15 +5,19 @@ import { Subject, takeUntil } from "rxjs"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PaymentMethodType } from "@bitwarden/common/billing/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SharedModule } from "../../../shared"; +import { PaymentLabelV2 } from "./payment-label-v2.component"; + @Component({ selector: "app-payment", templateUrl: "payment.component.html", standalone: true, - imports: [SharedModule], + imports: [SharedModule, PaymentLabelV2], }) export class PaymentComponent implements OnInit, OnDestroy { @Input() showMethods = true; @@ -63,14 +67,15 @@ export class PaymentComponent implements OnInit, OnDestroy { private apiService: ApiService, private logService: LogService, private themingService: AbstractThemingService, + private configService: ConfigService, ) { this.stripeScript = window.document.createElement("script"); this.stripeScript.src = "https://js.stripe.com/v3/?advancedFraudSignals=false"; this.stripeScript.async = true; - this.stripeScript.onload = () => { + this.stripeScript.onload = async () => { this.stripe = (window as any).Stripe(process.env.STRIPE_KEY); this.stripeElements = this.stripe.elements(); - this.setStripeElement(); + await this.setStripeElement(); }; this.btScript = window.document.createElement("script"); this.btScript.src = `scripts/dropin.js?cache=${process.env.CACHE_TAG}`; @@ -187,7 +192,7 @@ export class PaymentComponent implements OnInit, OnDestroy { ); }, 250); } else { - this.setStripeElement(); + void this.setStripeElement(); } } @@ -267,7 +272,17 @@ export class PaymentComponent implements OnInit, OnDestroy { }); } - private setStripeElement() { + private async setStripeElement() { + const extensionRefreshFlag = await this.configService.getFeatureFlag( + FeatureFlag.ExtensionRefresh, + ); + + // Apply unique styles for extension refresh + if (extensionRefreshFlag) { + this.StripeElementStyle.base.fontWeight = "500"; + this.StripeElementClasses.base = "v2"; + } + window.setTimeout(() => { if (this.showMethods && this.method === PaymentMethodType.Card) { if (this.stripeCardNumberElement == null) { diff --git a/apps/web/src/app/billing/shared/tax-info.component.html b/apps/web/src/app/billing/shared/tax-info.component.html index 89bc7438a77..82d5104a53a 100644 --- a/apps/web/src/app/billing/shared/tax-info.component.html +++ b/apps/web/src/app/billing/shared/tax-info.component.html @@ -1,6 +1,6 @@
-
+
{{ "country" | i18n }} @@ -13,7 +13,7 @@
-
+
{{ "zipPostalCode" | i18n }} diff --git a/apps/web/src/app/core/web-file-download.service.ts b/apps/web/src/app/core/web-file-download.service.ts index 743048dc3b9..ad034702a55 100644 --- a/apps/web/src/app/core/web-file-download.service.ts +++ b/apps/web/src/app/core/web-file-download.service.ts @@ -12,14 +12,12 @@ export class WebFileDownloadService implements FileDownloadService { download(request: FileDownloadRequest): void { const builder = new FileDownloadBuilder(request); const a = window.document.createElement("a"); - if (builder.downloadMethod === "save") { - a.download = request.fileName; - } else if (!this.platformUtilsService.isSafari()) { + if (!this.platformUtilsService.isSafari()) { a.rel = "noreferrer"; a.target = "_blank"; } a.href = URL.createObjectURL(builder.blob); - a.style.position = "fixed"; + a.download = request.fileName; window.document.body.appendChild(a); a.click(); window.document.body.removeChild(a); diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 53ffb045541..e8f9b12c2de 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -13,15 +13,17 @@ import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, + PasswordHintComponent, RegistrationFinishComponent, RegistrationStartComponent, RegistrationStartSecondaryComponent, RegistrationStartSecondaryComponentData, SetPasswordJitComponent, - LockIcon, RegistrationLinkExpiredComponent, LoginComponentV2, LoginSecondaryContentComponent, + LockIcon, + UserLockIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -215,6 +217,49 @@ const routes: Routes = [ ], }, ), + ...unauthUiRefreshSwap( + AnonLayoutWrapperComponent, + AnonLayoutWrapperComponent, + { + path: "hint", + canActivate: [unauthGuardFn()], + data: { + pageTitle: "passwordHint", + titleId: "passwordHint", + }, + children: [ + { path: "", component: HintComponent }, + { + path: "", + component: EnvironmentSelectorComponent, + outlet: "environment-selector", + }, + ], + }, + { + path: "", + children: [ + { + path: "hint", + canActivate: [unauthGuardFn()], + data: { + pageTitle: "requestPasswordHint", + pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou", + pageIcon: UserLockIcon, + state: "hint", + }, + children: [ + { path: "", component: PasswordHintComponent }, + { + path: "", + component: EnvironmentSelectorComponent, + outlet: "environment-selector", + }, + ], + }, + ], + }, + ), { path: "", component: AnonLayoutWrapperComponent, @@ -418,25 +463,6 @@ const routes: Routes = [ }, ], }, - { - path: "hint", - canActivate: [unauthGuardFn()], - data: { - pageTitle: "passwordHint", - titleId: "passwordHint", - } satisfies DataProperties & AnonLayoutWrapperData, - children: [ - { - path: "", - component: HintComponent, - }, - { - path: "", - component: EnvironmentSelectorComponent, - outlet: "environment-selector", - }, - ], - }, { path: "remove-password", component: RemovePasswordComponent, 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 524d9dff20b..6a04ff6071d 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 @@ -157,7 +157,7 @@ diff --git a/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts b/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts index d9930d22716..5e64067a826 100644 --- a/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts +++ b/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts @@ -13,11 +13,11 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { CipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { - DialogService, - SimpleDialogOptions, BreadcrumbsModule, + DialogService, MenuModule, SearchModule, + SimpleDialogOptions, } from "@bitwarden/components"; import { HeaderModule } from "../../../layouts/header/header.module"; @@ -88,8 +88,6 @@ export class VaultHeaderComponent implements OnInit { protected CollectionDialogTabType = CollectionDialogTabType; protected organizations$ = this.organizationService.organizations$; - protected restrictProviderAccessFlag = false; - /** * Whether the extension refresh feature flag is enabled. */ @@ -108,9 +106,6 @@ export class VaultHeaderComponent implements OnInit { ) {} async ngOnInit() { - this.restrictProviderAccessFlag = await this.configService.getFeatureFlag( - FeatureFlag.RestrictProviderAccess, - ); this.extensionRefreshEnabled = await this.configService.getFeatureFlag( FeatureFlag.ExtensionRefresh, ); @@ -245,11 +240,7 @@ export class VaultHeaderComponent implements OnInit { } get canCreateCipher(): boolean { - if ( - this.organization?.isProviderUser && - this.restrictProviderAccessFlag && - !this.organization?.isMember - ) { + if (this.organization?.isProviderUser && !this.organization?.isMember) { return false; } return true; diff --git a/apps/web/src/app/vault/org-vault/vault.component.html b/apps/web/src/app/vault/org-vault/vault.component.html index 63160abea69..ed7f6b1e3ed 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.html +++ b/apps/web/src/app/vault/org-vault/vault.component.html @@ -70,7 +70,6 @@ [viewingOrgVault]="true" [addAccessStatus]="addAccessStatus$ | async" [addAccessToggle]="showAddAccessToggle" - [restrictProviderAccess]="restrictProviderAccessEnabled" > diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts index f9652fe08ca..add1ecbe3e5 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -43,9 +43,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { EventType } from "@bitwarden/common/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.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"; @@ -172,17 +170,8 @@ export class VaultComponent implements OnInit, OnDestroy { protected orgRevokedUsers: OrganizationUserUserDetailsResponse[]; - private _restrictProviderAccessFlagEnabled: boolean; - protected get restrictProviderAccessEnabled(): boolean { - return this._restrictProviderAccessFlagEnabled; - } - protected get hideVaultFilters(): boolean { - return ( - this.restrictProviderAccessEnabled && - this.organization?.isProviderUser && - !this.organization?.isMember - ); + return this.organization?.isProviderUser && !this.organization?.isMember; } private searchText$ = new Subject(); @@ -218,7 +207,6 @@ export class VaultComponent implements OnInit, OnDestroy { private apiService: ApiService, private collectionService: CollectionService, private organizationUserApiService: OrganizationUserApiService, - protected configService: ConfigService, private toastService: ToastService, private accountService: AccountService, ) {} @@ -230,10 +218,6 @@ export class VaultComponent implements OnInit, OnDestroy { : "trashCleanupWarning", ); - this._restrictProviderAccessFlagEnabled = await this.configService.getFeatureFlag( - FeatureFlag.RestrictProviderAccess, - ); - const filter$ = this.routedVaultFilterService.filter$; const organizationId$ = filter$.pipe( map((filter) => filter.organizationId), @@ -325,7 +309,7 @@ export class VaultComponent implements OnInit, OnDestroy { this.editableCollections$ = this.allCollectionsWithoutUnassigned$.pipe( map((collections) => { // Users that can edit all ciphers can implicitly add to / edit within any collection - if (this.organization.canEditAllCiphers(this.restrictProviderAccessEnabled)) { + if (this.organization.canEditAllCiphers) { return collections; } // The user is only allowed to add/edit items to assigned collections that are not readonly @@ -362,16 +346,12 @@ export class VaultComponent implements OnInit, OnDestroy { // Restricted providers (who are not members) do not have access org cipher endpoint below // Return early to avoid 404 response - if ( - this.restrictProviderAccessEnabled && - !organization.isMember && - organization.isProviderUser - ) { + if (!organization.isMember && organization.isProviderUser) { return []; } // If the user can edit all ciphers for the organization then fetch them ALL. - if (organization.canEditAllCiphers(this.restrictProviderAccessEnabled)) { + if (organization.canEditAllCiphers) { ciphers = await this.cipherService.getAllFromApiForOrganization(organization.id); } else { // Otherwise, only fetch ciphers they have access to (includes unassigned for admins). @@ -476,11 +456,8 @@ export class VaultComponent implements OnInit, OnDestroy { ]).pipe( map(([filter, collection, organization]) => { return ( - (filter.collectionId === Unassigned && - !organization.canEditUnassignedCiphers(this.restrictProviderAccessEnabled)) || - (!organization.canEditAllCiphers(this.restrictProviderAccessEnabled) && - collection != undefined && - !collection.node.assigned) + (filter.collectionId === Unassigned && !organization.canEditUnassignedCiphers) || + (!organization.canEditAllCiphers && collection != undefined && !collection.node.assigned) ); }), shareReplay({ refCount: true, bufferSize: 1 }), @@ -525,7 +502,7 @@ export class VaultComponent implements OnInit, OnDestroy { } const canEditCipher = - organization.canEditAllCiphers(this.restrictProviderAccessEnabled) || + organization.canEditAllCiphers || (await firstValueFrom(allCipherMap$))[cipherId] != undefined; if (canEditCipher) { @@ -758,15 +735,9 @@ export class VaultComponent implements OnInit, OnDestroy { this.allCollectionsWithoutUnassigned$.pipe( map((c) => { return c.sort((a, b) => { - if ( - a.canEditItems(this.organization, this.restrictProviderAccessEnabled) && - !b.canEditItems(this.organization, this.restrictProviderAccessEnabled) - ) { + if (a.canEditItems(this.organization) && !b.canEditItems(this.organization)) { return -1; - } else if ( - !a.canEditItems(this.organization, this.restrictProviderAccessEnabled) && - b.canEditItems(this.organization, this.restrictProviderAccessEnabled) - ) { + } else if (!a.canEditItems(this.organization) && b.canEditItems(this.organization)) { return 1; } else { return a.name.localeCompare(b.name); @@ -1001,9 +972,7 @@ export class VaultComponent implements OnInit, OnDestroy { const unassignedCiphers: string[] = []; // If user has edit all Access no need to check for unassigned ciphers - const canEditAll = this.organization.canEditAllCiphers(this.restrictProviderAccessEnabled); - - if (canEditAll) { + if (this.organization.canEditAllCiphers) { ciphers.map((cipher) => { editAccessCiphers.push(cipher.id); }); @@ -1042,7 +1011,7 @@ export class VaultComponent implements OnInit, OnDestroy { } async deleteCipher(c: CipherView): Promise { - if (!c.edit && !this.organization.canEditAllCiphers(this.restrictProviderAccessEnabled)) { + if (!c.edit && !this.organization.canEditAllCiphers) { this.showMissingPermissionsError(); return; } @@ -1146,9 +1115,7 @@ export class VaultComponent implements OnInit, OnDestroy { const canDeleteCollections = collections == null || collections.every((c) => c.canDelete(organization)); const canDeleteCiphers = - ciphers == null || - ciphers.every((c) => c.edit) || - this.organization.canEditAllCiphers(this.restrictProviderAccessEnabled); + ciphers == null || ciphers.every((c) => c.edit) || this.organization.canEditAllCiphers; if (!canDeleteCiphers || !canDeleteCollections) { this.showMissingPermissionsError(); @@ -1351,8 +1318,7 @@ export class VaultComponent implements OnInit, OnDestroy { } protected deleteCipherWithServer(id: string, permanent: boolean, isUnassigned: boolean) { - const asAdmin = - this.organization?.canEditAllCiphers(this.restrictProviderAccessEnabled) || isUnassigned; + const asAdmin = this.organization?.canEditAllCiphers || isUnassigned; return permanent ? this.cipherService.deleteWithServer(id, asAdmin) : this.cipherService.softDeleteWithServer(id, asAdmin); diff --git a/apps/web/src/connectors/redirect.html b/apps/web/src/connectors/redirect.html new file mode 100644 index 00000000000..13b05fb17e2 --- /dev/null +++ b/apps/web/src/connectors/redirect.html @@ -0,0 +1,29 @@ + + + + + + + + Bitwarden Web vault + + + + + + + + + +
+ Bitwarden +
+ +
+
+ + diff --git a/apps/web/src/connectors/redirect.ts b/apps/web/src/connectors/redirect.ts new file mode 100644 index 00000000000..82bd273fad3 --- /dev/null +++ b/apps/web/src/connectors/redirect.ts @@ -0,0 +1,17 @@ +// This redirect connector is used to redirect users to the correct URL after they have been sent here from an email link. +// The fragment contains the information needed to redirect the user to the correct page. +// This is required because android app links couldn't properly handle the angular hash based route we originally had in the email link. +window.addEventListener("load", () => { + // ex: https://vault.bitwarden.com/redirect-connector.html#finish-signup?token=fakeToken&email=example%40example.com&fromEmail=true + const currentUrl = new URL(window.location.href); + + // Get the fragment (everything after the #) + const fragment = currentUrl.hash.substring(1); // Remove the leading # + + if (!fragment) { + throw new Error("No fragment found in URL. Cannot determine redirect."); + } + + const newUrl = `${window.location.origin}/#/${fragment}`; + window.location.href = newUrl; +}); diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index f5f39bd0223..da3e29eed8a 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Sekerheidskode (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identiteitnaam" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopieer waarde", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Sleutel bygewerk" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Gekose" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Eienaarskap" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index b8747656223..7c159370110 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "رمز الأمان (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "اسم الهوية" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "نسخ القيمة", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index b19ac2c3913..94dc8052f52 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Güvənlik kodu (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Kimlik adı" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Dəyəri kopyala", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Açar güncəlləndi" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Seçildi" }, + "recommended": { + "message": "Tövsiyə edilən" + }, "ownership": { "message": "Sahiblik" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Qruplar, kolleksiyalar və kolleksiya elementləri arasında təşkilat üzvlərinin müraciətini yoxlanışdan keçirin. CSV xaricə köçürməsi, kolleksiya icazələri və hesab konfiqurasiyaları haqqında məlumatlar da daxil olmaqla hər bir üzv üçün detallı məlumat təqdim edir." }, + "memberAccessReportNoCollection": { + "message": "(Kolleksiya yoxdur)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Kolleksiya icazəsi yoxdur)" + }, + "memberAccessReportNoGroup": { + "message": "(Qrup yoxdur)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Açıq" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Bağlı" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Açıq" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Bağlı" + }, "higherKDFIterations": { "message": "Daha yüksək KDF iterasiyaları, ana parolunuzu təcavüzkar tərəfindən \"brute force\" hücumuna qarşı qorumağa kömək edir." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Daha çox üzv dəvət etmək və əlavə Bitwarden özəlliklərinə müraciət əldə etmək üçün planınızı yüksəldin" + "upgradePlans": { + "message": "Üzv dəvət etmək və güclü təhlükəsizlik özəlliklərini təcrübə etmək üçün planınızı yüksəldin." }, "upgradeDiscount": { "message": "$AMOUNT$% endirim", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Daha böyük bizneslər üçün qabaqcıl imkanlar" + "enterprisePlanUpgradeMessage": { + "message": "Daha böyük təşkilatlar üçün qabaqcıl imkanlar" }, - "upgradeTeamsMessage": { - "message": "Güclü təhlükəsizlik axtaran bizneslər" + "teamsPlanUpgradeMessage": { + "message": "Böyüyən komandalar üçün dayanıqlı qoruma" }, "teamsInviteMessage": { "message": "Limitsiz üzv dəvət et" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Qrupları və istifadəçiləri bir kataloqdan sinxronlaşdır" }, - "upgradeFamilyMessage": { - "message": "Ailə və dostlarla paylaş" + "familyPlanUpgradeMessage": { + "message": "Ailənizin giriş məlumatlarını güvəndə saxlayın" }, "accessToPremiumFeatures": { "message": "Premium özəlliklərə müraciət" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Parol Meneceri" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Tamamlayıcı bir illik Parol Meneceri abunəliyiniz seçilmiş plana yüksəldiləcək. Ödənişsiz dövr bitənə qədər sizdən ödəniş alınmayacaq." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Hər kəsə açıq API", diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 340b7fb1f4e..21f6754deb7 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Код бяспекі (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Імя пасведчання" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Скапіяваць значэнне", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Ключ абноўлены" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Выбрана" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Уладальнік" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index dda600ce091..9c0315406ff 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Код за сигурност" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Име на самоличността" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Копиране на стойността", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Ключът е обновен" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Избрано" }, + "recommended": { + "message": "Препоръчано" + }, "ownership": { "message": "Собственост" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Направете проверка на достъпа на членовете до групи, колекции и елементи в колекциите. Изнасянето на на данните като файл CSV предоставя подробна разбивка по членове, включително информация относно правата за колекции и настройките на регистрациите." }, + "memberAccessReportNoCollection": { + "message": "(Няма колекция)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Няма право за колекции)" + }, + "memberAccessReportNoGroup": { + "message": "(Няма група)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Включено" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Изключено" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Включено" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Изключено" + }, "higherKDFIterations": { "message": "По-високите стойности за броя на повторения на KDF може да защитят главната Ви парола от атаки тип „груба сила“." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Надградете плана си, за да може да поканите повече членове и да получите достъп до още функционалности на Битуорден" + "upgradePlans": { + "message": "Надградете плана си, за да можете да поканите още хора, както и да се възползвате от подобрените защитни функционалности." }, "upgradeDiscount": { "message": "Спестете $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Допълнителни възможности за големи компании" + "enterprisePlanUpgradeMessage": { + "message": "Допълнителни възможности за големи организации" }, - "upgradeTeamsMessage": { - "message": "Компании търсещи подобрена сигурност" + "teamsPlanUpgradeMessage": { + "message": "Подсилена защита за разрастващи се екипи" }, "teamsInviteMessage": { "message": "Можете да каните неограничен брой членове" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Синхронизиране на групите и потребителите с директорийни услуги" }, - "upgradeFamilyMessage": { - "message": "Споделяне със семейство и приятели" + "familyPlanUpgradeMessage": { + "message": "Защитете данните за вписване на семейството си" }, "accessToPremiumFeatures": { "message": "Достъп до платените функционалности" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden — управител на пароли" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "Файлът е запазен на устройството. Можете да го намерите в мястото за сваляния на устройството." }, "publicApi": { "message": "Публичен ППИ", diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index c7112cc6c1a..cdb4a353f64 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "নিরাপত্তা কোড (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "পরিচয়ের নাম" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "মান অনুলিপিত করুন", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 829615cc551..cfafb9990e1 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Sigurnosni Kod (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Ime identiteta" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopirajte Vrijednost", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 26794588127..5aee2022f7d 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Codi de seguretat (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Nom d'identitat" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copia el valor", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Clau actualitzada" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Seleccionat" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Propietat" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Guarda $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 6f7367b8033..84042618a2e 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Bezpečnostní kód (CVC/CVV)" }, + "securityCodeSlashCVV": { + "message": "Bezpečnostní kód (CVC/CVV)" + }, "identityName": { "message": "Název identity" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Kopírování bylo úspěšné" + }, "copyValue": { "message": "Kopírovat hodnotu", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Aktualizace šifrovacího klíče nemůže pokračovat" + }, + "keyUpdateFoldersFailed": { + "message": "Při aktualizaci šifrovacího klíče se nepodařilo dešifrovat Vaše složky. Chcete-li pokračovat v aktualizaci, musí být Vaše složky smazány. Pokud budete pokračovat, nebudou smazány žádné položky trezoru." + }, "keyUpdated": { "message": "Klíč byl aktualizován" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Vybráno" }, + "recommended": { + "message": "Doporučeno" + }, "ownership": { "message": "Vlastnictví" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit přístupu členů organizace ke skupinám, kolekcím a položkám kolekcí. Export CSV poskytuje podrobný rozpis pro jednotlivé členy, včetně informací o oprávněních ke kolekcím a konfiguracích účtů." }, + "memberAccessReportNoCollection": { + "message": "(Žádná kolekce)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Žádné oprávnění ke kolekci)" + }, + "memberAccessReportNoGroup": { + "message": "(Žádná skupina)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Zapnuto" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Vypnuto" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Zapnuto" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Vypnuto" + }, "higherKDFIterations": { "message": "Více iterací KDF Vám může pomoci ochránit hlavní heslo před útočníkem, který by ho vylákal hrubou silou." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Povyšte svůj tarif a pozvěte více členů a získejte přístup k dalším funkcím Bitwardenu" + "upgradePlans": { + "message": "Aktualizujte svůj plán, pozvěte členy a vyzkoušejte výkonné bezpečnostní funkce." }, "upgradeDiscount": { "message": "Ušetřit $AMOUNT$ %", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Pokročilé možnosti pro větší podniky" + "enterprisePlanUpgradeMessage": { + "message": "Pokročilé funkce pro větší organizace" }, - "upgradeTeamsMessage": { - "message": "Podniky hledající silnou bezpečnost" + "teamsPlanUpgradeMessage": { + "message": "Odolná ochrana pro rostoucí týmy" }, "teamsInviteMessage": { "message": "Pozvat neomezený počet členů" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Synchronizace skupin a uživatelů z adresáře" }, - "upgradeFamilyMessage": { - "message": "Sdílení s rodinami a přáteli" + "familyPlanUpgradeMessage": { + "message": "Zabezpečení přihlašovacích údajů v rodině" }, "accessToPremiumFeatures": { "message": "Přístup k prémiovým funkcím" @@ -9043,9 +9079,12 @@ "bitwardenPasswordManager": { "message": "Bitwarden - Správce hesel" }, - "secretsManagerWithFreePasswordManagerInfo": { + "secretsManagerComplimentaryPasswordManager": { "message": "Vaše doplňkové roční předplatné Správce hesel bude aktualizováno na vybraný plán. Poplatek Vám bude účtován až po skončení bezplatného období." }, + "fileSavedToDevice": { + "message": "Soubor byl uložen. Můžete jej nalézt ve stažené složce v zařízení." + }, "publicApi": { "message": "Veřejné API", "description": "The text, 'API', is an acronymn and should not be translated." diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 5e1de3971d7..fe2ce4032ee 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index fae10f4eef0..4cb84814411 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Kortverifikationsværdi (CVC)" }, + "securityCodeSlashCVV": { + "message": "Sikkerhedskode/CVC" + }, "identityName": { "message": "Identitetsnavn" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Kopieret" + }, "copyValue": { "message": "Kopiér værdi", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Krypteringsnøgleopdatering kan ikke fortsætte" + }, + "keyUpdateFoldersFailed": { + "message": "Under opdatering af krypteringsnøglen kunne de relevante mapper ikke dekrypteres. For at fortsætte opdateringen skal mapperne slettes. Ingen boks-emner slettes, hvis der fortsættes." + }, "keyUpdated": { "message": "Nøgle opdateret" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Valgt" }, + "recommended": { + "message": "Anbefalet" + }, "ownership": { "message": "Ejerskab" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Inspicér organisationsmedlemsadgang på tværs af grupper, samlinger og samlingsemner. CSV-eksporten giver en detaljeret opdeling pr. medlem, herunder oplysninger om samlingstilladelser og kontoopsætninger." }, + "memberAccessReportNoCollection": { + "message": "(Ingen Samling)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Ingen Samling-tilladelse)" + }, + "memberAccessReportNoGroup": { + "message": "(Ingen Gruppe)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Til" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Fra" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Til" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Fra" + }, "higherKDFIterations": { "message": "Højere KDF-iterationer kan hjælpe med at beskytte hovedadgangskoden mod brute force-angreb." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Opgradér abonnementstypen for at invitere flere til at blive medlemmer samt opnå adgang til yderligere Bitwarden-funktioner" + "upgradePlans": { + "message": "Opgradér abonnementet for at invitere folk til at blive medlemmer og opleve kraftfulde sikkerhedsfunktioner." }, "upgradeDiscount": { "message": "Spar $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Avancerede muligheder for større virksomheder" + "enterprisePlanUpgradeMessage": { + "message": "Avancerede muligheder til større organisationer" }, - "upgradeTeamsMessage": { - "message": "Virksomheder, som leder efter stærk sikkerhed" + "teamsPlanUpgradeMessage": { + "message": "Modstandsdygtig beskyttelse til voksende teams" }, "teamsInviteMessage": { "message": "Ubegrænset medlemsantal" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Synk grupper og brugere fra et kartotek" }, - "upgradeFamilyMessage": { - "message": "Del med familier og venner" + "familyPlanUpgradeMessage": { + "message": "Sikr familie-logins" }, "accessToPremiumFeatures": { "message": "Adgang til Premium-funktioner" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Adgangskodehåndtering" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Det komplementære et års abonnement på Adgangskodehåndtering opgraderes til den valgte abonnementstype. Betaling opkræves først efter den gratis periode." + "secretsManagerComplimentaryPasswordManager": { + "message": "Det gratis et-års abonnement på Password Manager opgraderes til den valgte abonnementstype. Betaling opkræves først efter endt gratis-periode." + }, + "fileSavedToDevice": { + "message": "Fil gemt på enheden. Håndtér fra enhedens downloads." }, "publicApi": { "message": "Offentlig API", diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 9e83c16860a..1573a381af5 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Kartenprüfnummer (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identitätsname" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Wert kopieren", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Schlüssel aktualisiert" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Ausgewählt" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Eigentümer" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Kontrolliere den Zugriff von Organisationsmitgliedern auf Gruppen, Sammlungen und Sammlungseinträgen. Der CSV-Export bietet eine detaillierte Aufschlüsselung pro Mitglied, einschließlich Informationen über Sammlungsberechtigungen und Kontenkonfigurationen." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Höhere KDF-Iterationen können helfen, dein Master-Passwort vor Brute-Force-Attacken durch einen Angreifer zu schützen." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade deinen Tarif, um mehr Mitglieder einzuladen und Zugriff auf zusätzliche Bitwarden-Funktionen zu erhalten" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "$AMOUNT$% sparen", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Erweiterte Möglichkeiten für größere Unternehmen" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Unternehmen auf der Suche nach leistungsstarker Sicherheit" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Unbegrenzte Mitglieder einladen" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Gruppen und Benutzer aus einem Verzeichnis synchronisieren" }, - "upgradeFamilyMessage": { - "message": "Mit Familien und Freunden teilen" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Zugriff auf Premium Funktionen" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Passwortmanager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Dein kostenloses einjähriges Passwortmanager-Abonnement wird auf den ausgewählten Tarif umgestellt. Dir wird nichts berechnet, bis der kostenlose Zeitraum abgelaufen ist." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Öffentliche API", diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index cfe7d180e52..2fb7adf1ca3 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Κωδικός ασφαλείας (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Όνομα ταυτότητας" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Αντιγραφή τιμής", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Το Κλειδί Ενημερώθηκε" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Επιλεγμένα" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ιδιοκτησία" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index edf09be183e..4827b7f0dce 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -966,6 +972,18 @@ "settings": { "message": "Settings" }, + "accountEmail": { + "message": "Account email" + }, + "requestHint": { + "message": "Request hint" + }, + "requestPasswordHint": { + "message": "Request password hint" + }, + "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { + "message": "Enter your account email address and your password hint will be sent to you" + }, "passwordHint": { "message": "Password hint" }, @@ -3906,6 +3924,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4249,7 +4273,7 @@ }, "lock": { "message": "Lock", - "description": "Verb form: to make secure or inaccesible by" + "description": "Verb form: to make secure or inaccessible by" }, "trash": { "message": "Trash", @@ -6210,7 +6234,7 @@ }, "lastSync": { "message": "Last sync", - "description": "Used as a prefix to indicate the last time a sync occured. Example \"Last sync 1968-11-16 00:00:00\"" + "description": "Used as a prefix to indicate the last time a sync occurred. Example \"Last sync 1968-11-16 00:00:00\"" }, "sponsorshipsSynced": { "message": "Self-hosted sponsorships synced." @@ -6369,57 +6393,57 @@ }, "scim": { "message": "SCIM provisioning", - "description": "The text, 'SCIM', is an acronymn and should not be translated." + "description": "The text, 'SCIM', is an acronym and should not be translated." }, "scimDescription": { "message": "Automatically provision users and groups with your preferred identity provider via SCIM provisioning", - "description": "the text, 'SCIM', is an acronymn and should not be translated." + "description": "the text, 'SCIM', is an acronym and should not be translated." }, "scimEnabledCheckboxDesc": { "message": "Enable SCIM", - "description": "the text, 'SCIM', is an acronymn and should not be translated." + "description": "the text, 'SCIM', is an acronym and should not be translated." }, "scimEnabledCheckboxDescHelpText": { "message": "Set up your preferred identity provider by configuring the URL and SCIM API Key", - "description": "the text, 'SCIM', is an acronymn and should not be translated." + "description": "the text, 'SCIM', is an acronym and should not be translated." }, "scimApiKeyHelperText": { "message": "This API key has access to manage users within your organization. It should be kept secret." }, "copyScimKey": { "message": "Copy the SCIM API key to your clipboard", - "description": "the text, 'SCIM' and 'API', are acronymns and should not be translated." + "description": "the text, 'SCIM' and 'API', are acronyms and should not be translated." }, "rotateScimKey": { "message": "Rotate the SCIM API key", - "description": "the text, 'SCIM' and 'API', are acronymns and should not be translated." + "description": "the text, 'SCIM' and 'API', are acronyms and should not be translated." }, "rotateScimKeyWarning": { "message": "Are you sure you want to rotate the SCIM API Key? The current key will no longer work for any existing integrations.", - "description": "the text, 'SCIM' and 'API', are acronymns and should not be translated." + "description": "the text, 'SCIM' and 'API', are acronyms and should not be translated." }, "rotateKey": { "message": "Rotate key" }, "scimApiKey": { "message": "SCIM API key", - "description": "the text, 'SCIM' and 'API', are acronymns and should not be translated." + "description": "the text, 'SCIM' and 'API', are acronyms and should not be translated." }, "copyScimUrl": { "message": "Copy the SCIM endpoint URL to your clipboard", - "description": "the text, 'SCIM' and 'URL', are acronymns and should not be translated." + "description": "the text, 'SCIM' and 'URL', are acronyms and should not be translated." }, "scimUrl": { "message": "SCIM URL", - "description": "the text, 'SCIM' and 'URL', are acronymns and should not be translated." + "description": "the text, 'SCIM' and 'URL', are acronyms and should not be translated." }, "scimApiKeyRotated": { "message": "SCIM API key successfully rotated", - "description": "the text, 'SCIM' and 'API', are acronymns and should not be translated." + "description": "the text, 'SCIM' and 'API', are acronyms and should not be translated." }, "scimSettingsSaved": { "message": "SCIM settings saved", - "description": "the text, 'SCIM', is an acronymn and should not be translated." + "description": "the text, 'SCIM', is an acronym and should not be translated." }, "inputRequired": { "message": "Input is required." @@ -9079,12 +9103,15 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", - "description": "The text, 'API', is an acronymn and should not be translated." + "description": "The text, 'API', is an acronym and should not be translated." }, "showCharacterCount": { "message": "Show character count" diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index aab1efb7c5e..86b3be94bef 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organisation member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organisations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 7ccd08f86b1..434f01df5a6 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organisation member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organisations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index b7615fe4471..6ada709f4d8 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Sekureca Kodo (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Nomo de la identilo" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopii valoron", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Ŝlosilo ĝisdatigita" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Elektita" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Posedo" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index a9458b47176..2512c1e31bd 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Código de seguridad (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Nombre de la identidad" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copiar valor", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Clave actualizada" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Seleccionado" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Propiedad" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 9a54611285d..f2c83d2b859 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Kaardi turvakood (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identiteedi nimi" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopeeri kirje", "description": "Copy value to clipboard" @@ -1283,7 +1289,7 @@ "message": "Ekspordi" }, "exportFrom": { - "message": "Export from" + "message": "Ekspordi asukohast" }, "exportVault": { "message": "Hoidla sisu eksportimine" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Võti on uuendatud" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Valitud" }, + "recommended": { + "message": "Soovituslik" + }, "ownership": { "message": "Omanik" }, @@ -4148,13 +4163,13 @@ "message": "Klooni" }, "masterPassPolicyTitle": { - "message": "Master password requirements" + "message": "Ülemparooli nõuded" }, "masterPassPolicyDesc": { "message": "Määra minimaalsed ülemparooli tugevuse tingimused." }, "twoStepLoginPolicyTitle": { - "message": "Require two-step login" + "message": "Nõua kahe-astmelist sisselogimist" }, "twoStepLoginPolicyDesc": { "message": "Nõua, et kasutajad seadistaksid oma kontodes kaheastmelise kinnituse." @@ -4217,7 +4232,7 @@ "message": "Minimaalne sõnade arv" }, "overridePasswordTypePolicy": { - "message": "Password Type", + "message": "Parooli Tüüp", "description": "Name of the password generator policy that overrides the user's password/passphrase selection." }, "userPreference": { @@ -4340,10 +4355,10 @@ "message": "Võid nüüd selle vahelehe sulgeda ning jätkata brauseri laienduses." }, "youSuccessfullyLoggedIn": { - "message": "You successfully logged in" + "message": "Edukalt sisse logitud" }, "thisWindowWillCloseIn5Seconds": { - "message": "This window will automatically close in 5 seconds" + "message": "See aken sulgub automaatselt 5 sekundi pärast" }, "youMayCloseThisWindow": { "message": "You may close this window" @@ -5399,7 +5414,7 @@ "message": "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." }, "automaticAppLogin": { - "message": "Automatically log in users for allowed applications" + "message": "Logi kasutajad lubatud rakendustesse automaatselt sisse" }, "automaticAppLoginDesc": { "message": "Login forms will automatically be filled and submitted for apps launched from your configured identity provider." @@ -6674,7 +6689,7 @@ "description": "Notification for the successful editing of a secret." }, "secretCreated": { - "message": "Secret created", + "message": "Uus saladus loodud", "description": "Notification for the successful creation of a secret." }, "newSecret": { @@ -6768,7 +6783,7 @@ "description": "Notification for the successful saving of a project." }, "projectCreated": { - "message": "Project created", + "message": "Projekt loodud", "description": "Notification for the successful creation of a project." }, "projectName": { @@ -6948,7 +6963,7 @@ "description": "Error message indicating that an expiration date for the access token must be set." }, "accessTokenCreatedAndCopied": { - "message": "Access token created and copied to clipboard", + "message": "Juurdepääsuvõti loodud ja lõikelauale kopeeritud", "description": "Notification to inform the user that the access token has been created and copied to the clipboard." }, "revokeAccessToken": { @@ -7816,7 +7831,7 @@ "message": "Selected region flag" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Konto edukalt loodud!" }, "adminApprovalRequested": { "message": "Admin approval requested" @@ -8350,7 +8365,7 @@ "description": "Label for the name of a machine account" }, "machineAccountCreated": { - "message": "Machine account created", + "message": "Konto arvutile loodud", "description": "Notifies that a new machine account has been created" }, "machineAccountUpdated": { @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 091d5c62f93..7b42de91fc0 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Segurtasun-kodea (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identitate izena" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopiatu balioa", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Gakoa eguneratua" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Aukeratuta" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Jabetza" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index c977cc38f8d..a6a31f45895 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "کد امنیتی (cvv)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "نام هویت" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "کپی مقدار", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "کلیدها به‌روز شد" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "انتخاب شده" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "مالکیت" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 6952ef43b22..f47ee7f89ae 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Turvakoodi (CVC/CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Henkilöllisyyden nimi" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopioi arvo", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Avain päivitettiin" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Valittu" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Omistus" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Tarkastele organisaation jäsenten käyttöoikeuksia ryhmiin, kokoelmiin ja kokoelmien kohteisiin. CSV-vienti tuottaa yksityiskohtaisen jäsenkohtaisen erittelyn, joka sisältää kokoelmaoikeudet ja tilimääritykset." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Korkeampi KDF-toistojen määrä vahvistaa pääsalasanasi suojausta väsytyshyökkäyksien varalta." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Päivitä tilauksesi kutsuaksesi lisää jäseniä ja saadaksesi käyttöösi Bitwardenin lisäominaisuuksia" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Säästä $AMOUNT$ %", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Erityisominaisuudet suuryrityksille" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Vahvaa tietoturvaa tavoitteleville yrityksille" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Kutsu rajattomasti jäseniä" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Ryhmien ja käyttäjien synkronointi hakemistosta" }, - "upgradeFamilyMessage": { - "message": "Jaa perheen ja ystävien kanssa" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Premium-ominaisuuksien käyttöoikeus" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Salasanahallinta" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Ilmainen yhden vuoden Salasanahallinta-tilauksesi päivittyy valittuun tilaukseen. Sinua ei veloiteta ennen ilmaisajan päättymistä." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Julkinen API", diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 303f12643f8..d174e6694a9 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Code panseguridad (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Pangalan ng pagkakakilanlan" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopyahin ang halaga", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Na update ang Key" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Pinili" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Pag-aari" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index b1ac1951908..9e422b429e9 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Cryptogramme visuel (CVV)" }, + "securityCodeSlashCVV": { + "message": "Code de sécurité / CVV" + }, "identityName": { "message": "Identité" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copie Réussie" + }, "copyValue": { "message": "Copier la valeur", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "La mise à jour de la clé de chiffrement ne peut pas continuer" + }, + "keyUpdateFoldersFailed": { + "message": "Lors de la mise à jour de votre clé de chiffrement, vos dossiers n'ont pas pu être déchiffrés. Pour continuer avec la mise à jour, vos dossiers doivent être supprimés. Aucun élément du coffre ne sera supprimé si vous continuez." + }, "keyUpdated": { "message": "Clé mise à jour" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Sélectionné(s)" }, + "recommended": { + "message": "Recommandé" + }, "ownership": { "message": "Propriété" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audite les accès des membres de l'organisation au sein des groupes, des collections et des éléments des collections. L'exportation CSV fournit un rapport détaillé par membre comprenant des informations sur les collections autorisées et les configurations de compte." }, + "memberAccessReportNoCollection": { + "message": "(Aucune collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Aucune permission de collection)" + }, + "memberAccessReportNoGroup": { + "message": "(Aucun groupe)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Activé" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Désactivé" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Activé" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Désactivé" + }, "higherKDFIterations": { "message": "Des itérations KDF plus élevées peuvent aider à protéger votre mot de passe principal contre la force brute d'un assaillant." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Mettez à niveau votre plan pour inviter plus de membres et accéder à des fonctionnalités supplémentaires de Bitwarden" + "upgradePlans": { + "message": "Mettez à niveau votre plan pour inviter des membres et profiter de fonctionnalités de sécurité puissantes." }, "upgradeDiscount": { "message": "Économisez $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Fonctionnalités avancées pour les entreprises plus grandes" + "enterprisePlanUpgradeMessage": { + "message": "Fonctionnalités avancées pour les grandes organisations" }, - "upgradeTeamsMessage": { - "message": "Entreprises à la recherche d'une sécurité puissante" + "teamsPlanUpgradeMessage": { + "message": "Protection résistante pour les équipes en croissance" }, "teamsInviteMessage": { "message": "Invitez un nombre illimité de membres" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Synchroniser les groupes et les utilisateurs à partir d'un répertoire" }, - "upgradeFamilyMessage": { - "message": "Partager avec des membres de la famille et des amis" + "familyPlanUpgradeMessage": { + "message": "Sécurisez les identifiants de votre famille" }, "accessToPremiumFeatures": { "message": "Accède aux fonctionnalités Premium" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Gestionnaire de Mots de Passe Bitwarden" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Votre abonnement complémentaire d'un an au Gestionnaire de Mots de Passe sera mis à niveau au plan sélectionné. Vous ne serez pas chargé avant la fin de votre période gratuite." + "secretsManagerComplimentaryPasswordManager": { + "message": "Votre abonnement complémentairegratuit d'un an au Gestionnaire de Mots de Passe sera mis à niveau au plan sélectionné. Vous ne serez pas chargé avant la fin de votre période gratuite." + }, + "fileSavedToDevice": { + "message": "Fichier enregistré sur l'appareil. Gérez à partir des téléchargements de votre appareil." }, "publicApi": { "message": "API publique", diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index f7d33277076..25bed9fd572 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Código de seguridade do reverso (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Nome da identidade" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copiar valor", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index c0f0f764a9c..28b3cbf0a1a 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "קוד האבטחה (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "שם הזהות" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "העתק ערך", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "המפתח עודכן" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "נבחר\\ו" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "בעלות" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 65902135793..3eba1125258 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "सिक्योरिटी कोड (सीवीवी)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "पहचान का नाम" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 60c214ad181..186999d1fed 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Kontrolni broj" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Ime identiteta" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopiraj vrijednost", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Ključ ažuriran" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Odabrano" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Vlasništvo" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Provjeri pristup članova organizacije u grupama, zbirkama i stavkama zbirke. CSV izvoz pruža detaljnu raščlambu po članu, uključujući informacije o dozvolama za prikupljanje i konfiguracijama računa." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Veće KDF iteracije mogu pomoći u zaštiti tvoje glavne lozinke od napadača." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Nadogradi svoj plan za pozivanje više članova i pristup dodatnim Bitwarden značajkama" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Uštedi $AMOUNT$ %", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Napredne mogućnosti za veće tvrtke" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Tvrtke koje traže veću razinu sigurnosti" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Pozovi neograničen broj članova" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sinkroniziraj grupe i korisnike iz imenika" }, - "upgradeFamilyMessage": { - "message": "Dijeli s obitelji i prijateljima" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Pristup Premium značajkama" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden upravitelj lozinki" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Tvoja jednogodišnja poklon pretplata na Upravitelj tajni će se nadograditi na odabrani plan. Neće biti naplate dok ne završi besplatno razdoblje." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Javni API", diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index f216aaa91a6..4e66cbc710c 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Biztonsági kód (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Személyazonosság megnevezés" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Érték másolása", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "A kulcs frissítésre került." }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Kiválasztva" }, + "recommended": { + "message": "Ajánlott" + }, "ownership": { "message": "Tulajdonjog" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "A szervezeti tagok hozzáférésének ellenőrzése a csoportok, gyűjtemények és gyűjteményelemek között. A CSV exportálás tagonként részletes lebontást biztosít, beleértve a gyűjtemény engedélyekre és a fiókkonfigurációkra vonatkozó információkat." }, + "memberAccessReportNoCollection": { + "message": "(Nincs gyűjtemény)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Nincs gyűjtemény jogosultság)" + }, + "memberAccessReportNoGroup": { + "message": "(Nincs csoport)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Be" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Ki" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Be" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Ki" + }, "higherKDFIterations": { "message": "A magasabb szintű KDF iterációk segíthetnek megvédeni mesterjelszót a támadók erőszakossága ellen." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Térjünk át más csomagra, hogy több tagot hívhassunk meg és hozzáférjünk a Bitwarden további funkcióihoz." + "upgradePlans": { + "message": "Frissítsük a csomagot tagok meghívásához és élvezzük a hatékony biztonsági funkciókat." }, "upgradeDiscount": { "message": "$AMOUNT$% megtakarítás", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Fejlett képességek nagyobb vállalkozások számára" + "enterprisePlanUpgradeMessage": { + "message": "Fejlett képességek nagyobb szervezetek számára" }, - "upgradeTeamsMessage": { - "message": "Hatékony biztonságot kereső vállalkozások" + "teamsPlanUpgradeMessage": { + "message": "Rugalmas védelem növekvő csapatok számára" }, "teamsInviteMessage": { "message": "Korlátlan számú tagok meghívása" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "A felhasználók és csoportok szinkronizálása könyvtárból" }, - "upgradeFamilyMessage": { - "message": "Mggosztáás családtagokkal és ismerősökkel" + "familyPlanUpgradeMessage": { + "message": "Családi bejelentkezési adatok biztosítása" }, "accessToPremiumFeatures": { "message": "Prémium funkciók elérése" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden jelszókezelő" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "A kiegészítő egyéves Jelszókezelő előfizetés a kiválasztott csomagra frissül. A díjmentes időszak lejártáig nem számítunk fel díjat." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "A fájl mentésre került az eszközre. Kezeljük az eszközről a letöltéseket." }, "publicApi": { "message": "Nyilvános API", diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 30e1638a869..165d513febb 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Kode Keamanan (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Nama Identitas" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Salin Nilai", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Kunci Diperbarui" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Terpilih" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Kepemilikan" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index b2849a20338..e85779431a7 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Codice di sicurezza (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Nome identità" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copia valore", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Chiave aggiornata" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selezionato" }, + "recommended": { + "message": "Consigliato" + }, "ownership": { "message": "Proprietà" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Fai audit dell'accesso dei membri dell'organizzazione ai gruppi, alle raccolte e agli elementi. L'esportazione in CSV fornisce un report dettagliato per membro, comprese le informazioni sui permessi delle raccolte e le configurazioni dell'account." }, + "memberAccessReportNoCollection": { + "message": "(Nessuna raccolta)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Nessun permesso sulla raccolta)" + }, + "memberAccessReportNoGroup": { + "message": "(Nessun gruppo)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Attivo" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Disattivo" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Attivo" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Disattivo" + }, "higherKDFIterations": { "message": "Un numero di iterazioni KDF più elevato può aiutare a proteggere la tua password principale dall'essere forzata da un attaccante." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Aggiorna il tuo piano per invitare più membri e accedere a funzionalità aggiuntive di Bitwarden" + "upgradePlans": { + "message": "Aggiorna il tuo piano per invitare membri e sperimentare potenti funzionalità di sicurezza." }, "upgradeDiscount": { "message": "Risparmia il $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Capacità avanzate per le imprese più grandi" + "enterprisePlanUpgradeMessage": { + "message": "Capacità avanzate per le organizzazioni più grandi" }, - "upgradeTeamsMessage": { - "message": "Imprese alla ricerca di una sicurezza totale" + "teamsPlanUpgradeMessage": { + "message": "Protezione resiliente per i team in crescita" }, "teamsInviteMessage": { "message": "Invita un numero di membri illimitato" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sincronizza utenti e gruppi da una directory" }, - "upgradeFamilyMessage": { - "message": "Condividi con famiglia e amici" + "familyPlanUpgradeMessage": { + "message": "Proteggi gli accessi della tua famiglia" }, "accessToPremiumFeatures": { "message": "Accedi alle funzionalità Premium" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Il tuo abbonamento complementare a Password Manager di un anno verrà aggiornato al piano selezionato. Non ti verrà addebitato nessun costo fino a quando il periodo gratuito non sarà terminato." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File salvato sul dispositivo. Gestisci dai download del dispositivo." }, "publicApi": { "message": "API pubblica", diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 88973293334..a7f51d31363 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "セキュリティコード (CVV)" }, + "securityCodeSlashCVV": { + "message": "セキュリティコード / CVV" + }, "identityName": { "message": "固有名" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "コピーしました" + }, "copyValue": { "message": "値のコピー", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "暗号化キーの更新を続行できません" + }, + "keyUpdateFoldersFailed": { + "message": "暗号化キーを更新する際、フォルダーを復号できませんでした。 アップデートを続行するには、フォルダーを削除する必要があります。続行しても保管庫のアイテムは削除されません。" + }, "keyUpdated": { "message": "キーが更新されました" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "選択済み" }, + "recommended": { + "message": "おすすめ" + }, "ownership": { "message": "所有者" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "組織メンバーによるグループ、コレクション、コレクションアイテム間のアクセスを監査します。 CSV エクスポートには、コレクションの権限とアカウント構成に関する情報を含むメンバーごとの詳細な内訳が表示されます。" }, + "memberAccessReportNoCollection": { + "message": "(コレクションなし)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(コレクション権限がありません)" + }, + "memberAccessReportNoGroup": { + "message": "(グループなし)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "オン" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "オフ" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "オン" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "オフ" + }, "higherKDFIterations": { "message": "KDF 反復回数を多くすることで、攻撃者による総当たり攻撃からマスターパスワードを守ることができます。" }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "より多くのメンバーを招待し、Bitwarden の追加機能にアクセスするには、プランをアップグレードしてください。" + "upgradePlans": { + "message": "メンバーを招待し、強力なセキュリティ機能を体験するにはプランをアップグレードしてください。" }, "upgradeDiscount": { "message": "$AMOUNT$ %割引", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "大企業向けの高度な機能" + "enterprisePlanUpgradeMessage": { + "message": "大規模組織向けの高度な機能" }, - "upgradeTeamsMessage": { - "message": "強力なセキュリティを求めている企業" + "teamsPlanUpgradeMessage": { + "message": "成長するチームにとって強靭な保護" }, "teamsInviteMessage": { "message": "無制限のメンバー招待" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "ディレクトリからグループとユーザーを同期" }, - "upgradeFamilyMessage": { - "message": "家族や友達と共有" + "familyPlanUpgradeMessage": { + "message": "家族のログイン情報の保護" }, "accessToPremiumFeatures": { "message": "プレミアム機能へのアクセス" @@ -9043,9 +9079,12 @@ "bitwardenPasswordManager": { "message": "Bitwarden パスワードマネージャー" }, - "secretsManagerWithFreePasswordManagerInfo": { + "secretsManagerComplimentaryPasswordManager": { "message": "1年間のパスワードマネージャーサブスクリプションは、選択したプランにアップグレードされます。無料期間が終了するまで課金されることはありません。" }, + "fileSavedToDevice": { + "message": "ファイルをデバイスに保存しました。デバイスのダウンロードで管理できます。" + }, "publicApi": { "message": "公開 API", "description": "The text, 'API', is an acronymn and should not be translated." diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 67364dde68d..29346aa946b 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "უსაფრთხოების კოდი (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "მოწმობის სახელი" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "ასლირება მნიშვნელობის", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 21e8cf62999..d0be45fdc2c 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index c9c9ab49f71..07d77b4a4a3 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "ಭದ್ರತಾ ಕೋಡ್ (ಸಿವಿವಿ)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "ಹೆಸರು ಗುರುತು" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "ಮೌಲ್ಯವನ್ನು ನಕಲಿಸಿ", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "ಕೀ ನವೀಕರಿಸಲಾಗಿದೆ" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "ಮಾಲೀಕತ್ವ" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 934929b1ee0..459d16b7618 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "카드 보안 코드 (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "ID 이름" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "값 복사", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "키 업데이트됨" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "선택됨" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "소유자" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index 3fa2249bcd0..0d97c52f6e9 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Drošības kods (CVV)" }, + "securityCodeSlashCVV": { + "message": "Drošības kods / CVV" + }, "identityName": { "message": "Identitātes nosaukums" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Ievietošana starpliktuvē veiksmīga" + }, "copyValue": { "message": "Ievietot vērtību starpliktuvē", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Šifrēšanas atslēgas atjaunināšana nav iespējama" + }, + "keyUpdateFoldersFailed": { + "message": "Šifrēšanas atslēgas atjaunināšanas laikā mapes nevarēja atšifrēt. Lai turpinātu atjaunināšanu, mapes ir jāizdzēš. Glabātavas vienumi netiks izdzēsti, ja turpināsi." + }, "keyUpdated": { "message": "Atslēga atjaunināta" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Atlasīts" }, + "recommended": { + "message": "Ieteicams" + }, "ownership": { "message": "Īpašumtiesības" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Pārskatīt apvienības dalībnieku piekļuvi dažādām kopām, krājumiem un krājumu vienumiem. CSV izgūšana sniedz izvērstu pārskatu par katru dalībnieku, tajā skaitā informāciju par krājumu atļaujām un konta konfigurāciju." }, + "memberAccessReportNoCollection": { + "message": "(Nav krājuma)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Nav krājuma atļaujas)" + }, + "memberAccessReportNoGroup": { + "message": "(Nav kopas)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Ieslēgts" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Izslēgts" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Ieslēgts" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Izslēgts" + }, "higherKDFIterations": { "message": "Lielāks KDF atkārtojumu skaits var palīdzēt aizsargāt galveno paroli pārlases uzbrukuma gadījumā." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Uzlabo savu plānu, lai uzaicinātu vairāk dalībnieku un iegūtu piekļuvi papildu Bitwarden iespējām" + "upgradePlans": { + "message": "Jāuzlabo savs plāns, lai uzaicinātu dalībniekus un iegūtu spēcīgas drošības iespējas." }, "upgradeDiscount": { "message": "Ietaupi $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Izvērstas spējas lielākiem uzņēmumiem" + "enterprisePlanUpgradeMessage": { + "message": "Papildu spējas lielākām apvienībām" }, - "upgradeTeamsMessage": { - "message": "Uzņēmumiem, kuri meklē spēcīgu drošibu" + "teamsPlanUpgradeMessage": { + "message": "Elastīga aizsardzība augošām komandām" }, "teamsInviteMessage": { "message": "Neierobežota dalībnieku skaita uzaicināšana" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Kopu un lietotāju sinhronizēšana no direktorija" }, - "upgradeFamilyMessage": { - "message": "Kopīgošana ar ģimeni un draugiem" + "familyPlanUpgradeMessage": { + "message": "Nodrošini savas ģimenes pieteikšanās vienumus" }, "accessToPremiumFeatures": { "message": "Piekļuve Premium iespējām" @@ -9043,9 +9079,12 @@ "bitwardenPasswordManager": { "message": "Bitwarden paroļu pārvaldnieks" }, - "secretsManagerWithFreePasswordManagerInfo": { + "secretsManagerComplimentaryPasswordManager": { "message": "Bezmaksas viena gada Paroļu pārvaldnieka abonements tiks uzlabots uz izvēlēto plānu. Maksa netiks ieturēta līdz bezmaksas izmantošanas laika beigām." }, + "fileSavedToDevice": { + "message": "Datne saglabāta ierīcē. Tā ir atrodama ierīces lejupielāžu mapē." + }, "publicApi": { "message": "Publiskais API", "description": "The text, 'API', is an acronymn and should not be translated." diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 12d14414b5c..5e0efafcedf 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "സുരക്ഷാ കോഡ് സിവി‌വി" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "തിരിച്ചറിയലിൻ്റെ പേര്" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "മൂല്യം പകർത്തുക", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "കീ അപ്‌ഡേറ്റുചെയ്‌തു" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "തിരഞ്ഞെടുത്തത്" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "ഉടമസ്ഥാവകാശം" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 21e8cf62999..d0be45fdc2c 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 21e8cf62999..d0be45fdc2c 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index a33e12c544b..1f1be7b04ed 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Sikkerhetskode (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identitetsnavn" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopier verdien", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Nøkkelen ble oppdatert" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Valgt" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Eierskap" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index f9ba74ae957..f758c6d360a 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "सुरक्षा कोड (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "पहिचानको लागि नाम" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 0f208d53fce..e09fdb78dd7 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Beveiligingscode (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identiteitsnaam" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Waarde kopiëren", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Sleutel bijgewerkt" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Geselecteerd" }, + "recommended": { + "message": "Aanbevolen" + }, "ownership": { "message": "Eigendom" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit de toegang van een organisatielid tot groepen, verzamelingen en verzamelen van items. De CSV-export biedt een gedetailleerde verdeling per lid, inclusief informatie over verzamelrechten en accountconfiguraties." }, + "memberAccessReportNoCollection": { + "message": "(Geen collectie)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Geen rechten voor collectie)" + }, + "memberAccessReportNoGroup": { + "message": "(Geen groep)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Aan" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Uit" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Aan" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Uit" + }, "higherKDFIterations": { "message": "Hogere KDF-iteraties beschermen je hoofdwachtwoord tegen brute-foce-aanvallen." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade je abonnement voor het uitnodigen van meer leden en toegang te krijgen tot extra Bitwarden-functionaliteit" + "upgradePlans": { + "message": "Wijzig je abonnement voor het uitnodigen van leden en gebruikmaken van krachtige beveiligingsfuncties." }, "upgradeDiscount": { "message": "Bespaar $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { + "enterprisePlanUpgradeMessage": { "message": "Geavanceerde mogelijkheden voor grotere bedrijven" }, - "upgradeTeamsMessage": { - "message": "Bedrijven die op zoek zijn naar krachtige beveiliging" + "teamsPlanUpgradeMessage": { + "message": "Veerkrachtige bescherming voor groeiende teams" }, "teamsInviteMessage": { "message": "Onbeperkt aantal leden uitnodigen" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Synchroniseer je gebruikers en groepen vanuit een directory" }, - "upgradeFamilyMessage": { - "message": "Deel met families en vrienden" + "familyPlanUpgradeMessage": { + "message": "Beveilig je familie-logins" }, "accessToPremiumFeatures": { "message": "Toegang tot premium functionaliteiten" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "Bestand op apparaat opgeslagen. Beheer vanaf de downloads op je apparaat." }, "publicApi": { "message": "Openbare API", diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index ff329b1a781..3c89cddd65b 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Verifiseringskode (CVC)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Intentitetsnamn" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopier verdi", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 21e8cf62999..d0be45fdc2c 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 205104c177b..4461717618f 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Kod zabezpieczający (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Nazwa profilu" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopiuj wartość", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Klucz został zaktualizowany" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Zaznaczono" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Właściciel" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Dostęp członków organizacji audytowej do wszystkich grup, kolekcji i elementów kolekcji. Eksport CSV zapewnia szczegółowy podział na członków, w tym informacje o uprawnieniach do zbierania i konfiguracjach kont." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Wyższe wartości iteracji KDF mogą pomóc chronić Twoje hasło główne przed złamaniem przez atakującego." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Ulepsz swój plan, aby zaprosić więcej członków i uzyskać dostęp do dodatkowych funkcji Bitwarden" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Zaoszczędź $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Zaawansowane możliwości dla większych przedsiębiorstw" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Przedsiębiorstwa szukające potężnego bezpieczeństwa" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Zaproś nieograniczoną liczbę członków" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Synchronizuj grupy i użytkowników z użyciem katalogu" }, - "upgradeFamilyMessage": { - "message": "Podziel się z rodziną i przyjaciółmi" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Dostęp do funkcji Premium" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 2aecf452ea6..341c2c9db05 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Código de Segurança (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Nome de Identidade" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copiar Valor", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Chave Atualizada" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selecionado" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Propriedade" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Avalie o acesso de membros da organização entre grupos, coleções e itens de coleções. O exporte CSV fornece informações detalhadas por membro, incluindo informações sobre permissões de coleção e configurações de conta." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Iterações KDF mais altas podem ajudar a proteger sua senha mestra de ser descoberta por força bruta por alguém mal-intencionado." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Atualize o seu plano para convidar mais membros e ganhar acesso a recursos adicionais do Bitwarden" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Economize $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Recursos avançados para empresas maiores" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Empresas que procuram segurança poderosa" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Convide membros ilimitados" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sincronizar grupos e usuários de um diretório" }, - "upgradeFamilyMessage": { - "message": "Compartilhar com famílias e amigos" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Acesso às funcionalidades Premium" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Gerenciador de Senhas Bitwarden" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Sua assinatura complementar de um ano gerente de senha vai atualizar para o plano selecionado. Você não será cobrado até que o período complementar termine." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 50805349223..9cc1d8dd5fc 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Código de segurança (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Nome da identidade" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copiar valor", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Chave atualizada" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selecionado(s)" }, + "recommended": { + "message": "Recomendado" + }, "ownership": { "message": "Propriedade" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audite o acesso dos membros da organização a grupos, coleções e itens de coleção. A exportação CSV fornece uma análise detalhada por membro, incluindo informações sobre permissões de coleção e configurações de conta." }, + "memberAccessReportNoCollection": { + "message": "(Sem coleção)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Sem permissões de coleção)" + }, + "memberAccessReportNoGroup": { + "message": "(Sem grupos)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Ativado" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Desativado" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Ativado" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Desativado" + }, "higherKDFIterations": { "message": "Iterações KDF mais altas podem ajudar a proteger a sua palavra-passe mestra de ser forçada por um atacante." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Atualize o seu plano para convidar mais membros e obter acesso a funcionalidades adicionais do Bitwarden" + "upgradePlans": { + "message": "Atualize o seu plano para convidar membros e experimentar poderosas funcionalidades de segurança." }, "upgradeDiscount": { "message": "Poupe $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Capacidades avançadas para empresas de maior dimensão" + "enterprisePlanUpgradeMessage": { + "message": "Capacidades avançadas para organizações de maior dimensão" }, - "upgradeTeamsMessage": { - "message": "Empresas que procuram uma segurança poderosa" + "teamsPlanUpgradeMessage": { + "message": "Proteção resiliente para equipas em crescimento" }, "teamsInviteMessage": { "message": "Convide membros ilimitados" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sincronize grupos e utilizadores a partir de um diretório" }, - "upgradeFamilyMessage": { - "message": "Partilhe com familiares e amigos" + "familyPlanUpgradeMessage": { + "message": "Proteja as credenciais da sua família" }, "accessToPremiumFeatures": { "message": "Acesso a funcionalidades Premium" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden - Gestor de Palavras-passe" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "A sua subscrição complementar de um ano do Gestor de Palavras-passe será atualizada para o plano selecionado. Não será cobrado até que o período de cortesia termine." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "Ficheiro guardado no dispositivo. Gira-o a partir das transferências do seu dispositivo." }, "publicApi": { "message": "API pública", diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 7384da233f5..603e7ef5ffe 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Cod de securitate (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Numele identității" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copiere valoare", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Cheie actualizată" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selectat(e)" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Proprietate" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index b3e2c6efa77..83e8dfdb532 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Код безопасности (CVV)" }, + "securityCodeSlashCVV": { + "message": "Код безопасности / CVV" + }, "identityName": { "message": "Имя" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Скопировано успешно" + }, "copyValue": { "message": "Скопировать значение", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Обновление ключа шифрования невозможно" + }, + "keyUpdateFoldersFailed": { + "message": "При обновлении ключа шифрования не удалось расшифровать папки. Чтобы продолжить обновление, папки необходимо удалить. При продолжении обновления элементы хранилища удалены не будут." + }, "keyUpdated": { "message": "Ключ обновлен" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Выбрано" }, + "recommended": { + "message": "Рекомендуется" + }, "ownership": { "message": "Владелец" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Аудит доступа членов организации к группам, коллекциям и элементам коллекций. Экспорт в формате CSV содержит подробную разбивку по членам, включая информацию о разрешениях на коллекции и конфигурациях учетных записей." }, + "memberAccessReportNoCollection": { + "message": "(Нет коллекций)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Нет прав для коллекций)" + }, + "memberAccessReportNoGroup": { + "message": "(Нет группы)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Вкл" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Выкл" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Вкл" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Выкл" + }, "higherKDFIterations": { "message": "Увеличение числа итераций KDF может помочь защитить ваш мастер-пароль от взлома его злоумышленником." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Повысьте тарифный план, чтобы пригласить больше участников и получить доступ к дополнительным возможностям Bitwarden" + "upgradePlans": { + "message": "Улучшите свой план, чтобы приглашать пользователей и получить мощные возможности безопасности." }, "upgradeDiscount": { "message": "Сэкономить $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { + "enterprisePlanUpgradeMessage": { "message": "Расширенные возможности для крупного бизнеса" }, - "upgradeTeamsMessage": { - "message": "Предприятиям нужна мощная система безопасности" + "teamsPlanUpgradeMessage": { + "message": "Надежная защита для растущих команд" }, "teamsInviteMessage": { "message": "Приглашайте неограниченное количество участников" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Синхронизация групп и пользователей из каталога" }, - "upgradeFamilyMessage": { - "message": "Делитесь с семьей и друзьями" + "familyPlanUpgradeMessage": { + "message": "Защитите логины вашей семьи" }, "accessToPremiumFeatures": { "message": "Доступ к премиум-возможностям" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden - Менеджер паролей" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Ваш дополнительный год подписки на Менеджер паролей обновится до выбранный тарифа. Плата не будет взиматься до окончания бесплатного периода." + "secretsManagerComplimentaryPasswordManager": { + "message": "Ваша бесплатная годовая подписка на Password Manager будет обновлена до выбранного тарифного плана. Плата не будет взиматься до тех пор, пока не закончится бесплатный период." + }, + "fileSavedToDevice": { + "message": "Файл сохранен на устройстве. Управляйте им из загрузок устройства." }, "publicApi": { "message": "Публичный API", diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index f4585132675..e7fc8333893 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 1d90ff61265..5e87aadf1e8 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Bezpečnostný kód (CVV)" }, + "securityCodeSlashCVV": { + "message": "Bezpečnostný kód / CVV" + }, "identityName": { "message": "Názov identity" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Úspešne skopírované" + }, "copyValue": { "message": "Kopírovať hodnotu", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Aktualizácia šifrovacieho kľúča nemôže pokračovať" + }, + "keyUpdateFoldersFailed": { + "message": "Pri aktualizácii šifrovacieho kľúča nebolo možné dešifrovať vaše priečinky. Ak chcete pokračovať v aktualizácii, vaše priečinky sa musia odstrániť. Ak budete pokračovať, nebudú odstránené žiadne položky trezora." + }, "keyUpdated": { "message": "Kľúč aktualizovaný" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Vybraté" }, + "recommended": { + "message": "Odporúčané" + }, "ownership": { "message": "Vlastníctvo" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit prístupu členov organizácie v skupinách, kolekciách a položkách kolekcie. Export CSV poskytuje podrobný rozpis podľa jednotlivých členov vrátane informácií o oprávneniach kolekcií a konfiguráciách účtov." }, + "memberAccessReportNoCollection": { + "message": "(Žiadna Zbierka)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Žiadne povolenia ku zbierke)" + }, + "memberAccessReportNoGroup": { + "message": "(Žiadna Skupina)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Zapnuté" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Vypnuté" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Zapnuté" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Vypnuté" + }, "higherKDFIterations": { "message": "Zvýšenie počtu KDF iterácií môže pomôcť chrániť vaše hlavné heslo pri brute force útoku." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,9 +9079,12 @@ "bitwardenPasswordManager": { "message": "Bitwarden Správca Hesiel" }, - "secretsManagerWithFreePasswordManagerInfo": { + "secretsManagerComplimentaryPasswordManager": { "message": "Vaše bezplatné ročné predplatné programu Password Manager sa zmení na vybraný plán. Poplatok vám bude účtovaný až po skončení bezplatného obdobia." }, + "fileSavedToDevice": { + "message": "Súbor sa uložil do zariadenia. Spravujte stiahnuté súbory zo zariadenia." + }, "publicApi": { "message": "Verejné API", "description": "The text, 'API', is an acronymn and should not be translated." diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 6ff12ad4596..894dcffa26a 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Varnostna koda (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Ime identitete" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopiraj vrednost", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Ključ posodobljen" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index f0bea1eb573..79b6b47bfff 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Безбедносни кôд (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Име идентитета" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Копирај вредност", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Кључ је ажуриран" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Изабано" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Власништво" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Провера приступа чланова организације кроз групе, колекције и ставке колекције. ЦСВ извоз пружа детаљну анализу по члану, укључујући информације о дозволама за прикупљање и конфигурацијама налога." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Веће KDF итерације може помоћи у заштити ваше главне лозинке од грубе присиле од стране нападача." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Уштедите $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Напредне могућности за већа предузећа" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Предузећа која траже моћну сигурност" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Синхронизујте групе и кориснике из директоријума" }, - "upgradeFamilyMessage": { - "message": "Поделите са породицама и пријатељима" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Приступ Премиум функцијама" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Менаџер Лозинке" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Ваша комплементарна једногодишња претплата на Менаџер Лозинки ће надоградити на изабрани план. Неће вам бити наплаћено док се бесплатни период не заврши." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Јавни API", diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 5df939b84e7..4163b728bb5 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Sigurnosni Kod (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Naziv Isprave" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopiraj Vrednost", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 5aab64e9cbe..db393954546 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Säkerhetskod (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identitetsnamn" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Kopiera värde", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Nyckeln uppdaterades" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Markerade" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ägarskap" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(Ingen samling)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(Ingen grupp)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Spara $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Avancerade funktioner för större företag" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Dela med familj och vänner" + "familyPlanUpgradeMessage": { + "message": "Säkra dina familjeinloggningar" }, "accessToPremiumFeatures": { "message": "Tillgång till Premium-funktioner" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 21e8cf62999..d0be45fdc2c 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Security code (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Copy value", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 9055bc1c9f8..71e56b40804 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "รหัสความปลอดภัย (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Identity name" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "คัดลอกค่า", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index 640ddc5a5d6..8978be127d1 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -111,7 +111,7 @@ } }, "showMatchDetection": { - "message": "Show match detection $WEBSITE$", + "message": "$WEBSITE$ eşleşme tespitini göster", "placeholders": { "website": { "content": "$1", @@ -120,7 +120,7 @@ } }, "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", + "message": "$WEBSITE$ eşleşme tespitini gizle", "placeholders": { "website": { "content": "$1", @@ -143,6 +143,9 @@ "securityCode": { "message": "Güvenlik kodu (CVV)" }, + "securityCodeSlashCVV": { + "message": "Güvenlik kodu / CVV" + }, "identityName": { "message": "Kimlik adı" }, @@ -243,7 +246,7 @@ "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "Kimlik doğrulayıcılar hakkında bilgi alın" }, "folder": { "message": "Klasör" @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Kopyalama başarılı" + }, "copyValue": { "message": "Değeri kopyala", "description": "Copy value to clipboard" @@ -675,7 +681,7 @@ } }, "itemsMovedToOrg": { - "message": "Items moved to $ORGNAME$", + "message": "Kayıtlar $ORGNAME$ kuruluşuna taşındı", "placeholders": { "orgname": { "content": "$1", @@ -684,7 +690,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "Kayıt $ORGNAME$ kuruluşuna taşındı", "placeholders": { "orgname": { "content": "$1", @@ -1821,13 +1827,13 @@ "message": "İki aşamalı giriş ayarlarını değiştirmek için ana parolanızı girin." }, "twoStepAuthenticatorInstructionPrefix": { - "message": "Download an authenticator app such as" + "message": "Bir kimlik doğrulama uygulaması indirin. Örnek:" }, "twoStepAuthenticatorInstructionInfix1": { "message": "," }, "twoStepAuthenticatorInstructionInfix2": { - "message": "or" + "message": "veya" }, "twoStepAuthenticatorInstructionSuffix": { "message": "." @@ -1851,7 +1857,7 @@ "message": "Bitwarden Authenticator allows you to store authenticator keys and generate TOTP codes for 2-step verification flows. Learn more on the bitwarden.com website." }, "twoStepAuthenticatorScanCodeV2": { - "message": "Scan the QR code below with your authenticator app or enter the key." + "message": "Aşağıdaki QR kodunu kimlik doğrulama uygulamanıza okutun veya anahtarı girin." }, "twoStepAuthenticatorQRCanvasError": { "message": "Could not load QR code. Try again or use the key below." @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Şifreleme anahtarı güncellemesine devam edilemiyor" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Anahtar güncellendi" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Seçildi" }, + "recommended": { + "message": "Önerilen" + }, "ownership": { "message": "Sahip" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(Koleksiyon yok)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Koleksiyon izni yok)" + }, + "memberAccessReportNoGroup": { + "message": "(Grup yok)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Açık" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Kapalı" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Açık" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Kapalı" + }, "higherKDFIterations": { "message": "KDF iterasyonunun daha yüksek olması ana parolanızı kaba kuvvet saldırılarına karşı daha iyi korur." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Parola Yöneticisi" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "Dosya cihaza kaydedildi. Cihazınızın indirilenler klasöründen yönetebilirsiniz." }, "publicApi": { "message": "Genel API", diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index c8ae5f4b56e..9de932e9c09 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Код безпеки (CVV)" }, + "securityCodeSlashCVV": { + "message": "Код безпеки / CVV" + }, "identityName": { "message": "Назва посвідчення" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Успішно скопійовано" + }, "copyValue": { "message": "Копіювати значення", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Неможливо продовжити оновлення ключа шифрування" + }, + "keyUpdateFoldersFailed": { + "message": "Не вдалося розшифрувати ваші теки під час оновлення ключа шифрування. Щоб продовжити оновлення, необхідно видалити теки. Якщо ви продовжите, записи у сховищі не будуть видалені." + }, "keyUpdated": { "message": "Ключ оновлено" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Вибрано" }, + "recommended": { + "message": "Рекомендовано" + }, "ownership": { "message": "Власник" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Аудит доступу учасника організації до груп, збірок та записів збірок. Експорт CSV надає детальну розбивку для кожного учасника, зокрема інформацію про дозволи для збірок та конфігурації облікового запису." }, + "memberAccessReportNoCollection": { + "message": "(Без збірки)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(Без дозволу для збірки)" + }, + "memberAccessReportNoGroup": { + "message": "(Без групи)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "Увімк" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Вимк" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "Увімк" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Вимк" + }, "higherKDFIterations": { "message": "Вищі значення KDF-ітерацій можуть допомогти захистити ваш головний пароль від грубого зламу зловмисником." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Перейдіть на вищий тарифний план, щоб запросити більше учасників та отримати доступ до додаткових функцій Bitwarden" + "upgradePlans": { + "message": "Оновіть тарифний план, щоб запросити учасників та користуватися потужними функціями безпеки." }, "upgradeDiscount": { "message": "Заощадьте $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Розширені можливості для великого бізнесу" + "enterprisePlanUpgradeMessage": { + "message": "Розширені можливості для більших організацій" }, - "upgradeTeamsMessage": { - "message": "Компанії, яким потрібна гарантована безпека" + "teamsPlanUpgradeMessage": { + "message": "Адаптивний захист для команд, що розширюються" }, "teamsInviteMessage": { "message": "Запрошуйте користувачів без обмежень" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Синхронізуйте групи та користувачів з каталогу" }, - "upgradeFamilyMessage": { - "message": "Надавайте спільний доступ родині та друзям" + "familyPlanUpgradeMessage": { + "message": "Захистіть паролі своєї родини" }, "accessToPremiumFeatures": { "message": "Доступ до преміальних функцій" @@ -9043,9 +9079,12 @@ "bitwardenPasswordManager": { "message": "Bitwarden – менеджер паролів" }, - "secretsManagerWithFreePasswordManagerInfo": { + "secretsManagerComplimentaryPasswordManager": { "message": "Ваша додаткова річна передплата менеджера паролів поновиться до вибраного плану. З вас не стягуватиметься платіж доки не завершиться додатковий період." }, + "fileSavedToDevice": { + "message": "Файл збережено на пристрої. Ви можете його знайти у теці завантажень." + }, "publicApi": { "message": "Відкритий API", "description": "The text, 'API', is an acronymn and should not be translated." diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 90eca2660ec..970e61caa5a 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "Mã bảo mật (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "Tên danh tính" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "Sao chép giá trị", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "Key updated" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "Selected" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "Ownership" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 34804b736ea..a32fabb44b1 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -52,7 +52,7 @@ "message": "个人信息" }, "identification": { - "message": "Identification" + "message": "身份" }, "contactInfo": { "message": "联系信息" @@ -143,6 +143,9 @@ "securityCode": { "message": "安全码 (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "身份名称" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "复制值", "description": "Copy value to clipboard" @@ -3820,7 +3826,7 @@ } }, "subscriptionUserSeatsWithoutAdditionalSeatsOption": { - "message": "您最多可邀请 $COUNT$ 名成员,而无需额外付费。请联系客户支持升级您的计划并邀请更多成员。", + "message": "您最多可邀请 $COUNT$ 名成员,而无需额外付费。要升级您的计划并邀请更多成员,请联系客户支持。", "placeholders": { "count": { "content": "$1", @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "密钥已更新" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "已选择" }, + "recommended": { + "message": "推荐" + }, "ownership": { "message": "所有权" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "审计组织成员在各个群组、集合和集合项目之间的访问权限。CSV 导出文件提供了每位成员的详细信息,包括集合权限和账户配置的相关信息。" }, + "memberAccessReportNoCollection": { + "message": "(无集合)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(无集合权限)" + }, + "memberAccessReportNoGroup": { + "message": "(无群组)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "开启" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "关闭" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "开启" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "关闭" + }, "higherKDFIterations": { "message": "更高的 KDF 迭代可以帮助保护您的主密码免遭攻击者的暴力破解。" }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "要邀请更多成员并获得附加的 Bitwarden 功能,请升级您的计划" + "upgradePlans": { + "message": "升级您的计划以邀请成员并体验强大的安全功能。" }, "upgradeDiscount": { "message": "节省 $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "适用于大型企业的高级功能" + "enterprisePlanUpgradeMessage": { + "message": "适用于大型组织的高级功能" }, - "upgradeTeamsMessage": { - "message": "企业寻求的强大安全性" + "teamsPlanUpgradeMessage": { + "message": "为成长中的团队提供弹性保护" }, "teamsInviteMessage": { "message": "邀请不限数量的成员" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "从目录同步群组和用户" }, - "upgradeFamilyMessage": { - "message": "与家庭和朋友分享" + "familyPlanUpgradeMessage": { + "message": "保护您的家庭登录" }, "accessToPremiumFeatures": { "message": "访问高级会员功能" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden 密码管理器" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "您的免费一年密码管理器订阅将升级到所选计划。免费期结束之前不会收费。" + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "文件已保存到设备。在设备下载中进行管理。" }, "publicApi": { "message": "公共 API", @@ -9057,6 +9096,6 @@ "message": "隐藏字符计数" }, "editAccess": { - "message": "编辑权限" + "message": "编辑访问权限" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index 680d3759829..e17547e6f18 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -143,6 +143,9 @@ "securityCode": { "message": "安全碼 (CVV)" }, + "securityCodeSlashCVV": { + "message": "Security code / CVV" + }, "identityName": { "message": "身分名稱" }, @@ -562,6 +565,9 @@ } } }, + "copySuccessful": { + "message": "Copy Successful" + }, "copyValue": { "message": "複製值", "description": "Copy value to clipboard" @@ -3894,6 +3900,12 @@ } } }, + "encryptionKeyUpdateCannotProceed": { + "message": "Encryption key update cannot proceed" + }, + "keyUpdateFoldersFailed": { + "message": "When updating your encryption key, your folders could not be decrypted. To continue with the update, your folders must be deleted. No vault items will be deleted if you proceed." + }, "keyUpdated": { "message": "金鑰已更新" }, @@ -4035,6 +4047,9 @@ "selected": { "message": "已選擇" }, + "recommended": { + "message": "Recommended" + }, "ownership": { "message": "擁有權" }, @@ -8756,6 +8771,27 @@ "memberAccessReportPageDesc": { "message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations." }, + "memberAccessReportNoCollection": { + "message": "(No Collection)" + }, + "memberAccessReportNoCollectionPermission": { + "message": "(No Collection Permission)" + }, + "memberAccessReportNoGroup": { + "message": "(No Group)" + }, + "memberAccessReportTwoFactorEnabledTrue": { + "message": "On" + }, + "memberAccessReportTwoFactorEnabledFalse": { + "message": "Off" + }, + "memberAccessReportAuthenticationEnabledTrue": { + "message": "On" + }, + "memberAccessReportAuthenticationEnabledFalse": { + "message": "Off" + }, "higherKDFIterations": { "message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker." }, @@ -8959,8 +8995,8 @@ } } }, - "upgradePlan": { - "message": "Upgrade your plan to invite more members and gain access to additional Bitwarden features" + "upgradePlans": { + "message": "Upgrade your plan to invite members and experience powerful security features." }, "upgradeDiscount": { "message": "Save $AMOUNT$%", @@ -8971,11 +9007,11 @@ } } }, - "upgradeEnterpriseMessage": { - "message": "Advanced capabilities for larger businesses" + "enterprisePlanUpgradeMessage": { + "message": "Advanced capabilities for larger organizations" }, - "upgradeTeamsMessage": { - "message": "Businesses looking for powerful security" + "teamsPlanUpgradeMessage": { + "message": "Resilient protection for growing teams" }, "teamsInviteMessage": { "message": "Invite unlimited members" @@ -8986,8 +9022,8 @@ "syncGroupsAndUsersFromDirectory": { "message": "Sync groups and users from a directory" }, - "upgradeFamilyMessage": { - "message": "Share with families and friends" + "familyPlanUpgradeMessage": { + "message": "Secure your family logins" }, "accessToPremiumFeatures": { "message": "Access to Premium features" @@ -9043,8 +9079,11 @@ "bitwardenPasswordManager": { "message": "Bitwarden Password Manager" }, - "secretsManagerWithFreePasswordManagerInfo": { - "message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + "secretsManagerComplimentaryPasswordManager": { + "message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over." + }, + "fileSavedToDevice": { + "message": "File saved to device. Manage from your device downloads." }, "publicApi": { "message": "Public API", diff --git a/apps/web/src/scss/forms.scss b/apps/web/src/scss/forms.scss index 9404bc94031..f5800ff7e50 100644 --- a/apps/web/src/scss/forms.scss +++ b/apps/web/src/scss/forms.scss @@ -98,7 +98,7 @@ input[type="checkbox"] { cursor: pointer; } -.form-control.stripe-form-control { +.form-control.stripe-form-control:not(.v2) { padding-top: 0.55rem; &.is-focused { @@ -126,6 +126,30 @@ input[type="checkbox"] { } } +.form-control.stripe-form-control.v2 { + padding: 0.6875rem 0.875rem; + border-radius: 0.5rem; + border-color: rgb(var(--color-text-muted)); + height: unset; + font-weight: 500; + color: rgb(var(--color-text-main)); + background-color: rgb(var(--color-background)); + + &:hover { + border-color: rgb(var(--color-primary-500)); + } + + &.is-focused { + outline: 0; + border-color: rgb(var(--color-primary-500)); + } + + &.is-invalid { + color: rgb(var(--color-text-main)); + border-color: rgb(var(--color-danger-600)); + } +} + .dropdown-menu, .dropdown-item { @include themify($themes) { diff --git a/apps/web/webpack.config.js b/apps/web/webpack.config.js index ce3979f7918..cec4bf044be 100644 --- a/apps/web/webpack.config.js +++ b/apps/web/webpack.config.js @@ -111,6 +111,11 @@ const plugins = [ filename: "sso-connector.html", chunks: ["connectors/sso"], }), + new HtmlWebpackPlugin({ + template: "./src/connectors/redirect.html", + filename: "redirect-connector.html", + chunks: ["connectors/redirect", "styles"], + }), new HtmlWebpackPlugin({ template: "./src/connectors/captcha.html", filename: "captcha-connector.html", @@ -325,6 +330,7 @@ const webpackConfig = { "connectors/sso": "./src/connectors/sso.ts", "connectors/captcha": "./src/connectors/captcha.ts", "connectors/duo-redirect": "./src/connectors/duo-redirect.ts", + "connectors/redirect": "./src/connectors/redirect.ts", styles: ["./src/scss/styles.scss", "./src/scss/tailwind.css"], theme_head: "./src/theme.ts", }, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.html index 457eff37fac..2238fa9fc87 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.html @@ -1,11 +1,6 @@ - diff --git a/libs/angular/src/admin-console/components/collections.component.ts b/libs/angular/src/admin-console/components/collections.component.ts index 4f166286184..d6801aa155e 100644 --- a/libs/angular/src/admin-console/components/collections.component.ts +++ b/libs/angular/src/admin-console/components/collections.component.ts @@ -4,8 +4,6 @@ import { firstValueFrom, map } 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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -27,7 +25,6 @@ export class CollectionsComponent implements OnInit { collectionIds: string[]; collections: CollectionView[] = []; organization: Organization; - restrictProviderAccess: boolean; protected cipherDomain: Cipher; @@ -38,15 +35,11 @@ export class CollectionsComponent implements OnInit { protected cipherService: CipherService, protected organizationService: OrganizationService, private logService: LogService, - private configService: ConfigService, private accountService: AccountService, private toastService: ToastService, ) {} async ngOnInit() { - this.restrictProviderAccess = await this.configService.getFeatureFlag( - FeatureFlag.RestrictProviderAccess, - ); await this.load(); } @@ -76,7 +69,7 @@ export class CollectionsComponent implements OnInit { async submit(): Promise { const selectedCollectionIds = this.collections .filter((c) => { - if (this.organization.canEditAllCiphers(this.restrictProviderAccess)) { + if (this.organization.canEditAllCiphers) { return !!(c as any).checked; } else { return !!(c as any).checked && c.readOnly == null; diff --git a/apps/browser/src/popup/extension-refresh-route-utils.ts b/libs/angular/src/utils/extension-refresh-redirect.ts similarity index 94% rename from apps/browser/src/popup/extension-refresh-route-utils.ts rename to libs/angular/src/utils/extension-refresh-redirect.ts index 9d45d7d656d..f7399d9c278 100644 --- a/apps/browser/src/popup/extension-refresh-route-utils.ts +++ b/libs/angular/src/utils/extension-refresh-redirect.ts @@ -1,5 +1,5 @@ import { inject } from "@angular/core"; -import { Router, UrlTree } from "@angular/router"; +import { UrlTree, Router } from "@angular/router"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 96589fd2b07..45475440d0a 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -13,7 +13,6 @@ import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin- import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { EventType } from "@bitwarden/common/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -92,8 +91,6 @@ export class AddEditComponent implements OnInit, OnDestroy { private personalOwnershipPolicyAppliesToActiveUser: boolean; private previousCipherId: string; - protected restrictProviderAccess = false; - get fido2CredentialCreationDateValue(): string { const dateCreated = this.i18nService.t("dateCreated"); const creationDate = this.datePipe.transform( @@ -182,10 +179,6 @@ export class AddEditComponent implements OnInit, OnDestroy { } async ngOnInit() { - this.restrictProviderAccess = await this.configService.getFeatureFlag( - FeatureFlag.RestrictProviderAccess, - ); - this.policyService .policyAppliesToActiveUser$(PolicyType.PersonalOwnership) .pipe( @@ -683,11 +676,11 @@ export class AddEditComponent implements OnInit, OnDestroy { protected saveCipher(cipher: Cipher) { const isNotClone = this.editMode && !this.cloneMode; - let orgAdmin = this.organization?.canEditAllCiphers(this.restrictProviderAccess); + let orgAdmin = this.organization?.canEditAllCiphers; // if a cipher is unassigned we want to check if they are an admin or have permission to edit any collection if (!cipher.collectionIds) { - orgAdmin = this.organization?.canEditUnassignedCiphers(this.restrictProviderAccess); + orgAdmin = this.organization?.canEditUnassignedCiphers; } return this.cipher.id == null @@ -696,14 +689,14 @@ export class AddEditComponent implements OnInit, OnDestroy { } protected deleteCipher() { - const asAdmin = this.organization?.canEditAllCiphers(this.restrictProviderAccess); + const asAdmin = this.organization?.canEditAllCiphers; return this.cipher.isDeleted ? this.cipherService.deleteWithServer(this.cipher.id, asAdmin) : this.cipherService.softDeleteWithServer(this.cipher.id, asAdmin); } protected restoreCipher() { - const asAdmin = this.organization?.canEditAllCiphers(this.restrictProviderAccess); + const asAdmin = this.organization?.canEditAllCiphers; return this.cipherService.restoreWithServer(this.cipher.id, asAdmin); } diff --git a/libs/angular/src/vault/components/attachments.component.ts b/libs/angular/src/vault/components/attachments.component.ts index e377427eb89..4ae68c9ca9d 100644 --- a/libs/angular/src/vault/components/attachments.component.ts +++ b/libs/angular/src/vault/components/attachments.component.ts @@ -17,7 +17,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; @Directive() export class AttachmentsComponent implements OnInit { @@ -49,6 +49,7 @@ export class AttachmentsComponent implements OnInit { protected dialogService: DialogService, protected billingAccountProfileStateService: BillingAccountProfileStateService, protected accountService: AccountService, + protected toastService: ToastService, ) {} async ngOnInit() { @@ -182,6 +183,11 @@ export class AttachmentsComponent implements OnInit { fileName: attachment.fileName, blobData: decBuf, }); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("fileSavedToDevice"), + }); } catch (e) { this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred")); } diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.html b/libs/auth/src/angular/anon-layout/anon-layout.component.html index 6603fa970d4..9e6c27f6016 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout.component.html +++ b/libs/auth/src/angular/anon-layout/anon-layout.component.html @@ -1,9 +1,12 @@ +
@@ -23,6 +26,7 @@ {{ title }} +
{{ subtitle }}
diff --git a/libs/auth/src/angular/icons/index.ts b/libs/auth/src/angular/icons/index.ts index 3bdbbe4a18d..166b2d1f2ed 100644 --- a/libs/auth/src/angular/icons/index.ts +++ b/libs/auth/src/angular/icons/index.ts @@ -3,5 +3,6 @@ export * from "./bitwarden-shield.icon"; export * from "./lock.icon"; export * from "./registration-check-email.icon"; export * from "./registration-expired-link.icon"; +export * from "./user-lock.icon"; export * from "./user-verification-biometrics-fingerprint.icon"; export * from "./wave.icon"; diff --git a/libs/auth/src/angular/icons/lock.icon.ts b/libs/auth/src/angular/icons/lock.icon.ts index 981b520010a..198733d0dca 100644 --- a/libs/auth/src/angular/icons/lock.icon.ts +++ b/libs/auth/src/angular/icons/lock.icon.ts @@ -2,16 +2,16 @@ import { svgIcon } from "@bitwarden/components"; export const LockIcon = svgIcon` - - - - - - - - - - - + + + + + + + + + + + `; diff --git a/libs/auth/src/angular/icons/user-lock.icon.ts b/libs/auth/src/angular/icons/user-lock.icon.ts new file mode 100644 index 00000000000..fef00a09a92 --- /dev/null +++ b/libs/auth/src/angular/icons/user-lock.icon.ts @@ -0,0 +1,22 @@ +import { svgIcon } from "@bitwarden/components"; + +export const UserLockIcon = svgIcon` + + + + + + + + + + + + + + + + + + +`; diff --git a/libs/auth/src/angular/index.ts b/libs/auth/src/angular/index.ts index 3ba0b8ef71b..d153cf53558 100644 --- a/libs/auth/src/angular/index.ts +++ b/libs/auth/src/angular/index.ts @@ -27,6 +27,9 @@ export * from "./login/default-login.service"; // password callout export * from "./password-callout/password-callout.component"; +// password hint +export * from "./password-hint/password-hint.component"; + // registration export * from "./registration/registration-start/registration-start.component"; export * from "./registration/registration-finish/registration-finish.component"; diff --git a/libs/auth/src/angular/password-hint/password-hint.component.html b/libs/auth/src/angular/password-hint/password-hint.component.html new file mode 100644 index 00000000000..2a811a1b3b7 --- /dev/null +++ b/libs/auth/src/angular/password-hint/password-hint.component.html @@ -0,0 +1,40 @@ + + + + + {{ "accountEmail" | i18n }} + + + + + + + + +
+ +
+ + + + + + diff --git a/libs/auth/src/angular/password-hint/password-hint.component.ts b/libs/auth/src/angular/password-hint/password-hint.component.ts new file mode 100644 index 00000000000..1ae1fd337b0 --- /dev/null +++ b/libs/auth/src/angular/password-hint/password-hint.component.ts @@ -0,0 +1,107 @@ +import { CommonModule } from "@angular/common"; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; +import { Router, RouterModule } from "@angular/router"; +import { firstValueFrom } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { PasswordHintRequest } from "@bitwarden/common/auth/models/request/password-hint.request"; +import { ClientType } from "@bitwarden/common/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { + AsyncActionsModule, + ButtonModule, + FormFieldModule, + ToastService, +} from "@bitwarden/components"; + +@Component({ + standalone: true, + templateUrl: "./password-hint.component.html", + imports: [ + AsyncActionsModule, + ButtonModule, + CommonModule, + FormFieldModule, + JslibModule, + ReactiveFormsModule, + RouterModule, + ], +}) +export class PasswordHintComponent implements OnInit { + protected clientType: ClientType; + + protected formGroup = this.formBuilder.group({ + email: ["", [Validators.required, Validators.email]], + }); + + protected get email() { + return this.formGroup.controls.email.value; + } + + constructor( + private apiService: ApiService, + private formBuilder: FormBuilder, + private i18nService: I18nService, + private loginEmailService: LoginEmailServiceAbstraction, + private platformUtilsService: PlatformUtilsService, + private toastService: ToastService, + private router: Router, + ) { + this.clientType = this.platformUtilsService.getClientType(); + } + + async ngOnInit(): Promise { + const email = (await firstValueFrom(this.loginEmailService.loginEmail$)) ?? ""; + this.formGroup.controls.email.setValue(email); + } + + submit = async () => { + const isEmailValid = this.validateEmailOrShowToast(this.email); + if (!isEmailValid) { + return; + } + + await this.apiService.postPasswordHint(new PasswordHintRequest(this.email)); + + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("masterPassSent"), + }); + + await this.router.navigate(["login"]); + }; + + protected async cancel() { + this.loginEmailService.setLoginEmail(this.email); + await this.router.navigate(["login"]); + } + + private validateEmailOrShowToast(email: string): boolean { + // If email is null or empty, show error toast and return false + if (email == null || email === "") { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("emailRequired"), + }); + return false; + } + + // If not a valid email format, show error toast and return false + if (email.indexOf("@") === -1) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("invalidEmail"), + }); + return false; + } + + return true; // email is valid + } +} diff --git a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts index 85faf871447..63b01be9953 100644 --- a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts +++ b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts @@ -23,6 +23,9 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi email: string, passwordInputResult: PasswordInputResult, emailVerificationToken?: string, + orgSponsoredFreeFamilyPlanToken?: string, + acceptEmergencyAccessInviteToken?: string, + emergencyAccessId?: string, ): Promise { const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey( passwordInputResult.masterKey, @@ -35,10 +38,13 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi const registerRequest = await this.buildRegisterRequest( email, - emailVerificationToken, passwordInputResult, newEncUserKey.encryptedString, userAsymmetricKeys, + emailVerificationToken, + orgSponsoredFreeFamilyPlanToken, + acceptEmergencyAccessInviteToken, + emergencyAccessId, ); const capchaBypassToken = await this.accountApiService.registerFinish(registerRequest); @@ -48,19 +54,21 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi protected async buildRegisterRequest( email: string, - emailVerificationToken: string, passwordInputResult: PasswordInputResult, encryptedUserKey: EncryptedString, userAsymmetricKeys: [string, EncString], + emailVerificationToken?: string, + orgSponsoredFreeFamilyPlanToken?: string, // web only + acceptEmergencyAccessInviteToken?: string, // web only + emergencyAccessId?: string, // web only ): Promise { const userAsymmetricKeysRequest = new KeysRequest( userAsymmetricKeys[0], userAsymmetricKeys[1].encryptedString, ); - return new RegisterFinishRequest( + const registerFinishRequest = new RegisterFinishRequest( email, - emailVerificationToken, passwordInputResult.masterKeyHash, passwordInputResult.hint, encryptedUserKey, @@ -68,5 +76,11 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi passwordInputResult.kdfConfig.kdfType, passwordInputResult.kdfConfig.iterations, ); + + if (emailVerificationToken) { + registerFinishRequest.emailVerificationToken = emailVerificationToken; + } + + return registerFinishRequest; } } diff --git a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts index 3c7e31b5de6..ef40d95dce9 100644 --- a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts +++ b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts @@ -33,11 +33,21 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { submitting = false; email: string; - // Note: this token is the email verification token. It is always supplied as a query param, but + // Note: this token is the email verification token. When it is supplied as a query param, // it either comes from the email verification email or, if email verification is disabled server side // via global settings, it comes directly from the registration-start component directly. + // It is not provided when the user is coming from another emailed invite (ex: org invite or enterprise + // org sponsored free family plan invite). emailVerificationToken: string; + // this token is provided when the user is coming from an emailed invite to + // setup a free family plan sponsored by an organization but they don't have an account yet. + orgSponsoredFreeFamilyPlanToken: string; + + // this token is provided when the user is coming from an emailed invite to accept an emergency access invite + acceptEmergencyAccessInviteToken: string; + emergencyAccessId: string; + masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null; constructor( @@ -69,6 +79,15 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { if (qParams.token != null) { this.emailVerificationToken = qParams.token; } + + if (qParams.orgSponsoredFreeFamilyPlanToken != null) { + this.orgSponsoredFreeFamilyPlanToken = qParams.orgSponsoredFreeFamilyPlanToken; + } + + if (qParams.acceptEmergencyAccessInviteToken != null && qParams.emergencyAccessId) { + this.acceptEmergencyAccessInviteToken = qParams.acceptEmergencyAccessInviteToken; + this.emergencyAccessId = qParams.emergencyAccessId; + } }), switchMap((qParams: Params) => { if ( @@ -100,6 +119,9 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { this.email, passwordInputResult, this.emailVerificationToken, + this.orgSponsoredFreeFamilyPlanToken, + this.acceptEmergencyAccessInviteToken, + this.emergencyAccessId, ); } catch (e) { this.validationService.showError(e); diff --git a/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts b/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts index 63e23182f60..b585aa78ed6 100644 --- a/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts +++ b/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts @@ -14,12 +14,18 @@ export abstract class RegistrationFinishService { * * @param email The email address of the user. * @param passwordInputResult The password input result. - * @param emailVerificationToken The optional email verification token. Not present in org invite scenarios. + * @param emailVerificationToken The optional email verification token. Not present in emailed invite scenarios (ex: org invite). + * @param orgSponsoredFreeFamilyPlanToken The optional org sponsored free family plan token. + * @param acceptEmergencyAccessInviteToken The optional accept emergency access invite token. + * @param emergencyAccessId The optional emergency access id which is required to validate the emergency access invite token. * Returns a promise which resolves to the captcha bypass token string upon a successful account creation. */ abstract finishRegistration( email: string, passwordInputResult: PasswordInputResult, emailVerificationToken?: string, + orgSponsoredFreeFamilyPlanToken?: string, + acceptEmergencyAccessInviteToken?: string, + emergencyAccessId?: string, ): Promise; } diff --git a/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts b/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts index 218051b9e4b..0cea2aee539 100644 --- a/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts +++ b/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts @@ -113,6 +113,9 @@ export abstract class OrganizationService { * https://bitwarden.atlassian.net/browse/AC-2252. */ getFromState: (id: string) => Promise; + /** + * Emits true if the user can create or manage a Free Bitwarden Families sponsorship. + */ canManageSponsorships$: Observable; hasOrganizations: () => Promise; get$: (id: string) => Observable; diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index 490c799ad18..8c28bcb4935 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -183,14 +183,7 @@ export class Organization { return this.isAdmin || this.permissions.editAnyCollection; } - canEditUnassignedCiphers(restrictProviderAccessFlagEnabled: boolean) { - // Providers can access items until the restrictProviderAccess flag is enabled - // After the flag is enabled and removed, this block will be deleted - // so that they permanently lose access to items - if (this.isProviderUser && !restrictProviderAccessFlagEnabled) { - return true; - } - + get canEditUnassignedCiphers() { return ( this.type === OrganizationUserType.Admin || this.type === OrganizationUserType.Owner || @@ -198,14 +191,7 @@ export class Organization { ); } - canEditAllCiphers(restrictProviderAccessFlagEnabled: boolean) { - // Providers can access items until the restrictProviderAccess flag is enabled - // After the flag is enabled and removed, this block will be deleted - // so that they permanently lose access to items - if (this.isProviderUser && !restrictProviderAccessFlagEnabled) { - return true; - } - + get canEditAllCiphers() { // The allowAdminAccessToAllCollectionItems flag can restrict admins // Custom users with canEditAnyCollection are not affected by allowAdminAccessToAllCollectionItems flag return ( diff --git a/libs/common/src/auth/abstractions/auth.service.ts b/libs/common/src/auth/abstractions/auth.service.ts index 36d5d219b26..df408e76f8b 100644 --- a/libs/common/src/auth/abstractions/auth.service.ts +++ b/libs/common/src/auth/abstractions/auth.service.ts @@ -16,5 +16,5 @@ export abstract class AuthService { abstract authStatusFor$(userId: UserId): Observable; /** @deprecated use {@link activeAccountStatus$} instead */ abstract getAuthStatus: (userId?: string) => Promise; - abstract logOut: (callback: () => void) => void; + abstract logOut: (callback: () => void, userId?: string) => void; } diff --git a/libs/common/src/auth/models/request/registration/register-finish.request.ts b/libs/common/src/auth/models/request/registration/register-finish.request.ts index 22275fb228e..6a36bf82139 100644 --- a/libs/common/src/auth/models/request/registration/register-finish.request.ts +++ b/libs/common/src/auth/models/request/registration/register-finish.request.ts @@ -5,7 +5,6 @@ import { EncryptedString } from "../../../../platform/models/domain/enc-string"; export class RegisterFinishRequest { constructor( public email: string, - public emailVerificationToken: string, public masterPasswordHash: string, public masterPasswordHint: string, @@ -18,6 +17,11 @@ export class RegisterFinishRequest { public kdfMemory?: number, public kdfParallelism?: number, + public emailVerificationToken?: string, + public orgSponsoredFreeFamilyPlanToken?: string, + public acceptEmergencyAccessInviteToken?: string, + public acceptEmergencyAccessId?: string, + // Org Invite data (only applies on web) public organizationUserId?: string, public orgInviteToken?: string, diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 25e7b92edf2..307da55a5eb 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -93,8 +93,8 @@ export class AuthService implements AuthServiceAbstraction { return await firstValueFrom(this.authStatusFor$(userId as UserId)); } - logOut(callback: () => void) { + logOut(callback: () => void, userId?: string): void { callback(); - this.messageSender.send("loggedOut"); + this.messageSender.send("loggedOut", { userId }); } } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 221c2f469a7..26390a66fca 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -12,7 +12,6 @@ export enum FeatureFlag { EnableDeleteProvider = "AC-1218-delete-provider", ExtensionRefresh = "extension-refresh", PersistPopupView = "persist-popup-view", - RestrictProviderAccess = "restrict-provider-access", PM4154_BulkEncryptionService = "PM-4154-bulk-encryption-service", UseTreeWalkerApiForPageDetailsCollection = "use-tree-walker-api-for-page-details-collection", EmailVerification = "email-verification", @@ -59,7 +58,6 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.EnableDeleteProvider]: FALSE, [FeatureFlag.ExtensionRefresh]: FALSE, [FeatureFlag.PersistPopupView]: FALSE, - [FeatureFlag.RestrictProviderAccess]: FALSE, [FeatureFlag.PM4154_BulkEncryptionService]: FALSE, [FeatureFlag.UseTreeWalkerApiForPageDetailsCollection]: FALSE, [FeatureFlag.EmailVerification]: FALSE, diff --git a/libs/common/src/platform/misc/lazy.ts b/libs/common/src/platform/misc/lazy.ts index fb85b93678d..96b4d033646 100644 --- a/libs/common/src/platform/misc/lazy.ts +++ b/libs/common/src/platform/misc/lazy.ts @@ -1,6 +1,7 @@ +const NoValue = Symbol("NoValue"); + export class Lazy { - private _value: T | undefined = undefined; - private _isCreated = false; + private _value: T | typeof NoValue = NoValue; constructor(private readonly factory: () => T) {} @@ -10,11 +11,10 @@ export class Lazy { * @returns The value produced by your factory. */ get(): T { - if (!this._isCreated) { - this._value = this.factory(); - this._isCreated = true; + if (this._value === NoValue) { + return (this._value = this.factory()); } - return this._value as T; + return this._value; } } diff --git a/libs/common/src/services/vault-timeout/vault-timeout.service.ts b/libs/common/src/services/vault-timeout/vault-timeout.service.ts index d9efef44f42..c40e4687b75 100644 --- a/libs/common/src/services/vault-timeout/vault-timeout.service.ts +++ b/libs/common/src/services/vault-timeout/vault-timeout.service.ts @@ -1,4 +1,4 @@ -import { combineLatest, filter, firstValueFrom, map, switchMap, timeout } from "rxjs"; +import { combineLatest, concatMap, filter, firstValueFrom, map, timeout } from "rxjs"; import { LogoutReason } from "@bitwarden/auth/common"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -79,7 +79,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { this.accountService.activeAccount$, this.accountService.accountActivity$, ]).pipe( - switchMap(async ([activeAccount, accountActivity]) => { + concatMap(async ([activeAccount, accountActivity]) => { const activeUserId = activeAccount?.id; for (const userIdString in accountActivity) { const userId = userIdString as UserId; diff --git a/libs/common/src/tools/dependencies.ts b/libs/common/src/tools/dependencies.ts index 0488291b446..8b860591d54 100644 --- a/libs/common/src/tools/dependencies.ts +++ b/libs/common/src/tools/dependencies.ts @@ -77,7 +77,7 @@ export type SingleUserDependency = { export type OnDependency = { /** The stream that controls emissions */ - on$: Observable; + on$: Observable; }; /** A pattern for types that emit when a dependency is `true`. diff --git a/libs/common/src/tools/integration/rpc/integration-request.ts b/libs/common/src/tools/integration/rpc/integration-request.ts index 84a7b517abe..9f1808a632b 100644 --- a/libs/common/src/tools/integration/rpc/integration-request.ts +++ b/libs/common/src/tools/integration/rpc/integration-request.ts @@ -1,11 +1,6 @@ +import { GenerationRequest } from "../../types"; + /** Options that provide contextual information about the application state * when an integration is invoked. */ -export type IntegrationRequest = { - /** @param website The domain of the website the requested integration is used - * within. This should be set to `null` when the request is not specific - * to any website. - * @remarks this field contains sensitive data - */ - website: string | null; -}; +export type IntegrationRequest = Partial; diff --git a/libs/common/src/tools/types.ts b/libs/common/src/tools/types.ts index 0c2f2832eaa..83d69edb06c 100644 --- a/libs/common/src/tools/types.ts +++ b/libs/common/src/tools/types.ts @@ -46,3 +46,20 @@ export type Constraints = { /** utility type for methods that evaluate constraints generically. */ export type AnyConstraint = PrimitiveConstraint & StringConstraints & NumberConstraints; + +/** Options that provide contextual information about the application state + * when a generator is invoked. + */ +export type VaultItemRequest = { + /** The domain of the website the requested credential is used + * within. This should be set to `null` when the request is not specific + * to any website. + * @remarks this field contains sensitive data + */ + website: string | null; +}; + +/** Options that provide contextual information about the application state + * when a generator is invoked. + */ +export type GenerationRequest = Partial; diff --git a/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts b/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts index d29ff71290a..1762400b3dd 100644 --- a/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts +++ b/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts @@ -5,4 +5,5 @@ export class FolderApiServiceAbstraction { save: (folder: Folder) => Promise; delete: (id: string) => Promise; get: (id: string) => Promise; + deleteAll: () => Promise; } diff --git a/libs/common/src/vault/models/view/collection.view.ts b/libs/common/src/vault/models/view/collection.view.ts index 0a05007b66f..873f538ca64 100644 --- a/libs/common/src/vault/models/view/collection.view.ts +++ b/libs/common/src/vault/models/view/collection.view.ts @@ -38,18 +38,14 @@ export class CollectionView implements View, ITreeNodeObject { } } - canEditItems(org: Organization, restrictProviderAccess: boolean): boolean { + canEditItems(org: Organization): boolean { if (org != null && org.id !== this.organizationId) { throw new Error( "Id of the organization provided does not match the org id of the collection.", ); } - return ( - org?.canEditAllCiphers(restrictProviderAccess) || - this.manage || - (this.assigned && !this.readOnly) - ); + return org?.canEditAllCiphers || this.manage || (this.assigned && !this.readOnly); } /** diff --git a/libs/common/src/vault/services/folder/folder-api.service.ts b/libs/common/src/vault/services/folder/folder-api.service.ts index c618c958720..e46df37c176 100644 --- a/libs/common/src/vault/services/folder/folder-api.service.ts +++ b/libs/common/src/vault/services/folder/folder-api.service.ts @@ -32,6 +32,11 @@ export class FolderApiService implements FolderApiServiceAbstraction { await this.folderService.delete(id); } + async deleteAll(): Promise { + await this.apiService.send("DELETE", "/folders/all", null, true, false); + await this.folderService.clear(); + } + async get(id: string): Promise { const r = await this.apiService.send("GET", "/folders/" + id, null, true, true); return new FolderResponse(r); diff --git a/libs/components/src/icon/icons/no-results.ts b/libs/components/src/icon/icons/no-results.ts index 02e03f4c4ec..7ed886a06e9 100644 --- a/libs/components/src/icon/icons/no-results.ts +++ b/libs/components/src/icon/icons/no-results.ts @@ -2,17 +2,17 @@ import { svgIcon } from "../icon"; export const NoResults = svgIcon` - - - - - - - - - - - + + + + + + + + + + + `; diff --git a/libs/components/src/tw-theme.css b/libs/components/src/tw-theme.css index 6234ba380bc..6e5bb32edad 100644 --- a/libs/components/src/tw-theme.css +++ b/libs/components/src/tw-theme.css @@ -42,6 +42,9 @@ --color-info-600: 85 85 85; --color-info-700: 59 58 58; + --color-art-primary: 2 15 102; + --color-art-accent: 85 85 85; + --color-text-main: 33 37 41; --color-text-muted: 109 117 126; --color-text-contrast: 255 255 255; @@ -90,6 +93,9 @@ --color-info-600: 164 176 198; --color-info-700: 209 215 226; + --color-art-primary: 226 227 228; + --color-art-accent: 164 176 198; + --color-text-main: 255 255 255; --color-text-muted: 186 192 206; --color-text-contrast: 25 30 38; diff --git a/libs/components/tailwind.config.base.js b/libs/components/tailwind.config.base.js index 236baed74c8..537e731f14f 100644 --- a/libs/components/tailwind.config.base.js +++ b/libs/components/tailwind.config.base.js @@ -54,6 +54,10 @@ module.exports = { 600: rgba("--color-info-600"), 700: rgba("--color-info-700"), }, + art: { + primary: rgba("--color-art-primary"), + accent: rgba("--color-art-accent"), + }, text: { main: rgba("--color-text-main"), muted: rgba("--color-text-muted"), diff --git a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html index 626934b20e8..7555b206976 100644 --- a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html +++ b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html @@ -87,7 +87,9 @@ [disabled]="!filePassword" appStopClick bitSuffix - (click)="copyPasswordToClipboard()" + [appCopyClick]="filePassword" + [valueLabel]="'password' | i18n" + showToast > {{ "exportPasswordDescription" | i18n }} diff --git a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts index d83d189cd79..e4f5ec9d32d 100644 --- a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts +++ b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts @@ -121,7 +121,6 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit { encryptedExportType = EncryptedExportType; protected showFilePassword: boolean; - filePasswordValue: string = null; private _disabledByPolicy = false; organizations$: Observable; @@ -278,18 +277,9 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit { generatePassword = async () => { const [options] = await this.passwordGenerationService.getOptions(); - this.filePasswordValue = await this.passwordGenerationService.generatePassword(options); - this.exportForm.get("filePassword").setValue(this.filePasswordValue); - this.exportForm.get("confirmFilePassword").setValue(this.filePasswordValue); - }; - - copyPasswordToClipboard = async () => { - this.platformUtilsService.copyToClipboard(this.filePasswordValue); - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("valueCopied", this.i18nService.t("password")), - }); + const generatedPassword = await this.passwordGenerationService.generatePassword(options); + this.exportForm.get("filePassword").setValue(generatedPassword); + this.exportForm.get("confirmFilePassword").setValue(generatedPassword); }; submit = async () => { diff --git a/libs/tools/generator/components/src/dependencies.ts b/libs/tools/generator/components/src/dependencies.ts index 927c3811c86..d96ff0db8d6 100644 --- a/libs/tools/generator/components/src/dependencies.ts +++ b/libs/tools/generator/components/src/dependencies.ts @@ -4,41 +4,60 @@ import { ReactiveFormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; +import { SafeInjectionToken } from "@bitwarden/angular/services/injection-tokens"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { StateProvider } from "@bitwarden/common/platform/state"; import { CardComponent, CheckboxModule, ColorPasswordModule, FormFieldModule, + IconButtonModule, InputModule, + ItemModule, SectionComponent, SectionHeaderComponent, + ToggleGroupModule, } from "@bitwarden/components"; -import { CredentialGeneratorService } from "@bitwarden/generator-core"; +import { + createRandomizer, + CredentialGeneratorService, + Randomizer, +} from "@bitwarden/generator-core"; + +const RANDOMIZER = new SafeInjectionToken("Randomizer"); /** Shared module containing generator component dependencies */ @NgModule({ - imports: [SectionComponent, SectionHeaderComponent, CardComponent], + imports: [CardComponent, SectionComponent, SectionHeaderComponent], exports: [ + CardComponent, + CheckboxModule, + CommonModule, + ColorPasswordModule, + FormFieldModule, + IconButtonModule, + InputModule, + ItemModule, JslibModule, JslibServicesModule, - FormFieldModule, - CommonModule, ReactiveFormsModule, - ColorPasswordModule, - InputModule, - CheckboxModule, SectionComponent, SectionHeaderComponent, - CardComponent, + ToggleGroupModule, ], providers: [ + safeProvider({ + provide: RANDOMIZER, + useFactory: createRandomizer, + deps: [CryptoService], + }), safeProvider({ provide: CredentialGeneratorService, useClass: CredentialGeneratorService, - deps: [StateProvider, PolicyService], + deps: [RANDOMIZER, StateProvider, PolicyService], }), ], declarations: [], diff --git a/libs/tools/generator/components/src/index.ts b/libs/tools/generator/components/src/index.ts index 5915c5d59f6..4423f8a1ec9 100644 --- a/libs/tools/generator/components/src/index.ts +++ b/libs/tools/generator/components/src/index.ts @@ -2,3 +2,4 @@ export { PassphraseSettingsComponent } from "./passphrase-settings.component"; export { CredentialGeneratorHistoryComponent } from "./credential-generator-history.component"; export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component"; export { PasswordSettingsComponent } from "./password-settings.component"; +export { PasswordGeneratorComponent } from "./password-generator.component"; diff --git a/libs/tools/generator/components/src/password-generator.component.html b/libs/tools/generator/components/src/password-generator.component.html new file mode 100644 index 00000000000..db5a1ed379b --- /dev/null +++ b/libs/tools/generator/components/src/password-generator.component.html @@ -0,0 +1,44 @@ + + + {{ "password" | i18n }} + + + {{ "passphrase" | i18n }} + + + +
+ +
+
+ + +
+
+ + diff --git a/libs/tools/generator/components/src/password-generator.component.ts b/libs/tools/generator/components/src/password-generator.component.ts new file mode 100644 index 00000000000..6c84d83c4cf --- /dev/null +++ b/libs/tools/generator/components/src/password-generator.component.ts @@ -0,0 +1,117 @@ +import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from "@angular/core"; +import { BehaviorSubject, distinctUntilChanged, map, Subject, switchMap, takeUntil } from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { CredentialGeneratorService, Generators, GeneratorType } from "@bitwarden/generator-core"; +import { GeneratedCredential } from "@bitwarden/generator-history"; + +import { DependenciesModule } from "./dependencies"; +import { PassphraseSettingsComponent } from "./passphrase-settings.component"; +import { PasswordSettingsComponent } from "./password-settings.component"; + +/** Options group for passwords */ +@Component({ + standalone: true, + selector: "bit-password-generator", + templateUrl: "password-generator.component.html", + imports: [DependenciesModule, PasswordSettingsComponent, PassphraseSettingsComponent], +}) +export class PasswordGeneratorComponent implements OnInit, OnDestroy { + constructor( + private generatorService: CredentialGeneratorService, + private accountService: AccountService, + private zone: NgZone, + ) {} + + /** Binds the passphrase component to a specific user's settings. + * When this input is not provided, the form binds to the active + * user + */ + @Input() + userId: UserId | null; + + /** tracks the currently selected credential type */ + protected credentialType$ = new BehaviorSubject("password"); + + /** Emits the last generated value. */ + protected readonly value$ = new BehaviorSubject(""); + + /** Emits when the userId changes */ + protected readonly userId$ = new BehaviorSubject(null); + + /** Emits when a new credential is requested */ + protected readonly generate$ = new Subject(); + + /** Tracks changes to the selected credential type + * @param type the new credential type + */ + protected onCredentialTypeChanged(type: GeneratorType) { + if (this.credentialType$.value !== type) { + this.credentialType$.next(type); + this.generate$.next(); + } + } + + /** Emits credentials created from a generation request. */ + @Output() + readonly onGenerated = new EventEmitter(); + + async ngOnInit() { + if (this.userId) { + this.userId$.next(this.userId); + } else { + this.accountService.activeAccount$ + .pipe( + map((acct) => acct.id), + distinctUntilChanged(), + takeUntil(this.destroyed), + ) + .subscribe(this.userId$); + } + + this.credentialType$ + .pipe( + switchMap((type) => this.typeToGenerator$(type)), + takeUntil(this.destroyed), + ) + .subscribe((generated) => { + // update subjects within the angular zone so that the + // template bindings refresh immediately + this.zone.run(() => { + this.onGenerated.next(generated); + this.value$.next(generated.credential); + }); + }); + } + + private typeToGenerator$(type: GeneratorType) { + const dependencies = { + on$: this.generate$, + userId$: this.userId$, + }; + + switch (type) { + case "password": + return this.generatorService.generate$(Generators.Password, dependencies); + + case "passphrase": + return this.generatorService.generate$(Generators.Passphrase, dependencies); + default: + throw new Error(`Invalid generator type: "${type}"`); + } + } + + private readonly destroyed = new Subject(); + ngOnDestroy(): void { + // tear down subscriptions + this.destroyed.complete(); + + // finalize subjects + this.generate$.complete(); + this.value$.complete(); + + // finalize component bindings + this.onGenerated.complete(); + } +} diff --git a/libs/tools/generator/core/src/data/generators.ts b/libs/tools/generator/core/src/data/generators.ts index 94e289be03e..f71d484f9c2 100644 --- a/libs/tools/generator/core/src/data/generators.ts +++ b/libs/tools/generator/core/src/data/generators.ts @@ -1,5 +1,8 @@ +import { Randomizer } from "../abstractions"; +import { PasswordRandomizer } from "../engine"; import { PASSPHRASE_SETTINGS, PASSWORD_SETTINGS } from "../strategies/storage"; import { + CredentialGenerator, PassphraseGenerationOptions, PassphraseGeneratorPolicy, PasswordGenerationOptions, @@ -14,6 +17,12 @@ import { DefaultPasswordGenerationOptions } from "./default-password-generation- import { Policies } from "./policies"; const PASSPHRASE = Object.freeze({ + category: "passphrase", + engine: { + create(randomizer: Randomizer): CredentialGenerator { + return new PasswordRandomizer(randomizer); + }, + }, settings: { initial: DefaultPassphraseGenerationOptions, constraints: { @@ -32,6 +41,12 @@ const PASSPHRASE = Object.freeze({ >); const PASSWORD = Object.freeze({ + category: "password", + engine: { + create(randomizer: Randomizer): CredentialGenerator { + return new PasswordRandomizer(randomizer); + }, + }, settings: { initial: DefaultPasswordGenerationOptions, constraints: { diff --git a/libs/tools/generator/core/src/engine/password-randomizer.spec.ts b/libs/tools/generator/core/src/engine/password-randomizer.spec.ts index bbc31a4a293..fca98855fd5 100644 --- a/libs/tools/generator/core/src/engine/password-randomizer.spec.ts +++ b/libs/tools/generator/core/src/engine/password-randomizer.spec.ts @@ -335,4 +335,40 @@ describe("PasswordRandomizer", () => { expect(result).toEqual("foo-foo"); }); }); + + describe("generate", () => { + it("processes password generation options", async () => { + const password = new PasswordRandomizer(randomizer); + + const result = await password.generate( + {}, + { + length: 10, + }, + ); + + expect(result.category).toEqual("password"); + }); + + it("processes passphrase generation options", async () => { + const password = new PasswordRandomizer(randomizer); + + const result = await password.generate( + {}, + { + numWords: 10, + }, + ); + + expect(result.category).toEqual("passphrase"); + }); + + it("throws when it cannot recognize the options type", async () => { + const password = new PasswordRandomizer(randomizer); + + const result = password.generate({}, {}); + + await expect(result).rejects.toBeInstanceOf(Error); + }); + }); }); diff --git a/libs/tools/generator/core/src/engine/password-randomizer.ts b/libs/tools/generator/core/src/engine/password-randomizer.ts index 438ea8b8b47..c3a2e2b5d93 100644 --- a/libs/tools/generator/core/src/engine/password-randomizer.ts +++ b/libs/tools/generator/core/src/engine/password-randomizer.ts @@ -1,13 +1,26 @@ import { EFFLongWordList } from "@bitwarden/common/platform/misc/wordlist"; +import { GenerationRequest } from "@bitwarden/common/tools/types"; + +import { + CredentialGenerator, + GeneratedCredential, + PassphraseGenerationOptions, + PasswordGenerationOptions, +} from "../types"; +import { optionsToEffWordListRequest, optionsToRandomAsciiRequest } from "../util"; import { Randomizer } from "./abstractions"; import { Ascii } from "./data"; import { CharacterSet, EffWordListRequest, RandomAsciiRequest } from "./types"; /** Generation algorithms that produce randomized secrets */ -export class PasswordRandomizer { +export class PasswordRandomizer + implements + CredentialGenerator, + CredentialGenerator +{ /** Instantiates the password randomizer - * @param random data source for random data + * @param randomizer data source for random data */ constructor(private randomizer: Randomizer) {} @@ -52,6 +65,41 @@ export class PasswordRandomizer { return wordList.join(request.separator); } + + generate( + request: GenerationRequest, + settings: PasswordGenerationOptions, + ): Promise; + generate( + request: GenerationRequest, + settings: PassphraseGenerationOptions, + ): Promise; + async generate( + _request: GenerationRequest, + settings: PasswordGenerationOptions | PassphraseGenerationOptions, + ) { + if (isPasswordGenerationOptions(settings)) { + const request = optionsToRandomAsciiRequest(settings); + const password = await this.randomAscii(request); + + return new GeneratedCredential(password, "password", Date.now()); + } else if (isPassphraseGenerationOptions(settings)) { + const request = optionsToEffWordListRequest(settings); + const passphrase = await this.randomEffLongWords(request); + + return new GeneratedCredential(passphrase, "passphrase", Date.now()); + } + + throw new Error("Invalid settings received by generator."); + } +} + +function isPasswordGenerationOptions(settings: any): settings is PasswordGenerationOptions { + return "length" in (settings ?? {}); +} + +function isPassphraseGenerationOptions(settings: any): settings is PassphraseGenerationOptions { + return "numWords" in (settings ?? {}); } // given a generator request, convert each of its `number | undefined` properties diff --git a/libs/tools/generator/core/src/services/credential-generator.service.spec.ts b/libs/tools/generator/core/src/services/credential-generator.service.spec.ts index 31f5134918b..5b784b3d07b 100644 --- a/libs/tools/generator/core/src/services/credential-generator.service.spec.ts +++ b/libs/tools/generator/core/src/services/credential-generator.service.spec.ts @@ -1,5 +1,5 @@ import { mock } from "jest-mock-extended"; -import { BehaviorSubject, firstValueFrom } from "rxjs"; +import { BehaviorSubject, filter, firstValueFrom, Subject } from "rxjs"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -8,9 +8,14 @@ import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/st import { Constraints } from "@bitwarden/common/tools/types"; import { OrganizationId, PolicyId, UserId } from "@bitwarden/common/types/guid"; -import { FakeStateProvider, FakeAccountService, awaitAsync } from "../../../../../common/spec"; -import { PolicyEvaluator } from "../abstractions"; -import { CredentialGeneratorConfiguration } from "../types"; +import { + FakeStateProvider, + FakeAccountService, + awaitAsync, + ObservableTracker, +} from "../../../../../common/spec"; +import { PolicyEvaluator, Randomizer } from "../abstractions"; +import { CredentialGeneratorConfiguration, GeneratedCredential } from "../types"; import { CredentialGeneratorService } from "./credential-generator.service"; @@ -34,8 +39,23 @@ const somePolicy = new Policy({ enabled: true, }); +const SomeTime = new Date(1); +const SomeCategory = "passphrase"; + // fake the configuration const SomeConfiguration: CredentialGeneratorConfiguration = { + category: SomeCategory, + engine: { + create: (randomizer) => { + return { + generate: (request, settings) => { + const credential = request.website ? `${request.website}|${settings.foo}` : settings.foo; + const result = new GeneratedCredential(credential, SomeCategory, SomeTime); + return Promise.resolve(result); + }, + }; + }, + }, settings: { initial: { foo: "initial" }, constraints: { foo: {} }, @@ -87,6 +107,9 @@ const accountService = new FakeAccountService({ // fake state const stateProvider = new FakeStateProvider(accountService); +// fake randomizer +const randomizer = mock(); + describe("CredentialGeneratorService", () => { beforeEach(async () => { await accountService.switchAccount(SomeUser); @@ -94,10 +117,242 @@ describe("CredentialGeneratorService", () => { jest.clearAllMocks(); }); + describe("generate$", () => { + it("emits a generation for the active user when subscribed", async () => { + const settings = { foo: "value" }; + await stateProvider.setUserState(SettingsKey, settings, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generated = new ObservableTracker(generator.generate$(SomeConfiguration)); + + const result = await generated.expectEmission(); + + expect(result).toEqual(new GeneratedCredential("value", SomeCategory, SomeTime)); + }); + + it("follows the active user", async () => { + const someSettings = { foo: "some value" }; + const anotherSettings = { foo: "another value" }; + await stateProvider.setUserState(SettingsKey, someSettings, SomeUser); + await stateProvider.setUserState(SettingsKey, anotherSettings, AnotherUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generated = new ObservableTracker(generator.generate$(SomeConfiguration)); + + await accountService.switchAccount(AnotherUser); + await generated.pauseUntilReceived(2); + generated.unsubscribe(); + + expect(generated.emissions).toEqual([ + new GeneratedCredential("some value", SomeCategory, SomeTime), + new GeneratedCredential("another value", SomeCategory, SomeTime), + ]); + }); + + it("emits a generation when the settings change", async () => { + const someSettings = { foo: "some value" }; + const anotherSettings = { foo: "another value" }; + await stateProvider.setUserState(SettingsKey, someSettings, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generated = new ObservableTracker(generator.generate$(SomeConfiguration)); + + await stateProvider.setUserState(SettingsKey, anotherSettings, SomeUser); + await generated.pauseUntilReceived(2); + generated.unsubscribe(); + + expect(generated.emissions).toEqual([ + new GeneratedCredential("some value", SomeCategory, SomeTime), + new GeneratedCredential("another value", SomeCategory, SomeTime), + ]); + }); + + // FIXME: test these when the fake state provider can create the required emissions + it.todo("errors when the settings error"); + it.todo("completes when the settings complete"); + + it("includes `website$`'s last emitted value", async () => { + const settings = { foo: "value" }; + await stateProvider.setUserState(SettingsKey, settings, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const website$ = new BehaviorSubject("some website"); + const generated = new ObservableTracker(generator.generate$(SomeConfiguration, { website$ })); + + const result = await generated.expectEmission(); + + expect(result).toEqual(new GeneratedCredential("some website|value", SomeCategory, SomeTime)); + }); + + it("errors when `website$` errors", async () => { + await stateProvider.setUserState(SettingsKey, null, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const website$ = new BehaviorSubject("some website"); + let error = null; + + generator.generate$(SomeConfiguration, { website$ }).subscribe({ + error: (e: unknown) => { + error = e; + }, + }); + website$.error({ some: "error" }); + await awaitAsync(); + + expect(error).toEqual({ some: "error" }); + }); + + it("completes when `website$` completes", async () => { + await stateProvider.setUserState(SettingsKey, null, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const website$ = new BehaviorSubject("some website"); + let completed = false; + + generator.generate$(SomeConfiguration, { website$ }).subscribe({ + complete: () => { + completed = true; + }, + }); + website$.complete(); + await awaitAsync(); + + expect(completed).toBeTruthy(); + }); + + it("emits a generation for a specific user when `user$` supplied", async () => { + await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); + await stateProvider.setUserState(SettingsKey, { foo: "another" }, AnotherUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const userId$ = new BehaviorSubject(AnotherUser).asObservable(); + const generated = new ObservableTracker(generator.generate$(SomeConfiguration, { userId$ })); + + const result = await generated.expectEmission(); + + expect(result).toEqual(new GeneratedCredential("another", SomeCategory, SomeTime)); + }); + + it("emits a generation for a specific user when `user$` emits", async () => { + await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); + await stateProvider.setUserState(SettingsKey, { foo: "another" }, AnotherUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const userId = new BehaviorSubject(SomeUser); + const userId$ = userId.pipe(filter((u) => !!u)); + const generated = new ObservableTracker(generator.generate$(SomeConfiguration, { userId$ })); + + userId.next(AnotherUser); + const result = await generated.pauseUntilReceived(2); + + expect(result).toEqual([ + new GeneratedCredential("value", SomeCategory, SomeTime), + new GeneratedCredential("another", SomeCategory, SomeTime), + ]); + }); + + it("errors when `user$` errors", async () => { + await stateProvider.setUserState(SettingsKey, null, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const userId$ = new BehaviorSubject(SomeUser); + let error = null; + + generator.generate$(SomeConfiguration, { userId$ }).subscribe({ + error: (e: unknown) => { + error = e; + }, + }); + userId$.error({ some: "error" }); + await awaitAsync(); + + expect(error).toEqual({ some: "error" }); + }); + + it("completes when `user$` completes", async () => { + await stateProvider.setUserState(SettingsKey, null, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const userId$ = new BehaviorSubject(SomeUser); + let completed = false; + + generator.generate$(SomeConfiguration, { userId$ }).subscribe({ + complete: () => { + completed = true; + }, + }); + userId$.complete(); + await awaitAsync(); + + expect(completed).toBeTruthy(); + }); + + it("emits a generation only when `on$` emits", async () => { + // This test breaks from arrange/act/assert because it is testing causality + await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const on$ = new Subject(); + const results: any[] = []; + + // confirm no emission during subscription + const sub = generator + .generate$(SomeConfiguration, { on$ }) + .subscribe((result) => results.push(result)); + await awaitAsync(); + expect(results.length).toEqual(0); + + // confirm forwarded emission + on$.next(); + await awaitAsync(); + expect(results).toEqual([new GeneratedCredential("value", SomeCategory, SomeTime)]); + + // confirm updating settings does not cause an emission + await stateProvider.setUserState(SettingsKey, { foo: "next" }, SomeUser); + await awaitAsync(); + expect(results.length).toBe(1); + + // confirm forwarded emission takes latest value + on$.next(); + await awaitAsync(); + sub.unsubscribe(); + + expect(results).toEqual([ + new GeneratedCredential("value", SomeCategory, SomeTime), + new GeneratedCredential("next", SomeCategory, SomeTime), + ]); + }); + + it("errors when `on$` errors", async () => { + await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const on$ = new Subject(); + let error: any = null; + + // confirm no emission during subscription + generator.generate$(SomeConfiguration, { on$ }).subscribe({ + error: (e: unknown) => { + error = e; + }, + }); + on$.error({ some: "error" }); + await awaitAsync(); + + expect(error).toEqual({ some: "error" }); + }); + + it("completes when `on$` completes", async () => { + await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const on$ = new Subject(); + let complete = false; + + // confirm no emission during subscription + generator.generate$(SomeConfiguration, { on$ }).subscribe({ + complete: () => { + complete = true; + }, + }); + on$.complete(); + await awaitAsync(); + + expect(complete).toBeTruthy(); + }); + }); + describe("settings$", () => { it("defaults to the configuration's initial settings if settings aren't found", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const result = await firstValueFrom(generator.settings$(SomeConfiguration)); @@ -107,7 +362,7 @@ describe("CredentialGeneratorService", () => { it("reads from the active user's configuration-defined storage", async () => { const settings = { foo: "value" }; await stateProvider.setUserState(SettingsKey, settings, SomeUser); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const result = await firstValueFrom(generator.settings$(SomeConfiguration)); @@ -119,7 +374,7 @@ describe("CredentialGeneratorService", () => { await stateProvider.setUserState(SettingsKey, settings, SomeUser); const policy$ = new BehaviorSubject([somePolicy]); policyService.getAll$.mockReturnValue(policy$); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const result = await firstValueFrom(generator.settings$(SomeConfiguration)); @@ -131,7 +386,7 @@ describe("CredentialGeneratorService", () => { const anotherSettings = { foo: "another" }; await stateProvider.setUserState(SettingsKey, someSettings, SomeUser); await stateProvider.setUserState(SettingsKey, anotherSettings, AnotherUser); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const results: any = []; const sub = generator.settings$(SomeConfiguration).subscribe((r) => results.push(r)); @@ -148,7 +403,7 @@ describe("CredentialGeneratorService", () => { await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); const anotherSettings = { foo: "another" }; await stateProvider.setUserState(SettingsKey, anotherSettings, AnotherUser); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId$ = new BehaviorSubject(AnotherUser).asObservable(); const result = await firstValueFrom(generator.settings$(SomeConfiguration, { userId$ })); @@ -161,7 +416,7 @@ describe("CredentialGeneratorService", () => { await stateProvider.setUserState(SettingsKey, someSettings, SomeUser); const anotherSettings = { foo: "another" }; await stateProvider.setUserState(SettingsKey, anotherSettings, AnotherUser); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); const results: any = []; @@ -180,7 +435,7 @@ describe("CredentialGeneratorService", () => { it("errors when the arbitrary user's stream errors", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); let error = null; @@ -198,7 +453,7 @@ describe("CredentialGeneratorService", () => { it("completes when the arbitrary user's stream completes", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); let completed = false; @@ -216,7 +471,7 @@ describe("CredentialGeneratorService", () => { it("ignores repeated arbitrary user emissions", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); let count = 0; @@ -240,7 +495,7 @@ describe("CredentialGeneratorService", () => { describe("settings", () => { it("writes to the user's state", async () => { const singleUserId$ = new BehaviorSubject(SomeUser).asObservable(); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const subject = await generator.settings(SomeConfiguration, { singleUserId$ }); subject.next({ foo: "next value" }); @@ -253,7 +508,7 @@ describe("CredentialGeneratorService", () => { it("waits for the user to become available", async () => { const singleUserId = new BehaviorSubject(null); const singleUserId$ = singleUserId.asObservable(); - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); let completed = false; const promise = generator.settings(SomeConfiguration, { singleUserId$ }).then((settings) => { @@ -271,7 +526,7 @@ describe("CredentialGeneratorService", () => { describe("policy$", () => { it("creates a disabled policy evaluator when there is no policy", async () => { - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId$ = new BehaviorSubject(SomeUser).asObservable(); const result = await firstValueFrom(generator.policy$(SomeConfiguration, { userId$ })); @@ -281,7 +536,7 @@ describe("CredentialGeneratorService", () => { }); it("creates an active policy evaluator when there is a policy", async () => { - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId$ = new BehaviorSubject(SomeUser).asObservable(); const policy$ = new BehaviorSubject([somePolicy]); policyService.getAll$.mockReturnValue(policy$); @@ -293,7 +548,7 @@ describe("CredentialGeneratorService", () => { }); it("follows policy emissions", async () => { - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); const somePolicySubject = new BehaviorSubject([somePolicy]); @@ -316,7 +571,7 @@ describe("CredentialGeneratorService", () => { }); it("follows user emissions", async () => { - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); const somePolicy$ = new BehaviorSubject([somePolicy]).asObservable(); @@ -340,7 +595,7 @@ describe("CredentialGeneratorService", () => { }); it("errors when the user errors", async () => { - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); const expectedError = { some: "error" }; @@ -358,7 +613,7 @@ describe("CredentialGeneratorService", () => { }); it("completes when the user completes", async () => { - const generator = new CredentialGeneratorService(stateProvider, policyService); + const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); diff --git a/libs/tools/generator/core/src/services/credential-generator.service.ts b/libs/tools/generator/core/src/services/credential-generator.service.ts index 891d0016fef..d2012ecf20f 100644 --- a/libs/tools/generator/core/src/services/credential-generator.service.ts +++ b/libs/tools/generator/core/src/services/credential-generator.service.ts @@ -1,5 +1,7 @@ import { + BehaviorSubject, combineLatest, + concatMap, distinctUntilChanged, endWith, filter, @@ -8,32 +10,84 @@ import { map, mergeMap, Observable, + race, switchMap, takeUntil, + withLatestFrom, } from "rxjs"; +import { Simplify } from "type-fest"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { StateProvider } from "@bitwarden/common/platform/state"; -import { SingleUserDependency, UserDependency } from "@bitwarden/common/tools/dependencies"; +import { + OnDependency, + SingleUserDependency, + UserDependency, +} from "@bitwarden/common/tools/dependencies"; import { UserStateSubject } from "@bitwarden/common/tools/state/user-state-subject"; import { Constraints } from "@bitwarden/common/tools/types"; -import { PolicyEvaluator } from "../abstractions"; +import { PolicyEvaluator, Randomizer } from "../abstractions"; import { mapPolicyToEvaluatorV2 } from "../rx"; import { CredentialGeneratorConfiguration as Configuration } from "../types/credential-generator-configuration"; type Policy$Dependencies = UserDependency; type Settings$Dependencies = Partial; +type Generate$Dependencies = Simplify & Partial> & { + /** Emits the active website when subscribed. + * + * The generator does not respond to emissions of this interface; + * If it is provided, the generator blocks until a value becomes available. + * When `website$` is omitted, the generator uses the empty string instead. + * When `website$` completes, the generator completes. + * When `website$` errors, the generator forwards the error. + */ + website$?: Observable; +}; // FIXME: once the modernization is complete, switch the type parameters // in `PolicyEvaluator` and bake-in the constraints type. type Evaluator = PolicyEvaluator & Constraints; export class CredentialGeneratorService { constructor( + private randomizer: Randomizer, private stateProvider: StateProvider, private policyService: PolicyService, ) {} + /** Generates a stream of credentials + * @param configuration determines which generator's settings are loaded + * @param dependencies.on$ when specified, a new credential is emitted when + * this emits. Otherwise, a new credential is emitted when the settings + * update. + */ + generate$( + configuration: Readonly>, + dependencies?: Generate$Dependencies, + ) { + // instantiate the engine + const engine = configuration.engine.create(this.randomizer); + + // stream blocks until all of these values are received + const website$ = dependencies?.website$ ?? new BehaviorSubject(null); + const request$ = website$.pipe(map((website) => ({ website }))); + const settings$ = this.settings$(configuration, dependencies); + + // monitor completion + const requestComplete$ = request$.pipe(ignoreElements(), endWith(true)); + const settingsComplete$ = request$.pipe(ignoreElements(), endWith(true)); + const complete$ = race(requestComplete$, settingsComplete$); + + // generation proper + const generate$ = (dependencies?.on$ ?? settings$).pipe( + withLatestFrom(request$, settings$), + concatMap(([, request, settings]) => engine.generate(request, settings)), + takeUntil(complete$), + ); + + return generate$; + } + /** Get the settings for the provided configuration * @param configuration determines which generator's settings are loaded * @param dependencies.userId$ identifies the user to which the settings are bound. @@ -82,7 +136,7 @@ export class CredentialGeneratorService { * @remarks the subject enforces policy for the settings */ async settings( - configuration: Configuration, + configuration: Readonly>, dependencies: SingleUserDependency, ) { const userId = await firstValueFrom( diff --git a/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.spec.ts b/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.spec.ts index f9b346e02b0..6591b179fc2 100644 --- a/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.spec.ts +++ b/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.spec.ts @@ -17,7 +17,7 @@ import { PASSPHRASE_SETTINGS } from "./storage"; const SomeUser = "some user" as UserId; -describe("Password generation strategy", () => { +describe("Passphrase generation strategy", () => { describe("toEvaluator()", () => { it("should map to the policy evaluator", async () => { const strategy = new PassphraseGeneratorStrategy(null, null); diff --git a/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.ts b/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.ts index fe2731f9dd5..37d8b9e3fba 100644 --- a/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.ts +++ b/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.ts @@ -2,11 +2,11 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { StateProvider } from "@bitwarden/common/platform/state"; import { GeneratorStrategy } from "../abstractions"; -import { DefaultPassphraseBoundaries, DefaultPassphraseGenerationOptions, Policies } from "../data"; +import { DefaultPassphraseGenerationOptions, Policies } from "../data"; import { PasswordRandomizer } from "../engine"; import { mapPolicyToEvaluator } from "../rx"; import { PassphraseGenerationOptions, PassphraseGeneratorPolicy } from "../types"; -import { observe$PerUserId, sharedStateByUserId } from "../util"; +import { observe$PerUserId, optionsToEffWordListRequest, sharedStateByUserId } from "../util"; import { PASSPHRASE_SETTINGS } from "./storage"; @@ -33,13 +33,7 @@ export class PassphraseGeneratorStrategy // algorithm async generate(options: PassphraseGenerationOptions): Promise { - const requestWords = options.numWords ?? DefaultPassphraseGenerationOptions.numWords; - const request = { - numberOfWords: Math.max(requestWords, DefaultPassphraseBoundaries.numWords.min), - capitalize: options.capitalize ?? DefaultPassphraseGenerationOptions.capitalize, - number: options.includeNumber ?? DefaultPassphraseGenerationOptions.includeNumber, - separator: options.wordSeparator ?? DefaultPassphraseGenerationOptions.wordSeparator, - }; + const request = optionsToEffWordListRequest(options); return this.randomizer.randomEffLongWords(request); } diff --git a/libs/tools/generator/core/src/strategies/password-generator-strategy.ts b/libs/tools/generator/core/src/strategies/password-generator-strategy.ts index 9ed62490c06..9ff8a3d88b0 100644 --- a/libs/tools/generator/core/src/strategies/password-generator-strategy.ts +++ b/libs/tools/generator/core/src/strategies/password-generator-strategy.ts @@ -6,7 +6,7 @@ import { Policies, DefaultPasswordGenerationOptions } from "../data"; import { PasswordRandomizer } from "../engine"; import { mapPolicyToEvaluator } from "../rx"; import { PasswordGenerationOptions, PasswordGeneratorPolicy } from "../types"; -import { observe$PerUserId, sharedStateByUserId, sum } from "../util"; +import { observe$PerUserId, optionsToRandomAsciiRequest, sharedStateByUserId } from "../util"; import { PASSWORD_SETTINGS } from "./storage"; @@ -32,62 +32,7 @@ export class PasswordGeneratorStrategy // algorithm async generate(options: PasswordGenerationOptions): Promise { - // converts password generation option sets, which are defined by - // an "enabled" and "quantity" parameter, to the password engine's - // parameters, which represent disabled options as `undefined` - // properties. - function process( - // values read from the options - enabled: boolean, - quantity: number, - // value used if an option is missing - defaultEnabled: boolean, - defaultQuantity: number, - ) { - const isEnabled = enabled ?? defaultEnabled; - const actualQuantity = quantity ?? defaultQuantity; - const result = isEnabled ? actualQuantity : undefined; - - return result; - } - - const request = { - uppercase: process( - options.uppercase, - options.minUppercase, - DefaultPasswordGenerationOptions.uppercase, - DefaultPasswordGenerationOptions.minUppercase, - ), - lowercase: process( - options.lowercase, - options.minLowercase, - DefaultPasswordGenerationOptions.lowercase, - DefaultPasswordGenerationOptions.minLowercase, - ), - digits: process( - options.number, - options.minNumber, - DefaultPasswordGenerationOptions.number, - DefaultPasswordGenerationOptions.minNumber, - ), - special: process( - options.special, - options.minSpecial, - DefaultPasswordGenerationOptions.special, - DefaultPasswordGenerationOptions.minSpecial, - ), - ambiguous: options.ambiguous ?? DefaultPasswordGenerationOptions.ambiguous, - all: 0, - }; - - // engine represents character sets as "include only"; you assert how many all - // characters there can be rather than a total length. This conversion has - // the character classes win, so that the result is always consistent with policy - // minimums. - const required = sum(request.uppercase, request.lowercase, request.digits, request.special); - const remaining = (options.length ?? 0) - required; - request.all = Math.max(remaining, 0); - + const request = optionsToRandomAsciiRequest(options); const result = await this.randomizer.randomAscii(request); return result; diff --git a/libs/tools/generator/core/src/types/credential-category.ts b/libs/tools/generator/core/src/types/credential-category.ts new file mode 100644 index 00000000000..54c8c5ed8e9 --- /dev/null +++ b/libs/tools/generator/core/src/types/credential-category.ts @@ -0,0 +1,5 @@ +/** Kinds of credentials that can be stored by the history service + * password - a secret consisting of arbitrary characters used to authenticate a user + * passphrase - a secret consisting of words used to authenticate a user + */ +export type CredentialCategory = "password" | "passphrase"; diff --git a/libs/tools/generator/core/src/types/credential-generator-configuration.ts b/libs/tools/generator/core/src/types/credential-generator-configuration.ts index 2a8b07b0e82..80d977a73c8 100644 --- a/libs/tools/generator/core/src/types/credential-generator-configuration.ts +++ b/libs/tools/generator/core/src/types/credential-generator-configuration.ts @@ -1,9 +1,29 @@ import { UserKeyDefinition } from "@bitwarden/common/platform/state"; import { Constraints } from "@bitwarden/common/tools/types"; +import { Randomizer } from "../abstractions"; import { PolicyConfiguration } from "../types"; +import { CredentialCategory } from "./credential-category"; +import { CredentialGenerator } from "./credential-generator"; + export type CredentialGeneratorConfiguration = { + /** Category describing usage of the credential generated by this configuration + */ + category: CredentialCategory; + + /** An algorithm that generates credentials when ran. */ + engine: { + /** Factory for the generator + */ + // FIXME: note that this erases the engine's type so that credentials are + // generated uniformly. This property needs to be maintained for + // the credential generator, but engine configurations should return + // the underlying type. `create` may be able to do double-duty w/ an + // engine definition if `CredentialGenerator` can be made covariant. + create: (randomizer: Randomizer) => CredentialGenerator; + }; + /** Defines the stored parameters for credential generation */ settings: { /** value used when an account's settings haven't been initialized */ initial: Readonly>; diff --git a/libs/tools/generator/core/src/types/credential-generator.ts b/libs/tools/generator/core/src/types/credential-generator.ts new file mode 100644 index 00000000000..c95ff25afff --- /dev/null +++ b/libs/tools/generator/core/src/types/credential-generator.ts @@ -0,0 +1,12 @@ +import { GenerationRequest } from "@bitwarden/common/tools/types"; + +import { GeneratedCredential } from "./generated-credential"; + +/** An algorithm that generates credentials. */ +export type CredentialGenerator = { + /** Generates a credential + * @param request runtime parameters + * @param settings stored parameters + */ + generate: (request: GenerationRequest, settings: Settings) => Promise; +}; diff --git a/libs/tools/generator/core/src/types/generated-credential.spec.ts b/libs/tools/generator/core/src/types/generated-credential.spec.ts new file mode 100644 index 00000000000..a687676576b --- /dev/null +++ b/libs/tools/generator/core/src/types/generated-credential.spec.ts @@ -0,0 +1,58 @@ +import { CredentialCategory, GeneratedCredential } from "."; + +describe("GeneratedCredential", () => { + describe("constructor", () => { + it("assigns credential", () => { + const result = new GeneratedCredential("example", "passphrase", new Date(100)); + + expect(result.credential).toEqual("example"); + }); + + it("assigns category", () => { + const result = new GeneratedCredential("example", "passphrase", new Date(100)); + + expect(result.category).toEqual("passphrase"); + }); + + it("passes through date parameters", () => { + const result = new GeneratedCredential("example", "password", new Date(100)); + + expect(result.generationDate).toEqual(new Date(100)); + }); + + it("converts numeric dates to Dates", () => { + const result = new GeneratedCredential("example", "password", 100); + + expect(result.generationDate).toEqual(new Date(100)); + }); + }); + + it("toJSON converts from a credential into a JSON object", () => { + const credential = new GeneratedCredential("example", "password", new Date(100)); + + const result = credential.toJSON(); + + expect(result).toEqual({ + credential: "example", + category: "password" as CredentialCategory, + generationDate: 100, + }); + }); + + it("fromJSON converts Json objects into credentials", () => { + const jsonValue = { + credential: "example", + category: "password" as CredentialCategory, + generationDate: 100, + }; + + const result = GeneratedCredential.fromJSON(jsonValue); + + expect(result).toBeInstanceOf(GeneratedCredential); + expect(result).toEqual({ + credential: "example", + category: "password", + generationDate: new Date(100), + }); + }); +}); diff --git a/libs/tools/generator/core/src/types/generated-credential.ts b/libs/tools/generator/core/src/types/generated-credential.ts new file mode 100644 index 00000000000..ff174b04a58 --- /dev/null +++ b/libs/tools/generator/core/src/types/generated-credential.ts @@ -0,0 +1,47 @@ +import { Jsonify } from "type-fest"; + +import { CredentialCategory } from "./credential-category"; + +/** A credential generation result */ +export class GeneratedCredential { + /** + * Instantiates a generated credential + * @param credential The value of the generated credential (e.g. a password) + * @param category The kind of credential + * @param generationDate The date that the credential was generated. + * Numeric values should are interpreted using {@link Date.valueOf} + * semantics. + */ + constructor( + readonly credential: string, + readonly category: CredentialCategory, + generationDate: Date | number, + ) { + if (typeof generationDate === "number") { + this.generationDate = new Date(generationDate); + } else { + this.generationDate = generationDate; + } + } + + /** The date that the credential was generated */ + generationDate: Date; + + /** Constructs a credential from its `toJSON` representation */ + static fromJSON(jsonValue: Jsonify) { + return new GeneratedCredential( + jsonValue.credential, + jsonValue.category, + jsonValue.generationDate, + ); + } + + /** Serializes a credential to a JSON-compatible object */ + toJSON() { + return { + credential: this.credential, + category: this.category, + generationDate: this.generationDate.valueOf(), + }; + } +} diff --git a/libs/tools/generator/core/src/types/index.ts b/libs/tools/generator/core/src/types/index.ts index 786b15b9d11..229ac1c0c38 100644 --- a/libs/tools/generator/core/src/types/index.ts +++ b/libs/tools/generator/core/src/types/index.ts @@ -1,8 +1,11 @@ export * from "./boundary"; export * from "./catchall-generator-options"; +export * from "./credential-category"; +export * from "./credential-generator"; export * from "./credential-generator-configuration"; export * from "./eff-username-generator-options"; export * from "./forwarder-options"; +export * from "./generated-credential"; export * from "./generator-options"; export * from "./generator-type"; export * from "./no-policy"; diff --git a/libs/tools/generator/core/src/util.spec.ts b/libs/tools/generator/core/src/util.spec.ts index 32bdc3ad3af..7ffd869535b 100644 --- a/libs/tools/generator/core/src/util.spec.ts +++ b/libs/tools/generator/core/src/util.spec.ts @@ -1,4 +1,5 @@ -import { sum } from "./util"; +import { DefaultPassphraseGenerationOptions } from "./data"; +import { optionsToEffWordListRequest, optionsToRandomAsciiRequest, sum } from "./util"; describe("sum", () => { it("returns 0 when the list is empty", () => { @@ -15,3 +16,411 @@ describe("sum", () => { expect(sum(1, 2, 3)).toBe(6); }); }); + +describe("optionsToRandomAsciiRequest", () => { + it("should map options", async () => { + const result = optionsToRandomAsciiRequest({ + length: 20, + ambiguous: true, + uppercase: true, + lowercase: true, + number: true, + special: true, + minUppercase: 1, + minLowercase: 2, + minNumber: 3, + minSpecial: 4, + }); + + expect(result).toEqual({ + all: 10, + uppercase: 1, + lowercase: 2, + digits: 3, + special: 4, + ambiguous: true, + }); + }); + + it("should disable uppercase", async () => { + const result = optionsToRandomAsciiRequest({ + length: 3, + ambiguous: true, + uppercase: false, + lowercase: true, + number: true, + special: true, + minUppercase: 1, + minLowercase: 1, + minNumber: 1, + minSpecial: 1, + }); + + expect(result).toEqual({ + all: 0, + uppercase: undefined, + lowercase: 1, + digits: 1, + special: 1, + ambiguous: true, + }); + }); + + it("should disable lowercase", async () => { + const result = optionsToRandomAsciiRequest({ + length: 3, + ambiguous: true, + uppercase: true, + lowercase: false, + number: true, + special: true, + minUppercase: 1, + minLowercase: 1, + minNumber: 1, + minSpecial: 1, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 1, + lowercase: undefined, + digits: 1, + special: 1, + ambiguous: true, + }); + }); + + it("should disable digits", async () => { + const result = optionsToRandomAsciiRequest({ + length: 3, + ambiguous: true, + uppercase: true, + lowercase: true, + number: false, + special: true, + minUppercase: 1, + minLowercase: 1, + minNumber: 1, + minSpecial: 1, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 1, + lowercase: 1, + digits: undefined, + special: 1, + ambiguous: true, + }); + }); + + it("should disable special", async () => { + const result = optionsToRandomAsciiRequest({ + length: 3, + ambiguous: true, + uppercase: true, + lowercase: true, + number: true, + special: false, + minUppercase: 1, + minLowercase: 1, + minNumber: 1, + minSpecial: 1, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 1, + lowercase: 1, + digits: 1, + special: undefined, + ambiguous: true, + }); + }); + + it("should override length with minimums", async () => { + const result = optionsToRandomAsciiRequest({ + length: 20, + ambiguous: true, + uppercase: true, + lowercase: true, + number: true, + special: true, + minUppercase: 1, + minLowercase: 2, + minNumber: 3, + minSpecial: 4, + }); + + expect(result).toEqual({ + all: 10, + uppercase: 1, + lowercase: 2, + digits: 3, + special: 4, + ambiguous: true, + }); + }); + + it("should default uppercase", async () => { + const result = optionsToRandomAsciiRequest({ + length: 2, + ambiguous: true, + lowercase: true, + number: true, + special: true, + minUppercase: 2, + minLowercase: 0, + minNumber: 0, + minSpecial: 0, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 2, + lowercase: 0, + digits: 0, + special: 0, + ambiguous: true, + }); + }); + + it("should default lowercase", async () => { + const result = optionsToRandomAsciiRequest({ + length: 0, + ambiguous: true, + uppercase: true, + number: true, + special: true, + minUppercase: 0, + minLowercase: 2, + minNumber: 0, + minSpecial: 0, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 0, + lowercase: 2, + digits: 0, + special: 0, + ambiguous: true, + }); + }); + + it("should default number", async () => { + const result = optionsToRandomAsciiRequest({ + length: 0, + ambiguous: true, + uppercase: true, + lowercase: true, + special: true, + minUppercase: 0, + minLowercase: 0, + minNumber: 2, + minSpecial: 0, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 0, + lowercase: 0, + digits: 2, + special: 0, + ambiguous: true, + }); + }); + + it("should default special", async () => { + const result = optionsToRandomAsciiRequest({ + length: 0, + ambiguous: true, + uppercase: true, + lowercase: true, + number: true, + minUppercase: 0, + minLowercase: 0, + minNumber: 0, + minSpecial: 0, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 0, + lowercase: 0, + digits: 0, + special: undefined, + ambiguous: true, + }); + }); + + it("should default minUppercase", async () => { + const result = optionsToRandomAsciiRequest({ + length: 0, + ambiguous: true, + uppercase: true, + lowercase: true, + number: true, + special: true, + minLowercase: 0, + minNumber: 0, + minSpecial: 0, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 1, + lowercase: 0, + digits: 0, + special: 0, + ambiguous: true, + }); + }); + + it("should default minLowercase", async () => { + const result = optionsToRandomAsciiRequest({ + length: 0, + ambiguous: true, + uppercase: true, + lowercase: true, + number: true, + special: true, + minUppercase: 0, + minNumber: 0, + minSpecial: 0, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 0, + lowercase: 1, + digits: 0, + special: 0, + ambiguous: true, + }); + }); + + it("should default minNumber", async () => { + const result = optionsToRandomAsciiRequest({ + length: 0, + ambiguous: true, + uppercase: true, + lowercase: true, + number: true, + special: true, + minUppercase: 0, + minLowercase: 0, + minSpecial: 0, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 0, + lowercase: 0, + digits: 1, + special: 0, + ambiguous: true, + }); + }); + + it("should default minSpecial", async () => { + const result = optionsToRandomAsciiRequest({ + length: 0, + ambiguous: true, + uppercase: true, + lowercase: true, + number: true, + special: true, + minUppercase: 0, + minLowercase: 0, + minNumber: 0, + }); + + expect(result).toEqual({ + all: 0, + uppercase: 0, + lowercase: 0, + digits: 0, + special: 0, + ambiguous: true, + }); + }); +}); + +describe("optionsToEffWordListRequest", () => { + it("should map options", async () => { + const result = optionsToEffWordListRequest({ + numWords: 4, + capitalize: true, + includeNumber: true, + wordSeparator: "!", + }); + + expect(result).toEqual({ + numberOfWords: 4, + capitalize: true, + number: true, + separator: "!", + }); + }); + + it("should default numWords", async () => { + const result = optionsToEffWordListRequest({ + capitalize: true, + includeNumber: true, + wordSeparator: "!", + }); + + expect(result).toEqual({ + numberOfWords: DefaultPassphraseGenerationOptions.numWords, + capitalize: true, + number: true, + separator: "!", + }); + }); + + it("should default capitalize", async () => { + const result = optionsToEffWordListRequest({ + numWords: 4, + includeNumber: true, + wordSeparator: "!", + }); + + expect(result).toEqual({ + numberOfWords: 4, + capitalize: DefaultPassphraseGenerationOptions.capitalize, + number: true, + separator: "!", + }); + }); + + it("should default includeNumber", async () => { + const result = optionsToEffWordListRequest({ + numWords: 4, + capitalize: true, + wordSeparator: "!", + }); + + expect(result).toEqual({ + numberOfWords: 4, + capitalize: true, + number: DefaultPassphraseGenerationOptions.includeNumber, + separator: "!", + }); + }); + + it("should default wordSeparator", async () => { + const result = optionsToEffWordListRequest({ + numWords: 4, + capitalize: true, + includeNumber: true, + }); + + expect(result).toEqual({ + numberOfWords: 4, + capitalize: true, + number: true, + separator: DefaultPassphraseGenerationOptions.wordSeparator, + }); + }); +}); diff --git a/libs/tools/generator/core/src/util.ts b/libs/tools/generator/core/src/util.ts index cca2c75834c..21e901765e2 100644 --- a/libs/tools/generator/core/src/util.ts +++ b/libs/tools/generator/core/src/util.ts @@ -7,6 +7,13 @@ import { } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; +import { + DefaultPassphraseBoundaries, + DefaultPassphraseGenerationOptions, + DefaultPasswordGenerationOptions, +} from "./data"; +import { PassphraseGenerationOptions, PasswordGenerationOptions } from "./types"; + /** construct a method that outputs a copy of `defaultValue` as an observable. */ export function observe$PerUserId( create: () => Partial, @@ -50,3 +57,79 @@ export function sharedStateByUserId(key: UserKeyDefinition, provid /** returns the sum of items in the list. */ export const sum = (...items: number[]) => (items ?? []).reduce((sum: number, current: number) => sum + (current ?? 0), 0); + +/* converts password generation option sets, which are defined by + * an "enabled" and "quantity" parameter, to the password engine's + * parameters, which represent disabled options as `undefined` + * properties. + */ +export function optionsToRandomAsciiRequest(options: PasswordGenerationOptions) { + // helper for processing common option sets + function process( + // values read from the options + enabled: boolean, + quantity: number, + // value used if an option is missing + defaultEnabled: boolean, + defaultQuantity: number, + ) { + const isEnabled = enabled ?? defaultEnabled; + const actualQuantity = quantity ?? defaultQuantity; + const result = isEnabled ? actualQuantity : undefined; + + return result; + } + + const request = { + uppercase: process( + options.uppercase, + options.minUppercase, + DefaultPasswordGenerationOptions.uppercase, + DefaultPasswordGenerationOptions.minUppercase, + ), + lowercase: process( + options.lowercase, + options.minLowercase, + DefaultPasswordGenerationOptions.lowercase, + DefaultPasswordGenerationOptions.minLowercase, + ), + digits: process( + options.number, + options.minNumber, + DefaultPasswordGenerationOptions.number, + DefaultPasswordGenerationOptions.minNumber, + ), + special: process( + options.special, + options.minSpecial, + DefaultPasswordGenerationOptions.special, + DefaultPasswordGenerationOptions.minSpecial, + ), + ambiguous: options.ambiguous ?? DefaultPasswordGenerationOptions.ambiguous, + all: 0, + }; + + // engine represents character sets as "include only"; you assert how many all + // characters there can be rather than a total length. This conversion has + // the character classes win, so that the result is always consistent with policy + // minimums. + const required = sum(request.uppercase, request.lowercase, request.digits, request.special); + const remaining = (options.length ?? 0) - required; + request.all = Math.max(remaining, 0); + + return request; +} + +/* converts passphrase generation option sets to the eff word list request + */ +export function optionsToEffWordListRequest(options: PassphraseGenerationOptions) { + const requestWords = options.numWords ?? DefaultPassphraseGenerationOptions.numWords; + const request = { + numberOfWords: Math.max(requestWords, DefaultPassphraseBoundaries.numWords.min), + capitalize: options.capitalize ?? DefaultPassphraseGenerationOptions.capitalize, + number: options.includeNumber ?? DefaultPassphraseGenerationOptions.includeNumber, + separator: options.wordSeparator ?? DefaultPassphraseGenerationOptions.wordSeparator, + }; + + return request; +} diff --git a/libs/tools/send/send-ui/src/icons/no-send.icon.ts b/libs/tools/send/send-ui/src/icons/no-send.icon.ts index e1442ad7029..555d8024601 100644 --- a/libs/tools/send/send-ui/src/icons/no-send.icon.ts +++ b/libs/tools/send/send-ui/src/icons/no-send.icon.ts @@ -2,12 +2,12 @@ import { svgIcon } from "@bitwarden/components"; export const NoSendsIcon = svgIcon` - - - - - - - + + + + + + + `; diff --git a/libs/tools/send/send-ui/src/icons/send-created.icon.ts b/libs/tools/send/send-ui/src/icons/send-created.icon.ts index bb4bc2dd3b9..099baebb9ad 100644 --- a/libs/tools/send/send-ui/src/icons/send-created.icon.ts +++ b/libs/tools/send/send-ui/src/icons/send-created.icon.ts @@ -2,12 +2,12 @@ import { svgIcon } from "@bitwarden/components"; export const SendCreatedIcon = svgIcon` - - - - - - + + + + + + diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts index c78bd308731..96d4289028d 100644 --- a/libs/vault/src/cipher-form/cipher-form.stories.ts +++ b/libs/vault/src/cipher-form/cipher-form.stories.ts @@ -10,6 +10,7 @@ import { import { BehaviorSubject } from "rxjs"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; @@ -168,6 +169,12 @@ export default { autofillOnPageLoadDefault$: new BehaviorSubject(true), }, }, + { + provide: EventCollectionService, + useValue: { + collect: () => Promise.resolve(), + }, + }, ], }), componentWrapperDecorator( diff --git a/libs/vault/src/components/assign-collections.component.ts b/libs/vault/src/components/assign-collections.component.ts index 188fc543eff..db4d61691c9 100644 --- a/libs/vault/src/components/assign-collections.component.ts +++ b/libs/vault/src/components/assign-collections.component.ts @@ -11,12 +11,12 @@ import { } from "@angular/core"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; import { - Observable, - Subject, combineLatest, firstValueFrom, map, + Observable, shareReplay, + Subject, switchMap, takeUntil, tap, @@ -27,8 +27,6 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -170,7 +168,6 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI constructor( private cipherService: CipherService, private i18nService: I18nService, - private configService: ConfigService, private organizationService: OrganizationService, private collectionService: CollectionService, private formBuilder: FormBuilder, @@ -179,10 +176,6 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI ) {} async ngOnInit() { - const restrictProviderAccess = await this.configService.getFeatureFlag( - FeatureFlag.RestrictProviderAccess, - ); - this.activeUserId = await firstValueFrom( this.accountService.activeAccount$.pipe(map((a) => a?.id)), ); @@ -193,7 +186,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI this.showOrgSelector = true; } - await this.initializeItems(this.selectedOrgId, restrictProviderAccess); + await this.initializeItems(this.selectedOrgId); if (this.selectedOrgId && this.selectedOrgId !== MY_VAULT_ID) { await this.handleOrganizationCiphers(); @@ -339,7 +332,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI } } - private async initializeItems(organizationId: OrganizationId, restrictProviderAccess: boolean) { + private async initializeItems(organizationId: OrganizationId) { this.totalItemCount = this.params.ciphers.length; // If organizationId is not present or organizationId is MyVault, then all ciphers are considered personal items @@ -354,7 +347,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI const org = await this.organizationService.get(organizationId); this.orgName = org.name; - this.editableItems = org.canEditAllCiphers(restrictProviderAccess) + this.editableItems = org.canEditAllCiphers ? this.params.ciphers : this.params.ciphers.filter((c) => c.edit); diff --git a/libs/vault/src/icons/deactivated-org.ts b/libs/vault/src/icons/deactivated-org.ts index 7d1871a091d..5f7c910aec8 100644 --- a/libs/vault/src/icons/deactivated-org.ts +++ b/libs/vault/src/icons/deactivated-org.ts @@ -3,22 +3,22 @@ import { svgIcon } from "@bitwarden/components"; export const DeactivatedOrg = svgIcon` - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/libs/vault/src/icons/empty-trash.ts b/libs/vault/src/icons/empty-trash.ts index e112309e323..df74602e6d1 100644 --- a/libs/vault/src/icons/empty-trash.ts +++ b/libs/vault/src/icons/empty-trash.ts @@ -3,16 +3,16 @@ import { svgIcon } from "@bitwarden/components"; export const EmptyTrash = svgIcon` - - - - - - - - - + + + + + + + + + - + `; diff --git a/libs/vault/src/icons/no-folders.ts b/libs/vault/src/icons/no-folders.ts index 478cb6a6b79..666bfc86c4c 100644 --- a/libs/vault/src/icons/no-folders.ts +++ b/libs/vault/src/icons/no-folders.ts @@ -2,18 +2,18 @@ import { svgIcon } from "@bitwarden/components"; export const NoFolders = svgIcon` - - - - - - - - - - - - - + + + + + + + + + + + + + `; diff --git a/libs/vault/src/icons/vault.ts b/libs/vault/src/icons/vault.ts index 4afe0920f7b..577080b0f37 100644 --- a/libs/vault/src/icons/vault.ts +++ b/libs/vault/src/icons/vault.ts @@ -3,15 +3,15 @@ import { svgIcon } from "@bitwarden/components"; export const Vault = svgIcon` - - - - - - - - - + + + + + + + + + `; diff --git a/package-lock.json b/package-lock.json index 055962ac492..7cc733665a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "commander": "11.1.0", "core-js": "3.36.1", "form-data": "4.0.0", - "https-proxy-agent": "7.0.2", + "https-proxy-agent": "7.0.5", "inquirer": "8.2.6", "jquery": "3.7.1", "jsdom": "24.1.3", @@ -129,7 +129,7 @@ "copy-webpack-plugin": "12.0.2", "cross-env": "7.0.3", "css-loader": "7.1.2", - "electron": "32.0.1", + "electron": "32.0.2", "electron-builder": "24.13.3", "electron-log": "5.0.1", "electron-reload": "2.0.0-alpha.1", @@ -157,7 +157,7 @@ "jest-mock-extended": "3.0.7", "jest-preset-angular": "14.1.1", "lint-staged": "15.2.8", - "mini-css-extract-plugin": "2.8.1", + "mini-css-extract-plugin": "2.9.1", "node-ipc": "9.2.1", "postcss": "8.4.38", "postcss-loader": "8.1.1", @@ -192,7 +192,7 @@ }, "apps/browser": { "name": "@bitwarden/browser", - "version": "2024.9.0" + "version": "2024.9.1" }, "apps/cli": { "name": "@bitwarden/cli", @@ -207,7 +207,7 @@ "chalk": "4.1.2", "commander": "11.1.0", "form-data": "4.0.0", - "https-proxy-agent": "7.0.2", + "https-proxy-agent": "7.0.5", "inquirer": "8.2.6", "jsdom": "24.1.3", "jszip": "3.10.1", @@ -16433,9 +16433,9 @@ } }, "node_modules/electron": { - "version": "32.0.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-32.0.1.tgz", - "integrity": "sha512-5Hd5Jaf9niYVR2hZxoRd3gOrcxPOxQV1XPV5WaoSfT9jLJHFadhlKtuSDIk3U6rQZke+aC7GqPPAv55nWFCMsA==", + "version": "32.0.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-32.0.2.tgz", + "integrity": "sha512-nmZblq8wW3HZ17MAyaUuiMI9Mb0Cgc7UR3To85h/rVopbfyF5s34NxtK4gvyRfYPxpDGP4k+HoQIPniPPrdE3w==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -21637,9 +21637,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "license": "MIT", "dependencies": { "agent-base": "^7.0.2", @@ -24733,19 +24733,6 @@ "node": ">= 14" } }, - "node_modules/jsdom/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -27721,9 +27708,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", - "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz", + "integrity": "sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 1ed2e6cab99..365508e31ef 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "copy-webpack-plugin": "12.0.2", "cross-env": "7.0.3", "css-loader": "7.1.2", - "electron": "32.0.1", + "electron": "32.0.2", "electron-builder": "24.13.3", "electron-log": "5.0.1", "electron-reload": "2.0.0-alpha.1", @@ -119,7 +119,7 @@ "jest-mock-extended": "3.0.7", "jest-preset-angular": "14.1.1", "lint-staged": "15.2.8", - "mini-css-extract-plugin": "2.8.1", + "mini-css-extract-plugin": "2.9.1", "node-ipc": "9.2.1", "postcss": "8.4.38", "postcss-loader": "8.1.1", @@ -173,7 +173,7 @@ "commander": "11.1.0", "core-js": "3.36.1", "form-data": "4.0.0", - "https-proxy-agent": "7.0.2", + "https-proxy-agent": "7.0.5", "inquirer": "8.2.6", "jquery": "3.7.1", "jsdom": "24.1.3",