diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9eace676f4..9665d40b07 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 5b4cd52ac8..8d4067c116 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 b76a1e8412..4d008b684c 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 0090391fdb..e1fcf72c81 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 341406772e..a6b2a35e13 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 1a00659bbf..cf14c8e32a 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 bfc4fc04a1..9d90293b0c 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 fd084ed359..53a3ceb1a6 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 b80293fd04..13cd7e7f54 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 2b7bc2198d..160b625c4e 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 c5d80fc8b3..bb3c81e078 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 128894e754..e43e81e1e5 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 301e987dfd..8771b323cb 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 8abbdae03f..3af4de85e1 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 5b1ee6011b..cc008a4949 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 084445d8ce..c3347b8909 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 153c3987da..a8efa278e5 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 9c0200ab42..c90115ee53 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 fc98d5e277..572372bafc 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 43d894be6c..947b9b0966 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 175c5d305d..1cbb17bcf6 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 45a01a0946..224fa27300 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 07281c5df4..927225fce5 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 cc1fb49eb9..6a8288365e 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 66cd4bf6f8..f80a5959ef 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 5091559607..247542ec19 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 89f6719289..02a6842efa 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 b9bfa5b595..6b4d3922ff 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 0c6ca6bef7..88a1f158b3 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 17c6b7e5e4..ba551e0fa3 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 44dff3c818..b43eddb3fd 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 3bccac4121..79c4d3cff7 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 40d8cef50c..393a831177 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 f277d76bfa..5e3809e00d 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 81f6bacb96..338d70eaf5 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 e8d36e6fff..aa902ec5d7 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 74c9103943..cd2fff44a4 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 ebcf61ffd6..c60b8b7b21 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 0a847e86f6..2f1f123020 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 8a27d914ea..bcec218774 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 890fc14fdd..0ecb990750 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 81f6bacb96..338d70eaf5 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 a5d503d398..37486833e9 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 81f6bacb96..338d70eaf5 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 791fd7b237..435df8ecb4 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 81f6bacb96..338d70eaf5 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 81f6bacb96..338d70eaf5 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 7576f8a0ca..a99655378e 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 6abab6b104..a88aae1181 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 c516d2d1ed..75026c9d3e 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 62dda4fe0f..989f5e1b2e 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 fec64ad9ce..31d419aea6 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 9b906a57ea..3b47e77a5b 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 0f9088dc5e..c3135bb9fe 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 b9f72e0dae..a9665d631a 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 257478519b..c9d3783015 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 6c8715a958..8f8d34f8ba 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 81f6bacb96..338d70eaf5 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 645cfc5e37..0edcb1c9f8 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 9539f49b9f..02f67a672b 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 47f5bb486b..eb7affa78c 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 559000d519..1b059e1b9c 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 de2bfdf63f..04db7f8f6e 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 8f6673017c..d61e831d73 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 f916555fef..2d7f46fa59 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 12d501ba9e..5e132774e6 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 f76c94a076..b8bdb19aeb 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 84a9ee264c..477152fff8 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 f8d3c69105..f14dafacb7 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 0b43b0e257..d8c49da5b1 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 f07affd237..16938fbe79 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 affe9ffc04..9322ab5113 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 d5ca41c42c..cd11bc72f3 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 68eaae618f..6c894f4fa8 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 6013688df2..82f24974e2 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 9d763886fb..e0a6f6c53d 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 71a4ff119c..f927a7ca61 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 7dad7effee..9b9e6f0cd0 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 02071f5aa8..766646003b 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 a359c281eb..dc9f6cce68 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 25c8c547b2..341324c4a2 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 03269c70a5..ab81a602d2 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 89bc7438a7..82d5104a53 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 743048dc3b..ad034702a5 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 53ffb04554..e8f9b12c2d 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 524d9dff20..6a04ff6071 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 d9930d2271..5e64067a82 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 63160abea6..ed7f6b1e3e 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 f9652fe08c..add1ecbe3e 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 0000000000..13b05fb17e --- /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 0000000000..82bd273fad --- /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 f5f39bd022..da3e29eed8 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 b874765622..7c15937011 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 b19ac2c391..94dc8052f5 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 340b7fb1f4..21f6754deb 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 dda600ce09..9c0315406f 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 c7112cc6c1..cdb4a353f6 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 829615cc55..cfafb9990e 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 2679458812..5aee2022f7 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 6f7367b803..84042618a2 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 5e1de3971d..fe2ce4032e 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 fae10f4eef..4cb8481441 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 9e83c16860..1573a381af 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 cfe7d180e5..2fb7adf1ca 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 edf09be183..4827b7f0dc 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 aab1efb7c5..86b3be94be 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 7ccd08f86b..434f01df5a 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 b7615fe447..6ada709f4d 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 a9458b4717..2512c1e31b 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 9a54611285..f2c83d2b85 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 091d5c62f9..7b42de91fc 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 c977cc38f8..a6a31f4589 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 6952ef43b2..f47ee7f89a 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 303f12643f..d174e6694a 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 b1ac195190..9e422b429e 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 f7d3327707..25bed9fd57 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 c0f0f764a9..28b3cbf0a1 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 6590213579..3eba112525 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 60c214ad18..186999d1fe 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 f216aaa91a..4e66cbc710 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 30e1638a86..165d513feb 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 b2849a2033..e85779431a 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 8897329333..a7f51d3136 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 67364dde68..29346aa946 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 21e8cf6299..d0be45fdc2 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 c9c9ab49f7..07d77b4a4a 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 934929b1ee..459d16b761 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 3fa2249bcd..0d97c52f6e 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 12d14414b5..5e0efafced 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 21e8cf6299..d0be45fdc2 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 21e8cf6299..d0be45fdc2 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 a33e12c544..1f1be7b04e 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 f9ba74ae95..f758c6d360 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 0f208d53fc..e09fdb78dd 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 ff329b1a78..3c89cddd65 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 21e8cf6299..d0be45fdc2 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 205104c177..4461717618 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 2aecf452ea..341c2c9db0 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 5080534922..9cc1d8dd5f 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 7384da233f..603e7ef5ff 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 b3e2c6efa7..83e8dfdb53 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 f458513267..e7fc833389 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 1d90ff6126..5e87aadf1e 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 6ff12ad459..894dcffa26 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 f0bea1eb57..79b6b47bff 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 5df939b84e..4163b728bb 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 5aab64e9cb..db39395454 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 21e8cf6299..d0be45fdc2 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 9055bc1c9f..71e56b4080 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 640ddc5a5d..8978be127d 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 c8ae5f4b56..9de932e9c0 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 90eca2660e..970e61caa5 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 34804b736e..a32fabb44b 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 680d375982..e17547e6f1 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 9404bc9403..f5800ff7e5 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 ce3979f791..cec4bf044b 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 457eff37fa..2238fa9fc8 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 4f16628618..d6801aa155 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 9d45d7d656..f7399d9c27 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 96589fd2b0..45475440d0 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 e377427eb8..4ae68c9ca9 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 6603fa970d..9e6c27f601 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 3bdbbe4a18..166b2d1f2e 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 981b520010..198733d0dc 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 0000000000..fef00a09a9 --- /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 3ba0b8ef71..d153cf5355 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 0000000000..2a811a1b3b --- /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 0000000000..1ae1fd337b --- /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 85faf87144..63b01be995 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 3c7e31b5de..ef40d95dce 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 63e23182f6..b585aa78ed 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 218051b9e4..0cea2aee53 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 490c799ad1..8c28bcb493 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 36d5d219b2..df408e76f8 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 22275fb228..6a36bf8213 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 25e7b92edf..307da55a5e 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 221c2f469a..26390a66fc 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 fb85b93678..96b4d03364 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 d9efef44f4..c40e4687b7 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 0488291b44..8b860591d5 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 84a7b517ab..9f1808a632 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 0c2f2832ea..83d69edb06 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 d29ff71290..1762400b3d 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 0a05007b66..873f538ca6 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 c618c95872..e46df37c17 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 02e03f4c4e..7ed886a06e 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 6234ba380b..6e5bb32eda 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 236baed74c..537e731f14 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 626934b20e..7555b20697 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 d83d189cd7..e4f5ec9d32 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 927c3811c8..d96ff0db8d 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 5915c5d59f..4423f8a1ec 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 0000000000..db5a1ed379 --- /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 0000000000..6c84d83c4c --- /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 94e289be03..f71d484f9c 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 bbc31a4a29..fca98855fd 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 438ea8b8b4..c3a2e2b5d9 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 31f5134918..5b784b3d07 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 891d0016fe..d2012ecf20 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 f9b346e02b..6591b179fc 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 fe2731f9dd..37d8b9e3fb 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 9ed62490c0..9ff8a3d88b 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 0000000000..54c8c5ed8e --- /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 2a8b07b0e8..80d977a73c 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 0000000000..c95ff25aff --- /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 0000000000..a687676576 --- /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 0000000000..ff174b04a5 --- /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 786b15b9d1..229ac1c0c3 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 32bdc3ad3a..7ffd869535 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 cca2c75834..21e901765e 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 e1442ad702..555d802460 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 bb4bc2dd3b..099baebb9a 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 c78bd30873..96d4289028 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 188fc543ef..db4d61691c 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 7d1871a091..5f7c910aec 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 e112309e32..df74602e6d 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 478cb6a6b7..666bfc86c4 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 4afe0920f7..577080b0f3 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 055962ac49..7cc733665a 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 1ed2e6cab9..365508e31e 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",