diff --git a/.github/renovate.json5 b/.github/renovate.json5 index e646049c3d6..8e31ab7a384 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -106,7 +106,6 @@ "@emotion/css", "@webcomponents/custom-elements", "bitwarden-russh", - "bytes", "concurrently", "cross-env", "del", diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 8063306662d..49d9d4c079f 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -387,6 +387,15 @@ jobs: rustup target add aarch64-unknown-linux-musl node build.js --target=aarch64-unknown-linux-musl --release + - name: Check index.d.ts generated + if: github.event_name == 'pull_request' && steps.cache.outputs.cache-hit != 'true' + working-directory: apps/desktop/desktop_native + run: | + if ! git diff --quiet --name-only -- napi/index.d.ts; then + echo "NAPI index.d.ts doesn't match, make sure to regenerate it and commit it" + exit 1 + fi + - name: Build application run: npm run dist:lin:arm64 diff --git a/.github/workflows/nx.yml b/.github/workflows/nx.yml new file mode 100644 index 00000000000..13d151225c2 --- /dev/null +++ b/.github/workflows/nx.yml @@ -0,0 +1,41 @@ +name: Experimental Nx CI +on: + pull_request: + types: [opened, synchronize] + +permissions: + contents: read + +jobs: + nx-experiment: + name: Run Nx Affected Tasks + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + + - name: Get Node Version + id: retrieve-node-version + working-directory: ./ + run: | + NODE_NVMRC=$(cat .nvmrc) + NODE_VERSION=${NODE_NVMRC/v/''} + echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT + + - name: Set up Node + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + with: + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + node-version: ${{ env._NODE_VERSION }} + + - name: Install dependencies + run: npm ci + + - name: Set Nx SHAs for affected detection + uses: nrwl/nx-set-shas@826660b82addbef3abff5fa871492ebad618c9e1 # v4.3.3 + + - name: Run Nx affected tasks + run: npx nx affected -t build lint test \ No newline at end of file diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index b2d5563c545..397ea877cb5 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "تعديل" }, diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 66b7f948d92..c5a688f152f 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Axtarışı sıfırla" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Düzəliş et" }, diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 2da642ef6f2..44c82ef85b4 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Рэдагаваць" }, diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 3f77317f06e..a440690cee1 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Нулиране на търсенето" }, + "archive": { + "message": "Архивиране" + }, + "unarchive": { + "message": "Изваждане от архива" + }, + "itemsInArchive": { + "message": "Елементи в архива" + }, + "noItemsInArchive": { + "message": "Няма елементи в архива" + }, + "noItemsInArchiveDesc": { + "message": "Архивираните елементи ще се показват тук и ще бъдат изключени от общите резултати при търсене и от предложенията за автоматично попълване." + }, + "itemSentToArchive": { + "message": "Елементът е преместен в архива" + }, + "itemRemovedFromArchive": { + "message": "Елементът е изваден от архива" + }, + "archiveItem": { + "message": "Архивиране на елемента" + }, + "archiveItemConfirmDesc": { + "message": "Архивираните елементи са изключени от общите резултати при търсене и от предложенията за автоматично попълване. Наистина ли искате да архивирате този елемент?" + }, "edit": { "message": "Редактиране" }, diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 40844b6e734..e7c4c36bce0 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "সম্পাদনা" }, diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index e3dae509a9d..d9003a749a6 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index a79d0be6327..2002dfc467f 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Restableix la cerca" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edita" }, diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 5427289fdf4..0638257d687 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Resetovat hledání" }, + "archive": { + "message": "Archivovat" + }, + "unarchive": { + "message": "Odebrat z archivu" + }, + "itemsInArchive": { + "message": "Položky v archivu" + }, + "noItemsInArchive": { + "message": "Žádné položky v archivu" + }, + "noItemsInArchiveDesc": { + "message": "Zde se zobrazí archivované položky a budou vyloučeny z obecných výsledků vyhledávání a návrhů automatického vyplňování." + }, + "itemSentToArchive": { + "message": "Položka byla přesunuta do archivu" + }, + "itemRemovedFromArchive": { + "message": "Položka byla odebrána z archivu" + }, + "archiveItem": { + "message": "Archivovat položku" + }, + "archiveItemConfirmDesc": { + "message": "Archivované položky jsou vyloučeny z obecných výsledků vyhledávání a z návrhů automatického vyplňování. Jste si jisti, že chcete tuto položku archivovat?" + }, "edit": { "message": "Upravit" }, diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 6f670cec95f..8756a138e81 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Golygu" }, diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index e778685b566..a78ff26fb0f 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Redigér" }, diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 96dca6c3acb..f04ca5b11be 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Suche zurücksetzen" }, + "archive": { + "message": "Archivieren" + }, + "unarchive": { + "message": "Archivierung aufheben" + }, + "itemsInArchive": { + "message": "Einträge im Archiv" + }, + "noItemsInArchive": { + "message": "Kein Eintrag im Archiv" + }, + "noItemsInArchiveDesc": { + "message": "Archivierte Einträge erscheinen hier und werden von allgemeinen Suchergebnissen und Autofill Vorschlägen ausgeschlossen." + }, + "itemSentToArchive": { + "message": "Eintrag an das Archiv gesendet" + }, + "itemRemovedFromArchive": { + "message": "Eintrag aus dem Archiv entfernt" + }, + "archiveItem": { + "message": "Eintrag archivieren" + }, + "archiveItemConfirmDesc": { + "message": "Archivierte Einträge sind von allgemeinen Suchergebnissen und Autofill Vorschlägen ausgeschlossen. Sind Sie sicher, dass Sie diesen Eintrag archivieren möchten?" + }, "edit": { "message": "Bearbeiten" }, @@ -5512,16 +5539,16 @@ "message": "Willkommen in deinem Tresor!" }, "phishingPageTitle": { - "message": "Phishing website" + "message": "Phishing Webseite" }, "phishingPageCloseTab": { - "message": "Close tab" + "message": "Tab schließen" }, "phishingPageContinue": { - "message": "Continue" + "message": "Weiter" }, "phishingPageLearnWhy": { - "message": "Why are you seeing this?" + "message": "Warum sehen Sie das?" }, "hasItemsVaultNudgeBodyOne": { "message": "Einträge für die aktuelle Seite automatisch ausfüllen" diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index ce98d454084..cce3e0ea39f 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Επαναφορά αναζήτησης" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Επεξεργασία" }, diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index aab0cc92092..43bb17c297f 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 54ad3612f78..59c4966a48c 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 266a74b034a..d3c6e3556a0 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Restablecer búsqueda" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Editar" }, diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 7b9d096be82..5508a1cee72 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Muuda" }, diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 1ccd8f1849c..93242263dc0 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Editatu" }, diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 97b92fa8c29..129f2ee383a 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "ویرایش" }, diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 36c16254a67..5de1d9fe7e4 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Nollaa haku" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Muokkaa" }, diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 8a2c9912e5d..600abfb2d4e 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "I-edit" }, diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 3323995736b..765ebff53c5 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Réinitialiser la recherche" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Modifier" }, diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 947baabdc2c..c2573ea6bfa 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Editar" }, diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 6cdb3898962..38fe3618610 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "אפס חיפוש" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "ערוך" }, diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 215b5f88445..1575543aef3 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "खोज रीसेट करें" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "संपादन करें" }, diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index a37cc0cf368..4f67de34071 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Ponovno postavljanje pretraživanja" }, + "archive": { + "message": "Arhiviraj" + }, + "unarchive": { + "message": "Poništi arhiviranje" + }, + "itemsInArchive": { + "message": "Stavke u arhivi" + }, + "noItemsInArchive": { + "message": "Nema stavki u arhivi" + }, + "noItemsInArchiveDesc": { + "message": "Arhivirane stavke biti će prikazane ovdje i biti će izuzete iz rezultata općih pretraga i preporuka auto-ispune." + }, + "itemSentToArchive": { + "message": "Stavka poslana u arhivu" + }, + "itemRemovedFromArchive": { + "message": "Stavka maknute iz arhive" + }, + "archiveItem": { + "message": "Arhiviraj stavku" + }, + "archiveItemConfirmDesc": { + "message": "Arhivirane stavke biti će izuzete iz rezultata općih pretraga i preporuka auto-ispune. Sigurno želiš arhivirati?" + }, "edit": { "message": "Uredi" }, @@ -5512,16 +5539,16 @@ "message": "Dobrodošli u svoj trezor!" }, "phishingPageTitle": { - "message": "Phishing website" + "message": "Phishing web stranica" }, "phishingPageCloseTab": { - "message": "Close tab" + "message": "Zatvori karticu" }, "phishingPageContinue": { - "message": "Continue" + "message": "Nastavi" }, "phishingPageLearnWhy": { - "message": "Why are you seeing this?" + "message": "Zašto ovo vidiš?" }, "hasItemsVaultNudgeBodyOne": { "message": "Auto-ispuni stavke za trenutnu stranicu" diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 367a9fd1f9f..864580a64b0 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -6,11 +6,11 @@ "message": "Bitwarden logó" }, "extName": { - "message": "Bitwarden Password Manager", + "message": "Bitwarden Jelszókezelő", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information", + "message": "Legyen otthon, munkában, vagy úton, a Bitwarden könnyen biztosítja jelszavát, kulcsait, és kényes információit", "description": "Extension description, MUST be less than 112 characters (Safari restriction)" }, "loginOrCreateNewAccount": { @@ -550,6 +550,33 @@ "resetSearch": { "message": "Keresés visszaállítása" }, + "archive": { + "message": "Archívum" + }, + "unarchive": { + "message": "Visszavétel archívumból" + }, + "itemsInArchive": { + "message": "Archívum elemek száma" + }, + "noItemsInArchive": { + "message": "Nincs elem az archívumban." + }, + "noItemsInArchiveDesc": { + "message": "Az archivált elemek itt jelennek meg és kizárásra kerülnek az általános keresési eredményekből és az automatikus kitöltési javaslatokból." + }, + "itemSentToArchive": { + "message": "Archívumba küldött elemek száma" + }, + "itemRemovedFromArchive": { + "message": "Az elem kikerült a kedvencekből." + }, + "archiveItem": { + "message": "Elem archiválása" + }, + "archiveItemConfirmDesc": { + "message": "Az archivált elemek ki vannak zárva az általános keresési eredményekből és az automatikus kitöltési javaslatokból. Biztosan archiválni szeretnénk ezt az elemet?" + }, "edit": { "message": "Szerkesztés" }, @@ -866,13 +893,13 @@ "message": "Kijelentkezett" }, "loggedOutDesc": { - "message": "You have been logged out of your account." + "message": "Kijelentkezett fiókjából." }, "loginExpired": { "message": "Bejelentkezési munkamenete lejárt." }, "logIn": { - "message": "Log in" + "message": "Bejelentkezés" }, "logInToBitwarden": { "message": "Bejelentkezés a Bitwardenbe" @@ -896,16 +923,16 @@ "message": "Kövessük az alábbi lépéseket a biztonsági kulccsal bejelentkezés befejezéséhez." }, "restartRegistration": { - "message": "Restart registration" + "message": "Regisztráció újraindítása" }, "expiredLink": { - "message": "Expired link" + "message": "Lejárt hivatkozás" }, "pleaseRestartRegistrationOrTryLoggingIn": { - "message": "Please restart registration or try logging in." + "message": "Kérem kezdje újra a regisztrációt, vagy próbáljon meg bejelentkezni." }, "youMayAlreadyHaveAnAccount": { - "message": "You may already have an account" + "message": "Lehetséges, hogy már rendelkezik fiókkal" }, "logOutConfirmation": { "message": "Biztos benne, hogy ki szeretnél jelentkezni?" @@ -1176,7 +1203,7 @@ "description": "Error message shown when the system fails to save login details." }, "saveFailureDetails": { - "message": "Oh no! We couldn't save this. Try entering the details manually.", + "message": "Ja ne! Nem tudtuk elmenteni. Próbálja meg beírni a kézzel.", "description": "Detailed error message shown when saving login details fails." }, "changePasswordWarning": { @@ -1606,7 +1633,7 @@ "description": "Represents the message for allowing the user to enable the autofill overlay" }, "autofillSuggestionsSectionTitle": { - "message": "Autofill suggestions" + "message": "Automatikus kitöltés javaslatok" }, "autofillSpotlightTitle": { "message": "Az automatikus kitöltési javaslatok könnyű megtalálása" diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index c14294534df..b38b6f05628 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Atur ulang pencarian" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index ed699b61c91..df4411ee42b 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Svuota ricerca" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Modifica" }, diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 7137e7e1a90..5305a265781 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "編集" }, diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index c916c0d958e..b759d674cca 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "ჩასწორება" }, diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 0cdb46d102e..78a49021a0c 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 8e9d04688b1..1311a97df68 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "ಎಡಿಟ್" }, diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index f17371f28ba..06611be0282 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "편집" }, diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index f0f78f1de0f..464fa5aae92 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Keisti" }, diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 00708a95e41..99edb486d9d 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Atiestatīt meklēšanu" }, + "archive": { + "message": "Arhivēt" + }, + "unarchive": { + "message": "Atcelt arhivēšanu" + }, + "itemsInArchive": { + "message": "Vienumi arhīvā" + }, + "noItemsInArchive": { + "message": "Arhīvā nav vienumu" + }, + "noItemsInArchiveDesc": { + "message": "Šeit parādīsies arhivētie vienumi, un tie netiks iekļauti vispārējās meklēšanas iznākumos un automātiskās aizpildes ieteikumos." + }, + "itemSentToArchive": { + "message": "Vienums ievietots arhīvā" + }, + "itemRemovedFromArchive": { + "message": "Vienums izņemts no arhīva" + }, + "archiveItem": { + "message": "Arhivēt vienumu" + }, + "archiveItemConfirmDesc": { + "message": "Arhivētie vienumi netiek iekļauti vispārējās meklēšanas iznākumos un automātiskās aizpildes ieteikumos. Vai tiešām ahrivēt šo vienumu?" + }, "edit": { "message": "Labot" }, diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 8554d74db8c..efe18c96a59 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "തിരുത്തുക" }, diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 5dddf1f2bde..16ac31ff599 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 0cdb46d102e..78a49021a0c 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index c77bce78939..a23fd7fe4c1 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Rediger" }, diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 0cdb46d102e..78a49021a0c 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 379ed54e490..2562b7a1d4c 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Zoekopdracht resetten" }, + "archive": { + "message": "Archiveren" + }, + "unarchive": { + "message": "Dearchiveren" + }, + "itemsInArchive": { + "message": "Items in archief" + }, + "noItemsInArchive": { + "message": "Geen items in archief" + }, + "noItemsInArchiveDesc": { + "message": "Gearchiveerde items verschijnen hier en worden uitgesloten van algemene zoekresultaten en automatisch invulsuggesties." + }, + "itemSentToArchive": { + "message": "Item naar archief verzonden" + }, + "itemRemovedFromArchive": { + "message": "Item verwijderd uit archief" + }, + "archiveItem": { + "message": "Item archiveren" + }, + "archiveItemConfirmDesc": { + "message": "Gearchiveerde items worden uitgesloten van algemene zoekresultaten en automatische invulsuggesties. Weet je zeker dat je dit item wilt archiveren?" + }, "edit": { "message": "Bewerken" }, diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 0cdb46d102e..78a49021a0c 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 0cdb46d102e..78a49021a0c 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index b0f2b7017b7..f24e790c9ad 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Zresetuj wyszukiwanie" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Usuń z archiwum" + }, + "itemsInArchive": { + "message": "Elementy w archiwum" + }, + "noItemsInArchive": { + "message": "Brak elementów w archiwum" + }, + "noItemsInArchiveDesc": { + "message": "Zarchiwizowane elementy pojawią się tutaj i zostaną wykluczone z wyników wyszukiwania i sugestii autouzupełniania." + }, + "itemSentToArchive": { + "message": "Element został przeniesiony do archiwum" + }, + "itemRemovedFromArchive": { + "message": "Element został usunięty z archiwum" + }, + "archiveItem": { + "message": "Archiwizuj element" + }, + "archiveItemConfirmDesc": { + "message": "Zarchiwizowane elementy są wykluczone z wyników wyszukiwania i sugestii autouzupełniania. Czy na pewno chcesz archiwizować element?" + }, "edit": { "message": "Edytuj" }, diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 5fd9d1673b6..2d7dd1e42a4 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Editar" }, diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 1a8ea3bfb3c..acc5b5332f9 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Repor pesquisa" }, + "archive": { + "message": "Arquivar" + }, + "unarchive": { + "message": "Desarquivar" + }, + "itemsInArchive": { + "message": "Itens no arquivo" + }, + "noItemsInArchive": { + "message": "Nenhum item no arquivo" + }, + "noItemsInArchiveDesc": { + "message": "Os itens arquivados aparecerão aqui e serão excluídos dos resultados gerais da pesquisa e das sugestões de preenchimento automático." + }, + "itemSentToArchive": { + "message": "Item movido para o arquivo" + }, + "itemRemovedFromArchive": { + "message": "Item removido do arquivo" + }, + "archiveItem": { + "message": "Arquivar item" + }, + "archiveItemConfirmDesc": { + "message": "Os itens arquivados são excluídos dos resultados gerais da pesquisa e das sugestões de preenchimento automático. Tem a certeza de que pretende arquivar este item?" + }, "edit": { "message": "Editar" }, @@ -1235,10 +1262,10 @@ "message": "Tema" }, "themeDesc": { - "message": "Altere o tema de cores da aplicação." + "message": "Altere o tema da aplicação." }, "themeDescAlt": { - "message": "Altere o tema de cores da aplicação. Aplica-se a todas as contas com sessão iniciada." + "message": "Altere o tema da aplicação. Aplica-se a todas as contas com sessão iniciada." }, "dark": { "message": "Escuro", diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index e57fe4dd19c..d184460e293 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Editare" }, diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 485665524d5..17133350e3f 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Сбросить поиск" }, + "archive": { + "message": "Архив" + }, + "unarchive": { + "message": "Разархивировать" + }, + "itemsInArchive": { + "message": "Элементы в архиве" + }, + "noItemsInArchive": { + "message": "В архиве нет элементов" + }, + "noItemsInArchiveDesc": { + "message": "Архивированные элементы появятся здесь и будут исключены из общих результатов поиска и предложений автозаполнения." + }, + "itemSentToArchive": { + "message": "Элемент отправлен в архив" + }, + "itemRemovedFromArchive": { + "message": "Элемент удален из архива" + }, + "archiveItem": { + "message": "Архивировать элемент" + }, + "archiveItemConfirmDesc": { + "message": "Архивированные элементы исключены из общих результатов поиска и предложений автозаполнения. Вы уверены, что хотите архивировать этот элемент?" + }, "edit": { "message": "Изменить" }, diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 9e6f27bab83..2fd8f53e148 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "සංස්කරණය" }, diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 7d410f67db3..d0e143cce4a 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Resetovať vyhľadávanie" }, + "archive": { + "message": "Archivovať" + }, + "unarchive": { + "message": "Zrušiť archiváciu" + }, + "itemsInArchive": { + "message": "Položky v archíve" + }, + "noItemsInArchive": { + "message": "Žiadne položky v archíve" + }, + "noItemsInArchiveDesc": { + "message": "Tu sa zobrazia archivované položky, ktoré budú vylúčené zo všeobecného vyhľadávania a z návrhov automatického vypĺňania." + }, + "itemSentToArchive": { + "message": "Položka bola archivovaná" + }, + "itemRemovedFromArchive": { + "message": "Položka bola odobraná z archívu" + }, + "archiveItem": { + "message": "Archivovať položku" + }, + "archiveItemConfirmDesc": { + "message": "Archivované položky sú vylúčené zo všeobecného vyhľadávania a z návrhov automatického vypĺňania. Naozaj chcete archivovať túto položku?" + }, "edit": { "message": "Upraviť" }, diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index d742e0a4b2d..81b1a6bb52c 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Uredi" }, diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 5de4d4da336..cc4abafe878 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Ресетовати претрагу" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Уреди" }, @@ -1755,7 +1782,7 @@ "message": "Ако кликнете изван искачућег прозора да бисте проверили имејл за верификациони код, овај прозор ће се затворити. Да ли желите да отворите овај прозор у новом прозору да се не би затворио?" }, "showIconsChangePasswordUrls": { - "message": "Show website icons and retrieve change password URLs" + "message": "Прикажи иконе веб локација и преузмите линкове промене лозинке" }, "cardholderName": { "message": "Име Власника Картице" @@ -1920,79 +1947,79 @@ "message": "Белешка" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "Ново пријављивање", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "Нова картица", "description": "Header for new card item type" }, "newItemHeaderIdentity": { - "message": "New Identity", + "message": "Нови идентитет", "description": "Header for new identity item type" }, "newItemHeaderNote": { - "message": "New Note", + "message": "Нова белешка", "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "Нов SSH кљич", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { - "message": "New Text Send", + "message": "Нови текст Send", "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "New File Send", + "message": "Нова датотека Send", "description": "Header for new file send" }, "editItemHeaderLogin": { - "message": "Edit Login", + "message": "Уреди пријаву", "description": "Header for edit login item type" }, "editItemHeaderCard": { - "message": "Edit Card", + "message": "Уреди картицу", "description": "Header for edit card item type" }, "editItemHeaderIdentity": { - "message": "Edit Identity", + "message": "Уреди идентитет", "description": "Header for edit identity item type" }, "editItemHeaderNote": { - "message": "Edit Note", + "message": "Уреди белешку", "description": "Header for edit note item type" }, "editItemHeaderSshKey": { - "message": "Edit SSH key", + "message": "Уреди SSH кључ", "description": "Header for edit SSH key item type" }, "editItemHeaderTextSend": { - "message": "Edit Text Send", + "message": "Уреди текст Send", "description": "Header for edit text send" }, "editItemHeaderFileSend": { - "message": "Edit File Send", + "message": "Уреди датотеку Send", "description": "Header for edit file send" }, "viewItemHeaderLogin": { - "message": "View Login", + "message": "Преглед пријаве", "description": "Header for view login item type" }, "viewItemHeaderCard": { - "message": "View Card", + "message": "Преглед картице", "description": "Header for view card item type" }, "viewItemHeaderIdentity": { - "message": "View Identity", + "message": "Преглед идентитета", "description": "Header for view identity item type" }, "viewItemHeaderNote": { - "message": "View Note", + "message": "Преглед белешке", "description": "Header for view note item type" }, "viewItemHeaderSshKey": { - "message": "View SSH key", + "message": "Преглед SSH кључа", "description": "Header for view SSH key item type" }, "passwordHistory": { @@ -5512,16 +5539,16 @@ "message": "Добродошли у ваш сеф!" }, "phishingPageTitle": { - "message": "Phishing website" + "message": "Пронађен злонамеран сајт" }, "phishingPageCloseTab": { - "message": "Close tab" + "message": "Затвори језичак" }, "phishingPageContinue": { - "message": "Continue" + "message": "Настави" }, "phishingPageLearnWhy": { - "message": "Why are you seeing this?" + "message": "Зашто видите ово?" }, "hasItemsVaultNudgeBodyOne": { "message": "Ауто-пуњење предмета за тренутну страницу" @@ -5599,10 +5626,10 @@ "description": "Aria label for the body content of the generator nudge" }, "aboutThisSetting": { - "message": "About this setting" + "message": "О овом подешавању" }, "permitCipherDetailsDescription": { - "message": "Bitwarden will use saved login URIs to identify which icon or change password URL should be used to improve your experience. No information is collected or saved when you use this service." + "message": "Bitwarden ће користити сачуване URI-јеве за пријаву да би одредио коју икону или URL за промену лозинке треба користити како би побољшао ваше искуство. Никакви подаци нису сакупљени нити сачувани приликом коришћења ове услуге." }, "noPermissionsViewPage": { "message": "Немате дозволе за преглед ове странице. Покушајте да се пријавите са другим налогом." diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 91c8919c810..8b0263bf15a 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Nollställ sökning" }, + "archive": { + "message": "Arkivera" + }, + "unarchive": { + "message": "Packa upp" + }, + "itemsInArchive": { + "message": "Objekt i arkiv" + }, + "noItemsInArchive": { + "message": "Inga objekt i arkivet" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Arkivera objekt" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Redigera" }, diff --git a/apps/browser/src/_locales/ta/messages.json b/apps/browser/src/_locales/ta/messages.json index 5660467e3ff..8d2199db6ca 100644 --- a/apps/browser/src/_locales/ta/messages.json +++ b/apps/browser/src/_locales/ta/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "தேடலை மீட்டமை" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "திருத்து" }, diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 0cdb46d102e..78a49021a0c 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Edit" }, diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 93f064c6801..61f97564f6a 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Reset search" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "แก้ไข" }, diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index ce3d485a937..0b65ae7d476 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Aramayı sıfırla" }, + "archive": { + "message": "Arşivle" + }, + "unarchive": { + "message": "Arşivden çıkar" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Düzenle" }, diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 24f7f71b9b2..850c174f666 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Скинути пошук" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Змінити" }, diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 57d404438ef..76bea4120cd 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "Đặt lại tìm kiếm" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "Sửa" }, diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 9e8ce80a29b..051914b120c 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "重置搜索" }, + "archive": { + "message": "归档" + }, + "unarchive": { + "message": "取消归档" + }, + "itemsInArchive": { + "message": "归档中的项目" + }, + "noItemsInArchive": { + "message": "归档中没有项目" + }, + "noItemsInArchiveDesc": { + "message": "已归档的项目将显示在此处,并将被排除在一般搜索结果和自动填充建议之外。" + }, + "itemSentToArchive": { + "message": "项目已归档" + }, + "itemRemovedFromArchive": { + "message": "项目已取消归档" + }, + "archiveItem": { + "message": "归档项目" + }, + "archiveItemConfirmDesc": { + "message": "已归档的项目将被排除在一般搜索结果和自动填充建议之外。确定要归档此项目吗?" + }, "edit": { "message": "编辑" }, @@ -814,7 +841,7 @@ "message": "您可以关闭此窗口" }, "masterPassSent": { - "message": "我们已经为您发送了包含主密码提示的电子邮件。" + "message": "我们已经向您发送了一封包含主密码提示的电子邮件。" }, "verificationCodeRequired": { "message": "必须填写验证码。" @@ -1755,7 +1782,7 @@ "message": "如果您点击弹窗外的区域以检查您的验证码电子邮件,将导致弹窗关闭。您想在新窗口中打开此弹窗,以便它不会关闭吗?" }, "showIconsChangePasswordUrls": { - "message": "显示网站图标并检索更改密码的 URL" + "message": "显示网站图标并获取更改密码的 URL" }, "cardholderName": { "message": "持卡人姓名" @@ -4376,7 +4403,7 @@ "message": "仅此一次" }, "alwaysForThisSite": { - "message": "总是为此站点" + "message": "始终适用于此站点" }, "domainAddedToExcludedDomains": { "message": "$DOMAIN$ 已添加到排除域名列表。", @@ -5521,7 +5548,7 @@ "message": "继续" }, "phishingPageLearnWhy": { - "message": "您为什么会看到这个?" + "message": "为什么您会看到这个?" }, "hasItemsVaultNudgeBodyOne": { "message": "为当前页面自动填充项目" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index b528695ccc4..f5801fb2c7d 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -550,6 +550,33 @@ "resetSearch": { "message": "重設搜尋" }, + "archive": { + "message": "Archive" + }, + "unarchive": { + "message": "Unarchive" + }, + "itemsInArchive": { + "message": "Items in archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "noItemsInArchiveDesc": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, + "itemSentToArchive": { + "message": "Item sent to archive" + }, + "itemRemovedFromArchive": { + "message": "Item removed from archive" + }, + "archiveItem": { + "message": "Archive item" + }, + "archiveItemConfirmDesc": { + "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, "edit": { "message": "編輯" }, @@ -1049,10 +1076,10 @@ "message": "於分頁頁面顯示身分以便於自動填入。" }, "clickToAutofillOnVault": { - "message": "在密碼庫檢視中點擊項目來自動填入" + "message": "在密碼庫檢視中點選項目來自動填入" }, "clickToAutofill": { - "message": "Click items in autofill suggestion to fill" + "message": "點選自動填入建議中的項目進行填入" }, "clearClipboard": { "message": "清除剪貼簿", @@ -1562,16 +1589,16 @@ "message": "輸入寄送到您電子郵件信箱的驗證碼。" }, "selfHostedEnvironment": { - "message": "自我裝載環境" + "message": "自行部署環境" }, "selfHostedBaseUrlHint": { - "message": "指定您自建的 Bitwarden 伺服器的網域 URL。例如:https://bitwarden.company.com" + "message": "指定您自架的 Bitwarden 伺服器的網域 URL。例如:https://bitwarden.company.com" }, "selfHostedCustomEnvHeader": { "message": "適用於進階設定。您可以單獨指定各個服務的網域 URL。" }, "selfHostedEnvFormInvalid": { - "message": "您必須新增伺服器網域 URL 或至少一個自定義環境。" + "message": "您必須新增伺服器網域 URL 或至少一個自訂環境。" }, "customEnvironment": { "message": "自訂環境" @@ -1580,7 +1607,7 @@ "message": "伺服器 URL" }, "selfHostBaseUrl": { - "message": "自建伺服器 URL", + "message": "自架伺服器 URL", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -5134,7 +5161,7 @@ } }, "showQuickCopyActions": { - "message": "在密碼庫中顯示快速複製" + "message": "在密碼庫中顯示快速複製圖示" }, "systemDefault": { "message": "系統預設值" diff --git a/apps/browser/src/autofill/background/abstractions/overlay-notifications.background.ts b/apps/browser/src/autofill/background/abstractions/overlay-notifications.background.ts index 71452ec975a..a70ffe25310 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay-notifications.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay-notifications.background.ts @@ -19,7 +19,7 @@ export type LoginSecurityTaskInfo = { export type WebsiteOriginsWithFields = Map>; -export type ActiveFormSubmissionRequests = Set; +export type ActiveFormSubmissionRequests = Set; export type ModifyLoginCipherFormData = { uri: string; diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 75f2659c9df..6067d563db2 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -48,6 +48,7 @@ export type FocusedFieldData = { frameId?: number; accountCreationFieldType?: string; showPasskeys?: boolean; + focusedFieldForm?: string; }; export type InlineMenuElementPosition = { diff --git a/apps/browser/src/autofill/background/auto-submit-login.background.spec.ts b/apps/browser/src/autofill/background/auto-submit-login.background.spec.ts index 373354b4c54..82a907a9e43 100644 --- a/apps/browser/src/autofill/background/auto-submit-login.background.spec.ts +++ b/apps/browser/src/autofill/background/auto-submit-login.background.spec.ts @@ -110,11 +110,11 @@ describe("AutoSubmitLoginBackground", () => { }); describe("when the AutomaticAppLogIn policy is valid and active", () => { - let webRequestDetails: chrome.webRequest.WebRequestBodyDetails; + let webRequestDetails: chrome.webRequest.WebRequestDetails; describe("starting the auto-submit login workflow", () => { beforeEach(async () => { - webRequestDetails = mock({ + webRequestDetails = mock({ initiator: validIpdUrl1, url: validAutoSubmitUrl, type: "main_frame", @@ -196,7 +196,7 @@ describe("AutoSubmitLoginBackground", () => { describe("cancelling an active auto-submit login workflow", () => { beforeEach(async () => { - webRequestDetails = mock({ + webRequestDetails = mock({ initiator: validIpdUrl1, url: validAutoSubmitUrl, type: "main_frame", @@ -280,7 +280,7 @@ describe("AutoSubmitLoginBackground", () => { }); describe("requests that occur within a sub-frame", () => { - const webRequestDetails = mock({ + const webRequestDetails = mock({ url: validAutoSubmitUrl, frameId: 1, }); @@ -324,7 +324,7 @@ describe("AutoSubmitLoginBackground", () => { it("updates the most recent idp host when a tab is activated", async () => { jest.spyOn(BrowserApi, "getTab").mockResolvedValue(newTab); - triggerTabOnActivatedEvent(mock({ tabId: newTabId })); + triggerTabOnActivatedEvent(mock({ tabId: newTabId })); await flushPromises(); expect(autoSubmitLoginBackground["mostRecentIdpHost"]).toStrictEqual({ @@ -336,7 +336,7 @@ describe("AutoSubmitLoginBackground", () => { it("updates the most recent id host when a tab is updated", () => { triggerTabOnUpdatedEvent( newTabId, - mock({ url: validIpdUrl1 }), + mock({ url: validIpdUrl1 }), newTab, ); @@ -389,7 +389,7 @@ describe("AutoSubmitLoginBackground", () => { tabId: newTabId, }; - triggerTabOnRemovedEvent(newTabId, mock()); + triggerTabOnRemovedEvent(newTabId, mock()); expect(autoSubmitLoginBackground["currentAutoSubmitHostData"]).toStrictEqual({}); }); @@ -403,14 +403,14 @@ describe("AutoSubmitLoginBackground", () => { tabId: tabId, }; triggerWebRequestOnBeforeRedirectEvent( - mock({ + mock({ url: validIpdUrl1, redirectUrl: validIpdUrl2, frameId: 0, }), ); triggerWebRequestOnBeforeRedirectEvent( - mock({ + mock({ url: validIpdUrl2, redirectUrl: validAutoSubmitUrl, frameId: 0, @@ -418,7 +418,7 @@ describe("AutoSubmitLoginBackground", () => { ); triggerWebRequestOnBeforeRequestEvent( - mock({ + mock({ tabId: tabId, url: `https://${validAutoSubmitHost}`, initiator: null, diff --git a/apps/browser/src/autofill/background/auto-submit-login.background.ts b/apps/browser/src/autofill/background/auto-submit-login.background.ts index dfdfa0f4d67..f593fab2516 100644 --- a/apps/browser/src/autofill/background/auto-submit-login.background.ts +++ b/apps/browser/src/autofill/background/auto-submit-login.background.ts @@ -161,7 +161,9 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * * @param details - The details of the request. */ - private handleOnBeforeRequest = (details: chrome.webRequest.WebRequestBodyDetails) => { + private handleOnBeforeRequest = ( + details: chrome.webRequest.OnBeforeRequestDetails, + ): undefined => { const requestInitiator = this.getRequestInitiator(details); const isValidInitiator = this.isValidInitiator(requestInitiator); @@ -191,7 +193,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * @param isValidInitiator - A flag indicating if the initiator of the request is valid. */ private postRequestEncounteredAfterSubmission = ( - details: chrome.webRequest.WebRequestBodyDetails, + details: chrome.webRequest.OnBeforeRequestDetails, isValidInitiator: boolean, ) => { return details.method === "POST" && this.validAutoSubmitHosts.size > 0 && isValidInitiator; @@ -205,7 +207,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * @param isValidInitiator - A flag indicating if the initiator of the request is valid. */ private requestRedirectsToInvalidHost = ( - details: chrome.webRequest.WebRequestBodyDetails, + details: chrome.webRequest.OnBeforeRequestDetails, isValidInitiator: boolean, ) => { return ( @@ -221,7 +223,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * * @param details - The details of the request. */ - private setupAutoSubmitFlow = (details: chrome.webRequest.WebRequestBodyDetails) => { + private setupAutoSubmitFlow = (details: chrome.webRequest.OnBeforeRequestDetails) => { if (this.isRequestInMainFrame(details)) { this.currentAutoSubmitHostData = { url: details.url, @@ -288,7 +290,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * @param details - The details of the request. */ private handleWebRequestOnBeforeRedirect = ( - details: chrome.webRequest.WebRedirectionResponseDetails, + details: chrome.webRequest.OnBeforeRedirectDetails, ) => { if (this.isRequestInMainFrame(details) && this.urlContainsAutoSubmitHash(details.redirectUrl)) { this.validAutoSubmitHosts.add(this.getUrlHost(details.redirectUrl)); @@ -354,7 +356,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr */ private disableAutoSubmitFlow = async ( requestInitiator: string, - details: chrome.webRequest.WebRequestBodyDetails, + details: chrome.webRequest.OnBeforeRequestDetails, ) => { if (this.isValidAutoSubmitHost(requestInitiator)) { this.removeUrlFromAutoSubmitHosts(requestInitiator); @@ -390,7 +392,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * @param initiator - The initiator of the request. */ private shouldRouteTriggerAutoSubmit = ( - details: chrome.webRequest.ResourceRequest, + details: chrome.webRequest.OnBeforeRequestDetails, initiator: string, ) => { if (this.isRequestInMainFrame(details)) { @@ -449,7 +451,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * * @param details - The details of the request. */ - private getRequestInitiator = (details: chrome.webRequest.ResourceRequest) => { + private getRequestInitiator = (details: chrome.webRequest.OnBeforeRequestDetails) => { if (!this.isSafariBrowser) { return details.initiator || (details as browser.webRequest._OnBeforeRequestDetails).originUrl; } @@ -470,7 +472,12 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * * @param details - The details of the request. */ - private isRequestInMainFrame = (details: chrome.webRequest.ResourceRequest) => { + private isRequestInMainFrame = ( + details: SetPartial< + chrome.webRequest.WebRequestDetails, + "documentId" | "documentLifecycle" | "frameType" + >, + ) => { if (this.isSafariBrowser) { return details.frameId === 0; } @@ -545,7 +552,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * * @param activeInfo - The active tab information. */ - private handleSafariTabOnActivated = async (activeInfo: chrome.tabs.TabActiveInfo) => { + private handleSafariTabOnActivated = async (activeInfo: chrome.tabs.OnActivatedInfo) => { if (activeInfo.tabId < 0) { return; } @@ -562,7 +569,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr * @param tabId - The tab ID associated with the URL. * @param changeInfo - The change information of the tab. */ - private handleSafariTabOnUpdated = (tabId: number, changeInfo: chrome.tabs.TabChangeInfo) => { + private handleSafariTabOnUpdated = (tabId: number, changeInfo: chrome.tabs.OnUpdatedInfo) => { if (changeInfo) { this.setMostRecentIdpHost(changeInfo.url, tabId); } diff --git a/apps/browser/src/autofill/background/overlay-notifications.background.spec.ts b/apps/browser/src/autofill/background/overlay-notifications.background.spec.ts index cf317de4fd2..c596a1ba774 100644 --- a/apps/browser/src/autofill/background/overlay-notifications.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay-notifications.background.spec.ts @@ -385,7 +385,7 @@ describe("OverlayNotificationsBackground", () => { it("ignores requests that are not part of an active form submission", async () => { triggerWebRequestOnCompletedEvent( - mock({ + mock({ url: sender.url, tabId: sender.tab.id, requestId: "123345", @@ -409,7 +409,7 @@ describe("OverlayNotificationsBackground", () => { await flushPromises(); triggerWebRequestOnCompletedEvent( - mock({ + mock({ url: sender.url, tabId: sender.tab.id, requestId, @@ -438,7 +438,7 @@ describe("OverlayNotificationsBackground", () => { await flushPromises(); triggerWebRequestOnCompletedEvent( - mock({ + mock({ url: sender.url, tabId: sender.tab.id, statusCode: 404, @@ -492,7 +492,7 @@ describe("OverlayNotificationsBackground", () => { ); }); triggerWebRequestOnCompletedEvent( - mock({ + mock({ url: sender.url, tabId: sender.tab.id, requestId, @@ -541,7 +541,7 @@ describe("OverlayNotificationsBackground", () => { }); triggerWebRequestOnCompletedEvent( - mock({ + mock({ url: sender.url, tabId: sender.tab.id, requestId, @@ -643,7 +643,7 @@ describe("OverlayNotificationsBackground", () => { }); it("clears all associated data with a removed tab", () => { - triggerTabOnRemovedEvent(sender.tab.id, mock()); + triggerTabOnRemovedEvent(sender.tab.id, mock()); expect(overlayNotificationsBackground["websiteOriginsWithFields"].size).toBe(0); }); @@ -652,7 +652,7 @@ describe("OverlayNotificationsBackground", () => { it("skips clearing the website origins if the changeInfo does not contain a `loading` status", () => { triggerTabOnUpdatedEvent( sender.tab.id, - mock({ status: "complete" }), + mock({ status: "complete" }), mock({ status: "complete" }), ); @@ -662,7 +662,7 @@ describe("OverlayNotificationsBackground", () => { it("skips clearing the website origins if the changeInfo does not contain a url", () => { triggerTabOnUpdatedEvent( sender.tab.id, - mock({ status: "loading", url: "" }), + mock({ status: "loading", url: "" }), mock({ status: "loading" }), ); @@ -672,7 +672,7 @@ describe("OverlayNotificationsBackground", () => { it("skips clearing the website origins if the tab does not contain known website origins", () => { triggerTabOnUpdatedEvent( 199, - mock({ status: "loading", url: "https://example.com" }), + mock({ status: "loading", url: "https://example.com" }), mock({ status: "loading", id: 199 }), ); @@ -682,7 +682,7 @@ describe("OverlayNotificationsBackground", () => { it("skips clearing the website origins if the changeInfo's url is present as part of the know website origin match patterns", () => { triggerTabOnUpdatedEvent( sender.tab.id, - mock({ + mock({ status: "loading", url: "https://subdomain.example.com", }), @@ -695,7 +695,7 @@ describe("OverlayNotificationsBackground", () => { it("clears all associated data with a tab that is entering a `loading` state", () => { triggerTabOnUpdatedEvent( sender.tab.id, - mock({ status: "loading" }), + mock({ status: "loading" }), mock({ status: "loading" }), ); diff --git a/apps/browser/src/autofill/background/overlay-notifications.background.ts b/apps/browser/src/autofill/background/overlay-notifications.background.ts index e7126a57e9f..4657dfb6d1f 100644 --- a/apps/browser/src/autofill/background/overlay-notifications.background.ts +++ b/apps/browser/src/autofill/background/overlay-notifications.background.ts @@ -228,7 +228,9 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * * @param details - The details of the web request */ - private handleOnBeforeRequestEvent = (details: chrome.webRequest.WebRequestDetails) => { + private handleOnBeforeRequestEvent = ( + details: chrome.webRequest.OnBeforeRequestDetails, + ): undefined => { if (this.isPostSubmissionFormRedirection(details)) { this.setupNotificationInitTrigger( details.tabId, @@ -275,7 +277,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * * @param details - The details of the web request */ - private isPostSubmissionFormRedirection = (details: chrome.webRequest.WebRequestDetails) => { + private isPostSubmissionFormRedirection = (details: chrome.webRequest.OnBeforeRequestDetails) => { return ( details.method?.toUpperCase() === "GET" && this.activeFormSubmissionRequests.has(details.requestId) && @@ -289,7 +291,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * * @param details - The details of the web request */ - private isValidFormSubmissionRequest = (details: chrome.webRequest.WebRequestDetails) => { + private isValidFormSubmissionRequest = (details: chrome.webRequest.OnBeforeRequestDetails) => { return ( !this.requestHostIsInvalid(details) && this.formSubmissionRequestMethods.has(details.method?.toUpperCase()) @@ -325,7 +327,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * * @param details - The details of the web response */ - private handleOnCompletedRequestEvent = async (details: chrome.webRequest.WebResponseDetails) => { + private handleOnCompletedRequestEvent = async (details: chrome.webRequest.OnCompletedDetails) => { if ( this.requestHostIsInvalid(details) || !this.activeFormSubmissionRequests.has(details.requestId) @@ -382,8 +384,8 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * @param modifyLoginData - The modified login form data */ private delayNotificationInitUntilTabIsComplete = async ( - tabId: chrome.webRequest.ResourceRequest["tabId"], - requestId: chrome.webRequest.ResourceRequest["requestId"], + tabId: chrome.webRequest.WebRequestDetails["tabId"], + requestId: chrome.webRequest.WebRequestDetails["requestId"], modifyLoginData: ModifyLoginCipherFormData, ) => { const handleWebNavigationOnCompleted = async () => { @@ -403,7 +405,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * @param tab - The tab details */ private processNotifications = async ( - requestId: chrome.webRequest.ResourceRequest["requestId"], + requestId: chrome.webRequest.WebRequestDetails["requestId"], modifyLoginData: ModifyLoginCipherFormData, tab: chrome.tabs.Tab, config: { skippable: NotificationType[] } = { skippable: [] }, @@ -477,7 +479,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * @param tab - The tab details */ private clearCompletedWebRequest = ( - requestId: chrome.webRequest.ResourceRequest["requestId"], + requestId: chrome.webRequest.WebRequestDetails["requestId"], tabId: chrome.tabs.Tab["id"], ) => { this.activeFormSubmissionRequests.delete(requestId); @@ -492,7 +494,12 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * * @param details - The details of the web request */ - private requestHostIsInvalid = (details: chrome.webRequest.ResourceRequest) => { + private requestHostIsInvalid = ( + details: SetPartial< + chrome.webRequest.WebRequestDetails, + "documentId" | "documentLifecycle" | "frameType" + >, + ) => { return !details.url?.startsWith("http") || details.tabId < 0; }; @@ -553,7 +560,7 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg * @param tabId - The id of the tab that was updated * @param changeInfo - The change info of the tab */ - private handleTabUpdated = (tabId: number, changeInfo: chrome.tabs.TabChangeInfo) => { + private handleTabUpdated = (tabId: number, changeInfo: chrome.tabs.OnUpdatedInfo) => { if (changeInfo.status !== "loading" || !changeInfo.url) { return; } diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index 696454b4248..47a5e8fec4c 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -3371,7 +3371,7 @@ describe("OverlayBackground", () => { }); await flushPromises(); triggerWebRequestOnCompletedEvent( - mock({ + mock({ statusCode: 401, }), ); @@ -3391,7 +3391,7 @@ describe("OverlayBackground", () => { }); await flushPromises(); triggerWebRequestOnCompletedEvent( - mock({ + mock({ statusCode: 200, }), ); diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index a2eb6eb7e90..35585d58863 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -1175,6 +1175,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { pageDetails, fillNewPassword: true, allowTotpAutofill: true, + focusedFieldForm: this.focusedFieldData?.focusedFieldForm, }); if (totpCode) { @@ -1237,7 +1238,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param details - The web request details */ private handlePasskeyAuthenticationOnCompleted = ( - details: chrome.webRequest.WebResponseCacheDetails, + details: chrome.webRequest.OnCompletedDetails, ) => { chrome.webRequest.onCompleted.removeListener(this.handlePasskeyAuthenticationOnCompleted); @@ -1859,6 +1860,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { pageDetails, fillNewPassword: true, allowTotpAutofill: false, + focusedFieldForm: this.focusedFieldData?.focusedFieldForm, }); globalThis.setTimeout(async () => { diff --git a/apps/browser/src/autofill/background/tabs.background.ts b/apps/browser/src/autofill/background/tabs.background.ts index cd2c1595d69..b76997c0ae9 100644 --- a/apps/browser/src/autofill/background/tabs.background.ts +++ b/apps/browser/src/autofill/background/tabs.background.ts @@ -81,7 +81,7 @@ export default class TabsBackground { */ private handleTabOnUpdated = async ( tabId: number, - changeInfo: chrome.tabs.TabChangeInfo, + changeInfo: chrome.tabs.OnUpdatedInfo, tab: chrome.tabs.Tab, ) => { if (this.focusedWindowId > 0 && tab.windowId !== this.focusedWindowId) { diff --git a/apps/browser/src/autofill/background/web-request.background.ts b/apps/browser/src/autofill/background/web-request.background.ts index 22e10a3dd0a..5c02f2df34d 100644 --- a/apps/browser/src/autofill/background/web-request.background.ts +++ b/apps/browser/src/autofill/background/web-request.background.ts @@ -26,7 +26,10 @@ export default class WebRequestBackground { startListening() { this.webRequest.onAuthRequired.addListener( - async (details, callback) => { + (async ( + details: chrome.webRequest.OnAuthRequiredDetails, + callback: (response: chrome.webRequest.BlockingResponse) => void, + ) => { if (!details.url || this.pendingAuthRequests.has(details.requestId)) { if (callback) { callback(null); @@ -42,7 +45,7 @@ export default class WebRequestBackground { } else { await this.resolveAuthCredentials(details.url, callback, callback); } - }, + }) as any, { urls: ["http://*/*", "https://*/*"] }, [this.isFirefox ? "blocking" : "asyncBlocking"], ); @@ -50,16 +53,17 @@ export default class WebRequestBackground { this.webRequest.onCompleted.addListener((details) => this.completeAuthRequest(details), { urls: ["http://*/*"], }); - this.webRequest.onErrorOccurred.addListener( - (details: any) => this.completeAuthRequest(details), - { - urls: ["http://*/*"], - }, - ); + this.webRequest.onErrorOccurred.addListener((details) => this.completeAuthRequest(details), { + urls: ["http://*/*"], + }); } - // eslint-disable-next-line - private async resolveAuthCredentials(domain: string, success: Function, error: Function) { + private async resolveAuthCredentials( + domain: string, + success: (response: chrome.webRequest.BlockingResponse) => void, + // eslint-disable-next-line + error: Function, + ) { const activeUserId = await firstValueFrom( this.accountService.activeAccount$.pipe(getOptionalUserId), ); @@ -97,7 +101,7 @@ export default class WebRequestBackground { } } - private completeAuthRequest(details: chrome.webRequest.WebResponseCacheDetails) { + private completeAuthRequest(details: chrome.webRequest.WebRequestDetails) { this.pendingAuthRequests.delete(details.requestId); } } diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.ts b/apps/browser/src/autofill/fido2/background/fido2.background.ts index 788c98ca85b..22ee4a1822d 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.ts @@ -218,7 +218,10 @@ export class Fido2Background implements Fido2BackgroundInterface { tabId: tab.id, injectDetails: { frame: "all_frames", ...this.sharedInjectionDetails }, mv2Details: { file: await this.getFido2PageScriptAppendFileName() }, - mv3Details: { file: Fido2ContentScript.PageScript, world: "MAIN" }, + mv3Details: { + file: Fido2ContentScript.PageScript, + world: chrome.scripting.ExecutionWorld.MAIN, + }, }); void this.scriptInjectorService.inject({ diff --git a/apps/browser/src/autofill/fido2/content/fido2-content-script.spec.ts b/apps/browser/src/autofill/fido2/content/fido2-content-script.spec.ts index af7344beb66..f50e541f677 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-content-script.spec.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-content-script.spec.ts @@ -75,7 +75,7 @@ describe("Fido2 Content Script", () => { data: mock(), }); const mockResult = { credentialId: "mock" } as CreateCredentialResult; - jest.spyOn(chrome.runtime, "sendMessage").mockResolvedValue(mockResult); + (jest.spyOn(chrome.runtime, "sendMessage") as jest.Mock).mockResolvedValue(mockResult); // FIXME: Remove when updating file. Eslint update // eslint-disable-next-line @typescript-eslint/no-require-imports @@ -167,7 +167,9 @@ describe("Fido2 Content Script", () => { data: mock(), }); const abortController = new AbortController(); - jest.spyOn(chrome.runtime, "sendMessage").mockResolvedValue({ error: errorMessage }); + (jest.spyOn(chrome.runtime, "sendMessage") as jest.Mock).mockResolvedValue({ + error: errorMessage, + }); // FIXME: Remove when updating file. Eslint update // eslint-disable-next-line @typescript-eslint/no-require-imports diff --git a/apps/browser/src/autofill/services/abstractions/autofill.service.ts b/apps/browser/src/autofill/services/abstractions/autofill.service.ts index 5eb8ee99eb5..09e22e278be 100644 --- a/apps/browser/src/autofill/services/abstractions/autofill.service.ts +++ b/apps/browser/src/autofill/services/abstractions/autofill.service.ts @@ -30,6 +30,7 @@ export interface AutoFillOptions { allowUntrustedIframe?: boolean; allowTotpAutofill?: boolean; autoSubmitLogin?: boolean; + focusedFieldForm?: string; } export interface FormData { diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index 07c97a5a344..656516d1119 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -974,6 +974,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ inlineMenuFillType: autofillFieldData?.inlineMenuFillType, showPasskeys: !!autofillFieldData?.showPasskeys, accountCreationFieldType: autofillFieldData?.accountCreationFieldType, + focusedFieldForm: autofillFieldData?.form, }; const allFields = this.formFieldElements; diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 512690929cc..89c3e2ee175 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -434,7 +434,15 @@ export default class AutofillService implements AutofillServiceInterface { return; } - const fillScript = await this.generateFillScript(pd.details, { + // If we have a focused form, filter the page details to only include fields from that form + const details = options.focusedFieldForm + ? { + ...pd.details, + fields: pd.details.fields.filter((f) => f.form === options.focusedFieldForm), + } + : pd.details; + + const fillScript = await this.generateFillScript(details, { skipUsernameOnlyFill: options.skipUsernameOnlyFill || false, onlyEmptyFields: options.onlyEmptyFields || false, fillNewPassword: options.fillNewPassword || false, diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts index 1e6c38bdce1..9ee329fa150 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts @@ -578,7 +578,7 @@ describe("CollectAutofillContentService", () => { const autofillFieldsData = await Promise.resolve(autofillFieldsPromise); expect(collectAutofillContentService["getAutofillFieldElements"]).toHaveBeenCalledWith( - 100, + 200, formFieldElements, ); expect(collectAutofillContentService["buildAutofillFieldItem"]).toHaveBeenCalledTimes(2); diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.ts index 0629621fad4..2ddee289044 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.ts @@ -278,7 +278,12 @@ export class CollectAutofillContentService implements CollectAutofillContentServ private async buildAutofillFieldsData( formFieldElements: FormFieldElement[], ): Promise { - const autofillFieldElements = this.getAutofillFieldElements(100, formFieldElements); + // Maximum number of form fields to process for autofill to prevent performance issues on pages with excessive fields + const autofillFieldsLimit = 200; + const autofillFieldElements = this.getAutofillFieldElements( + autofillFieldsLimit, + formFieldElements, + ); const autofillFieldDataPromises = autofillFieldElements.map(this.buildAutofillFieldItem); return Promise.all(autofillFieldDataPromises); diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts index e36d6811ecb..9edcdbb3a95 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts @@ -153,7 +153,9 @@ describe("InsertAutofillContentService", () => { it("returns early if the script is filling within a sand boxed iframe", async () => { Object.defineProperty(globalThis, "frameElement", { - value: { hasAttribute: jest.fn(() => true) }, + value: { + getAttribute: jest.fn(() => ""), + }, writable: true, }); jest.spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill"); diff --git a/apps/browser/src/autofill/spec/testing-utils.ts b/apps/browser/src/autofill/spec/testing-utils.ts index 1a3f3a52234..0082f022fb6 100644 --- a/apps/browser/src/autofill/spec/testing-utils.ts +++ b/apps/browser/src/autofill/spec/testing-utils.ts @@ -80,7 +80,7 @@ export function triggerWindowOnFocusedChangedEvent(windowId: number) { ); } -export function triggerTabOnActivatedEvent(activeInfo: chrome.tabs.TabActiveInfo) { +export function triggerTabOnActivatedEvent(activeInfo: chrome.tabs.OnActivatedInfo) { (chrome.tabs.onActivated.addListener as unknown as jest.SpyInstance).mock.calls.forEach( (call) => { const callback = call[0]; @@ -98,7 +98,7 @@ export function triggerTabOnReplacedEvent(addedTabId: number, removedTabId: numb export function triggerTabOnUpdatedEvent( tabId: number, - changeInfo: chrome.tabs.TabChangeInfo, + changeInfo: chrome.tabs.OnUpdatedInfo, tab: chrome.tabs.Tab, ) { (chrome.tabs.onUpdated.addListener as unknown as jest.SpyInstance).mock.calls.forEach((call) => { @@ -107,7 +107,7 @@ export function triggerTabOnUpdatedEvent( }); } -export function triggerTabOnRemovedEvent(tabId: number, removeInfo: chrome.tabs.TabRemoveInfo) { +export function triggerTabOnRemovedEvent(tabId: number, removeInfo: chrome.tabs.OnRemovedInfo) { (chrome.tabs.onRemoved.addListener as unknown as jest.SpyInstance).mock.calls.forEach((call) => { const callback = call[0]; callback(tabId, removeInfo); @@ -165,7 +165,7 @@ export function triggerWebRequestOnBeforeRedirectEvent( }); } -export function triggerWebRequestOnCompletedEvent(details: chrome.webRequest.WebResponseDetails) { +export function triggerWebRequestOnCompletedEvent(details: chrome.webRequest.OnCompletedDetails) { (chrome.webRequest.onCompleted.addListener as unknown as jest.SpyInstance).mock.calls.forEach( (call) => { const callback = call[0]; diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index 0e102dcfd99..a3d61c7f0b2 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -499,11 +499,24 @@ export function isInvalidResponseStatusCode(statusCode: number) { * Determines if the current context is within a sandboxed iframe. */ export function currentlyInSandboxedIframe(): boolean { - return ( - String(self.origin).toLowerCase() === "null" || - globalThis.frameElement?.hasAttribute("sandbox") || - globalThis.location.hostname === "" - ); + if (String(self.origin).toLowerCase() === "null" || globalThis.location.hostname === "") { + return true; + } + + const sandbox = globalThis.frameElement?.getAttribute?.("sandbox"); + + // No frameElement or sandbox attribute means not sandboxed + if (sandbox === null || sandbox === undefined) { + return false; + } + + // An empty string means fully sandboxed + if (sandbox === "") { + return true; + } + + const tokens = new Set(sandbox.toLowerCase().split(" ")); + return !["allow-scripts", "allow-same-origin"].every((token) => tokens.has(token)); } /** diff --git a/apps/browser/src/background/idle.background.ts b/apps/browser/src/background/idle.background.ts index 81a869917a6..2de4b48a9c0 100644 --- a/apps/browser/src/background/idle.background.ts +++ b/apps/browser/src/background/idle.background.ts @@ -46,7 +46,7 @@ export default class IdleBackground { if (this.idle.onStateChanged) { this.idle.onStateChanged.addListener( - async (newState: chrome.idle.IdleState | browser.idle.IdleState) => { + async (newState: `${chrome.idle.IdleState}` | browser.idle.IdleState) => { if (newState === "locked") { // Need to check if any of the current users have their timeout set to `onLocked` const allUsers = await firstValueFrom(this.accountService.accounts$); diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts index dd7cf083a01..1497ac96dba 100644 --- a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts +++ b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts @@ -230,7 +230,7 @@ export class PhishingDetectionService { */ private static async _processNavigation( tabId: number, - changeInfo: chrome.tabs.TabChangeInfo, + changeInfo: chrome.tabs.OnUpdatedInfo, tab: chrome.tabs.Tab, ): Promise { if (changeInfo.status !== "complete" || !tab.url) { @@ -253,7 +253,7 @@ export class PhishingDetectionService { private static _handleNavigationEvent( tabId: number, - changeInfo: chrome.tabs.TabChangeInfo, + changeInfo: chrome.tabs.OnUpdatedInfo, tab: chrome.tabs.Tab, ): boolean { this._navigationEventsSubject.next({ tabId, changeInfo, tab }); diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.types.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.types.ts index 86fe61909c4..21793616241 100644 --- a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.types.ts +++ b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.types.ts @@ -30,6 +30,6 @@ export type CaughtPhishingDomain = { export type PhishingDetectionNavigationEvent = { tabId: number; - changeInfo: chrome.tabs.TabChangeInfo; + changeInfo: chrome.tabs.OnUpdatedInfo; tab: chrome.tabs.Tab; }; diff --git a/apps/browser/src/key-management/key-connector/remove-password.component.html b/apps/browser/src/key-management/key-connector/remove-password.component.html index 56baf0de2a0..427065e83f3 100644 --- a/apps/browser/src/key-management/key-connector/remove-password.component.html +++ b/apps/browser/src/key-management/key-connector/remove-password.component.html @@ -1,53 +1,51 @@ -
-
-
- {{ "removeMasterPassword" | i18n }} -
-
-
+ + + + + + -
-
-
-
- -
+ @if (loading) { +
+ + {{ "loading" | i18n }}
-
-
-

{{ "removeMasterPasswordForOrganizationUserKeyConnector" | i18n }}

-

{{ "organizationName" | i18n }}:

-

{{ organization.name }}

-

{{ "keyConnectorDomain" | i18n }}:

-

{{ organization.keyConnectorUrl }}

-
-
- -
-
- -
-
-
-
+ } @else { +

{{ "removeMasterPasswordForOrganizationUserKeyConnector" | i18n }}

+

{{ "organizationName" | i18n }}:

+

{{ organization.name }}

+

{{ "keyConnectorDomain" | i18n }}:

+

{{ organization.keyConnectorUrl }}

+ + + + } +
diff --git a/apps/browser/src/platform/browser/browser-api.register-content-scripts-polyfill.ts b/apps/browser/src/platform/browser/browser-api.register-content-scripts-polyfill.ts index 0b0dd21824b..a8e7b7eeb37 100644 --- a/apps/browser/src/platform/browser/browser-api.register-content-scripts-polyfill.ts +++ b/apps/browser/src/platform/browser/browser-api.register-content-scripts-polyfill.ts @@ -141,8 +141,24 @@ function buildRegisterContentScriptsPolyfill() { return [possibleArray]; } - function arrayOrUndefined(value?: number) { - return value === undefined ? undefined : [value]; + function createTarget( + tabId: number, + frameId: number | undefined, + allFrames: boolean, + ): chrome.scripting.InjectionTarget { + if (frameId === undefined) { + return { + tabId, + frameIds: undefined, + allFrames: allFrames, + }; + } else { + return { + tabId, + frameIds: [frameId], + allFrames: undefined, + }; + } } async function insertCSS( @@ -170,15 +186,17 @@ function buildRegisterContentScriptsPolyfill() { } if (gotScripting) { - return chrome.scripting.insertCSS({ - target: { - tabId, - frameIds: arrayOrUndefined(frameId), - allFrames: frameId === undefined ? allFrames : undefined, - }, - files: "file" in content ? [content.file] : undefined, - css: "code" in content ? content.code : undefined, - }); + if ("file" in content) { + return chrome.scripting.insertCSS({ + target: createTarget(tabId, frameId, allFrames), + files: [content.file], + }); + } else { + return chrome.scripting.insertCSS({ + target: createTarget(tabId, frameId, allFrames), + css: content.code, + }); + } } return chromeProxy.tabs.insertCSS(tabId, { @@ -226,11 +244,7 @@ function buildRegisterContentScriptsPolyfill() { if (gotScripting) { assertNoCode(normalizedFiles); const injection = chrome.scripting.executeScript({ - target: { - tabId, - frameIds: arrayOrUndefined(frameId), - allFrames: frameId === undefined ? allFrames : undefined, - }, + target: createTarget(tabId, frameId, allFrames), files: normalizedFiles.map(({ file }: { file: string }) => file), }); @@ -397,7 +411,7 @@ function buildRegisterContentScriptsPolyfill() { }; const tabListener = async ( tabId: number, - { status }: chrome.tabs.TabChangeInfo, + { status }: chrome.tabs.OnUpdatedInfo, { url }: chrome.tabs.Tab, ) => { if (status === "loading" && url) { diff --git a/apps/browser/src/platform/browser/browser-api.spec.ts b/apps/browser/src/platform/browser/browser-api.spec.ts index 49d3e8e1cec..f7561b2b50b 100644 --- a/apps/browser/src/platform/browser/browser-api.spec.ts +++ b/apps/browser/src/platform/browser/browser-api.spec.ts @@ -375,7 +375,7 @@ describe("BrowserApi", () => { describe("executeScriptInTab", () => { it("calls to the extension api to execute a script within the give tabId", async () => { const tabId = 1; - const injectDetails = mock(); + const injectDetails = mock(); jest.spyOn(BrowserApi, "manifestVersion", "get").mockReturnValue(2); (chrome.tabs.executeScript as jest.Mock).mockImplementation( (tabId, injectDetails, callback) => callback(executeScriptResult), @@ -393,7 +393,7 @@ describe("BrowserApi", () => { it("calls the manifest v3 scripting API if the extension manifest is for v3", async () => { const tabId = 1; - const injectDetails = mock({ + const injectDetails = mock({ file: "file.js", allFrames: true, runAt: "document_start", @@ -419,7 +419,7 @@ describe("BrowserApi", () => { it("injects the script into a specified frameId when the extension is built for manifest v3", async () => { const tabId = 1; const frameId = 2; - const injectDetails = mock({ + const injectDetails = mock({ file: "file.js", allFrames: true, runAt: "document_start", @@ -443,7 +443,7 @@ describe("BrowserApi", () => { it("injects the script into the MAIN world context when injecting a script for manifest v3", async () => { const tabId = 1; - const injectDetails = mock({ + const injectDetails = mock({ file: null, allFrames: true, runAt: "document_start", diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index 339fd71b071..8a3dbafc5ce 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -685,29 +685,27 @@ export class BrowserApi { */ static executeScriptInTab( tabId: number, - details: chrome.tabs.InjectDetails, + details: chrome.extensionTypes.InjectDetails, scriptingApiDetails?: { world: chrome.scripting.ExecutionWorld; }, ): Promise { if (BrowserApi.isManifestVersion(3)) { - const target: chrome.scripting.InjectionTarget = { - tabId, - }; + let target: chrome.scripting.InjectionTarget; if (typeof details.frameId === "number") { - target.frameIds = [details.frameId]; - } - - if (!target.frameIds?.length && details.allFrames) { - target.allFrames = details.allFrames; + target = { tabId, frameIds: [details.frameId] }; + } else if (details.allFrames) { + target = { tabId, allFrames: true }; + } else { + target = { tabId }; } return chrome.scripting.executeScript({ target, files: details.file ? [details.file] : null, injectImmediately: details.runAt === "document_start", - world: scriptingApiDetails?.world || "ISOLATED", + world: scriptingApiDetails?.world || chrome.scripting.ExecutionWorld.ISOLATED, }); } diff --git a/apps/browser/src/platform/services/abstractions/chrome-storage-api.service.spec.ts b/apps/browser/src/platform/services/abstractions/chrome-storage-api.service.spec.ts index ac8a01375fa..c1d0aa235fb 100644 --- a/apps/browser/src/platform/services/abstractions/chrome-storage-api.service.spec.ts +++ b/apps/browser/src/platform/services/abstractions/chrome-storage-api.service.spec.ts @@ -52,7 +52,7 @@ describe("ChromeStorageApiService", () => { }); afterEach(() => { - chrome.runtime.lastError = undefined; + (chrome.runtime.lastError as any) = undefined; }); it("uses `objToStore` to prepare a value for set", async () => { @@ -80,7 +80,7 @@ describe("ChromeStorageApiService", () => { it("translates chrome.runtime.lastError to promise rejection", async () => { setMock.mockImplementation((data, callback) => { - chrome.runtime.lastError = new Error("Test Error"); + (chrome.runtime.lastError as any) = new Error("Test Error"); callback(); }); @@ -101,7 +101,7 @@ describe("ChromeStorageApiService", () => { }); afterEach(() => { - chrome.runtime.lastError = undefined; + (chrome.runtime.lastError as any) = undefined; }); it("returns a stored value when it is serialized", async () => { @@ -132,9 +132,9 @@ describe("ChromeStorageApiService", () => { it("translates chrome.runtime.lastError to promise rejection", async () => { getMock.mockImplementation((key, callback) => { - chrome.runtime.lastError = new Error("Test Error"); + (chrome.runtime.lastError as any) = new Error("Test Error"); callback(); - chrome.runtime.lastError = undefined; + (chrome.runtime.lastError as any) = undefined; }); await expect(async () => await service.get("test")).rejects.toThrow("Test Error"); diff --git a/apps/browser/src/platform/services/browser-script-injector.service.spec.ts b/apps/browser/src/platform/services/browser-script-injector.service.spec.ts index 21f6debc02f..e4abea1d719 100644 --- a/apps/browser/src/platform/services/browser-script-injector.service.spec.ts +++ b/apps/browser/src/platform/services/browser-script-injector.service.spec.ts @@ -41,7 +41,10 @@ describe("ScriptInjectorService", () => { const mv2SpecificFile = "content/autofill-init-mv2.js"; const mv2Details = { file: mv2SpecificFile }; const mv3SpecificFile = "content/autofill-init-mv3.js"; - const mv3Details: Mv3ScriptInjectionDetails = { file: mv3SpecificFile, world: "MAIN" }; + const mv3Details: Mv3ScriptInjectionDetails = { + file: mv3SpecificFile, + world: chrome.scripting.ExecutionWorld.MAIN, + }; const sharedInjectDetails: CommonScriptInjectionDetails = { runAt: "document_start", }; diff --git a/apps/browser/src/platform/services/browser-script-injector.service.ts b/apps/browser/src/platform/services/browser-script-injector.service.ts index a617f2215c0..575e9ccf70b 100644 --- a/apps/browser/src/platform/services/browser-script-injector.service.ts +++ b/apps/browser/src/platform/services/browser-script-injector.service.ts @@ -63,7 +63,7 @@ export class BrowserScriptInjectorService extends ScriptInjectorService { if (BrowserApi.isManifestVersion(3)) { try { await BrowserApi.executeScriptInTab(tabId, injectionDetails, { - world: mv3Details?.world ?? "ISOLATED", + world: mv3Details?.world ?? chrome.scripting.ExecutionWorld.ISOLATED, }); } catch (error) { // Swallow errors for host permissions, since this is believed to be a Manifest V3 Chrome bug @@ -112,9 +112,9 @@ export class BrowserScriptInjectorService extends ScriptInjectorService { private buildInjectionDetails( injectDetails: CommonScriptInjectionDetails, file: string, - ): chrome.tabs.InjectDetails { + ): chrome.extensionTypes.InjectDetails { const { frame, runAt } = injectDetails; - const injectionDetails: chrome.tabs.InjectDetails = { file }; + const injectionDetails: chrome.extensionTypes.InjectDetails = { file }; if (runAt) { injectionDetails.runAt = runAt; diff --git a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts index 81e1008eea8..61e56f08e16 100644 --- a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts +++ b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts @@ -167,7 +167,7 @@ describe("Browser Utils Service", () => { it("returns false if special error is sent", async () => { chrome.runtime.sendMessage = jest.fn().mockImplementation((message, callback) => { - chrome.runtime.lastError = new Error( + (chrome.runtime.lastError as any) = new Error( "Could not establish connection. Receiving end does not exist.", ); callback(undefined); @@ -177,7 +177,7 @@ describe("Browser Utils Service", () => { expect(isViewOpen).toBe(false); - chrome.runtime.lastError = null; + (chrome.runtime.lastError as any) = null; }); }); diff --git a/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts b/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts index 0eb4739ea92..b835c711853 100644 --- a/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts +++ b/apps/browser/src/platform/system-notifications/browser-system-notification.service.ts @@ -46,7 +46,7 @@ export class BrowserSystemNotificationService implements SystemNotificationsServ return new Promise((resolve) => { const deviceType: DeviceType = this.platformUtilsService.getDevice(); - const options: chrome.notifications.NotificationOptions = { + const options: chrome.notifications.NotificationCreateOptions = { iconUrl: chrome.runtime.getURL("images/icon128.png"), message: createInfo.body, type: "basic", @@ -70,6 +70,7 @@ export class BrowserSystemNotificationService implements SystemNotificationsServ } async clear(clearInfo: SystemNotificationClearInfo): Promise { + // eslint-disable-next-line @typescript-eslint/no-floating-promises chrome.notifications.clear(clearInfo.id); } diff --git a/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html b/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html index 4c394317d14..d389fd8d783 100644 --- a/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html +++ b/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html @@ -5,12 +5,9 @@ [showBackButton]="showBackButton" [pageTitle]="''" > - +
+ +
diff --git a/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts b/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts index 3b84eac2217..c1694d80668 100644 --- a/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts +++ b/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts @@ -5,7 +5,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, Data, NavigationEnd, Router, RouterModule } from "@angular/router"; import { Subject, filter, switchMap, takeUntil, tap } from "rxjs"; -import { ExtensionBitwardenLogo, Icon } from "@bitwarden/assets/svg"; +import { BitwardenLogo, Icon } from "@bitwarden/assets/svg"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { IconModule, @@ -62,7 +62,7 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy { protected hideCardWrapper: boolean = false; protected theme: string; - protected logo = ExtensionBitwardenLogo; + protected logo = BitwardenLogo; constructor( private router: Router, diff --git a/apps/browser/src/popup/scss/base.scss b/apps/browser/src/popup/scss/base.scss index df96199e1a9..b3d14e65061 100644 --- a/apps/browser/src/popup/scss/base.scss +++ b/apps/browser/src/popup/scss/base.scss @@ -151,6 +151,7 @@ textarea { app-root > div { height: 100%; + width: 100%; } main::-webkit-scrollbar, @@ -373,7 +374,8 @@ header:not(bit-callout header, bit-dialog header, popup-page header) { app-root { width: 100%; - height: 100%; + height: 100vh; + display: flex; @include themify($themes) { background-color: themed("backgroundColor"); diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts index bb61769de90..a37c038d822 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts @@ -5,11 +5,13 @@ import { combineLatest, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { NoResults, NoSendsIcon } from "@bitwarden/assets/svg"; +import { BrowserPremiumUpgradePromptService } from "@bitwarden/browser/vault/popup/services/browser-premium-upgrade-prompt.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { ButtonModule, CalloutModule, @@ -39,6 +41,12 @@ export enum SendState { @Component({ templateUrl: "send-v2.component.html", + providers: [ + { + provide: PremiumUpgradePromptService, + useClass: BrowserPremiumUpgradePromptService, + }, + ], imports: [ CalloutModule, PopupPageComponent, diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index a1e0a43343f..463819b96e4 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -17,6 +17,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; @@ -50,6 +51,7 @@ import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-p import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service"; import { PopupCloseWarningService } from "../../../../../popup/services/popup-close-warning.service"; import { BrowserCipherFormGenerationService } from "../../../services/browser-cipher-form-generation.service"; +import { BrowserPremiumUpgradePromptService } from "../../../services/browser-premium-upgrade-prompt.service"; import { BrowserTotpCaptureService } from "../../../services/browser-totp-capture.service"; import { fido2PopoutSessionData$, @@ -136,6 +138,7 @@ export type AddEditQueryParams = Partial>; { provide: CipherFormConfigService, useClass: DefaultCipherFormConfigService }, { provide: TotpCaptureService, useClass: BrowserTotpCaptureService }, { provide: CipherFormGenerationService, useClass: BrowserCipherFormGenerationService }, + { provide: PremiumUpgradePromptService, useClass: BrowserPremiumUpgradePromptService }, ], imports: [ CommonModule, diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.html b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.html index 2650345e94b..0fbe1c55b0a 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.html @@ -5,10 +5,10 @@ (click)="openAttachments()" [disabled]="parentFormDisabled" > - {{ "attachments" | i18n }} - - {{ "premium" | i18n }} - +
+ {{ "attachments" | i18n }} + +
diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts index 8f1729f0ab6..a2045736ce2 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts @@ -13,6 +13,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { ToastService } from "@bitwarden/components"; @@ -108,6 +109,12 @@ describe("OpenAttachmentsComponent", () => { provide: AccountService, useValue: accountService, }, + { + provide: PremiumUpgradePromptService, + useValue: { + promptForPremium: jest.fn().mockResolvedValue(null), + }, + }, ], }).compileComponents(); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts index 493d632dd64..26410a46187 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts @@ -6,6 +6,7 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { Router } from "@angular/router"; import { firstValueFrom, map, switchMap } from "rxjs"; +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { getOrganizationById, @@ -27,7 +28,14 @@ import { FilePopoutUtilsService } from "../../../../../../tools/popup/services/f @Component({ selector: "app-open-attachments", templateUrl: "./open-attachments.component.html", - imports: [BadgeModule, CommonModule, ItemModule, JslibModule, TypographyModule], + imports: [ + BadgeModule, + CommonModule, + ItemModule, + JslibModule, + TypographyModule, + PremiumBadgeComponent, + ], }) export class OpenAttachmentsComponent implements OnInit { /** Cipher `id` */ diff --git a/apps/browser/store/locales/zh_TW/copy.resx b/apps/browser/store/locales/zh_TW/copy.resx index ad3f12ae6f0..bfabc13f280 100644 --- a/apps/browser/store/locales/zh_TW/copy.resx +++ b/apps/browser/store/locales/zh_TW/copy.resx @@ -121,55 +121,55 @@ Bitwarden 密碼管理工具 - 無論在家、在辦公或在途中,Bitwarden 都能輕易的保護你的密碼、登入金鑰和敏感資訊。 + 無論在家中、工作中抑或旅途中,Bitwarden 都能輕鬆地保護您的密碼、登入金鑰和敏感資訊。 - Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! + 被 PCMag、WIRED、The Verge、CNET、G2 等認可為最佳的密碼管理工具! -SECURE YOUR DIGITAL LIFE -Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. +保護您的數位生活 +透過為每個帳戶產生並保存唯一的強密碼,保護您的數位生活並防止資料外洩。將所有內容儲存在只有您可以存取的端對端加密密碼庫中。 -ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE -Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. +隨時隨地在任何裝置上存取您的資料 +不受限制地跨裝置輕鬆管理、儲存、保護和分享無限多的密碼。 -EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE -Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. +每個人都應該擁有保持上網安全的工具 +免費使用 Bitwarden,沒有廣告或銷售資料。Bitwarden 認為每個人都應該有能力確保上網安全。進階版計劃提供對進階功能的存取。 -EMPOWER YOUR TEAMS WITH BITWARDEN -Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. +透過 BITWARDEN 強化您的團隊 +團隊和企業計劃具有專業的商業功能。包括 SSO 整合、自架服務、目錄整合和 SCIM 配置、全域原則、API 存取、事件記錄等。 -Use Bitwarden to secure your workforce and share sensitive information with colleagues. +使用 Bitwarden 來保護您的員工並與同事分享敏感資訊。 -More reasons to choose Bitwarden: +選擇 Bitwarden 的其他理由: -World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +世界級的加密 +密碼受到進階端對端加密(AES-256 位元加密、加鹽雜湊和 PBKDF2 SHA-256)的保護,因此您的資料保持安全和私密。 -3rd-party Audits -Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. +第三方稽核 +Bitwarden 定期與知名資安公司進行全面的第三方安全稽核。這些年度稽核包括 Bitwarden IP、伺服器和 Web 應用程式的原始程式碼評估和滲透測試。 -Advanced 2FA -Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. +進階的雙重要素驗證 +使用第三方驗證器、透過電子郵件傳送的代碼或 FIDO2 WebAuthn 憑證(例如硬體安全金鑰或密碼)來保護您的登入。 -Bitwarden Send -Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. +Bitwarden 傳送 +直接將資料傳輸給其他人,同時保持端對端加密安全性並限制暴露。 -Built-in Generator -Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. +內建產生器 +為您造訪的每個網站建立長、複雜且獨特的密碼和唯一的使用者名稱。與電子郵件別名提供者整合以進一步保護隱私。 -Global Translations -Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. +全球化翻譯 +Bitwarden 有 60 多種語言翻譯,由全球社群透過 Crowdin 翻譯。 -Cross-Platform Applications -Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. +跨平台應用程式 +透過任何瀏覽器、行動裝置或桌面作業系統等,保護和共用 Bitwarden 密碼庫中的敏感資料。 -Bitwarden secures more than just passwords -End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! +Bitwarden 保護的不僅是密碼 +Bitwarden 的端對端加密憑證管理解決方案可讓組織保護一切,包括開發人員機密和金鑰體驗。造訪 Bitwarden.com 以了解更多有關 Bitwarden 機密管理員和 Bitwarden Passwordless.dev 的資訊! - 無論在家、在辦公或在途中,Bitwarden 都能輕易的保護你的密碼、登入金鑰和敏感資訊。 + 無論在家中、工作中抑或旅途中,Bitwarden 都能輕鬆地保護您的密碼、登入金鑰和敏感資訊。 在多部裝置上同步和存取密碼庫 diff --git a/apps/browser/test.setup.ts b/apps/browser/test.setup.ts index eb635e646e1..08b7287984c 100644 --- a/apps/browser/test.setup.ts +++ b/apps/browser/test.setup.ts @@ -79,6 +79,7 @@ const scripting = { executeScript: jest.fn(), registerContentScripts: jest.fn(), unregisterContentScripts: jest.fn(), + ExecutionWorld: { ISOLATED: "ISOLATED", MAIN: "MAIN" }, }; const windows = { diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index e3b1b0e0341..55f474f2992 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -1012,7 +1012,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1713,7 +1713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.3", ] [[package]] @@ -1734,9 +1734,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.33.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ "cc", "pkg-config", @@ -2729,9 +2729,9 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.35.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22715a5d6deef63c637207afbe68d0c72c3f8d0022d7cf9714c442d6157606b" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ "bitflags", "fallible-iterator", diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/Cargo.toml b/apps/desktop/desktop_native/bitwarden_chromium_importer/Cargo.toml index b7289c48609..d1efbd006f0 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/Cargo.toml +++ b/apps/desktop/desktop_native/bitwarden_chromium_importer/Cargo.toml @@ -16,7 +16,7 @@ hex = { workspace = true } homedir = { workspace = true } pbkdf2 = "=0.12.2" rand = { workspace = true } -rusqlite = { version = "=0.35.0", features = ["bundled"] } +rusqlite = { version = "=0.37.0", features = ["bundled"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } sha1 = "=0.10.6" diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/crypto.rs b/apps/desktop/desktop_native/bitwarden_chromium_importer/src/crypto.rs index ce10b27b83f..e6442e21742 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/crypto.rs +++ b/apps/desktop/desktop_native/bitwarden_chromium_importer/src/crypto.rs @@ -6,16 +6,13 @@ use aes::cipher::{ block_padding::Pkcs7, generic_array::GenericArray, typenum::U32, BlockDecryptMut, KeyIvInit, }; -#[allow(clippy::question_mark)] pub fn decrypt_aes256(iv: &[u8; 16], data: &[u8], key: GenericArray) -> Result> { let iv = GenericArray::from_slice(iv); let mut data = data.to_vec(); - let result = cbc::Decryptor::::new(&key, iv) + cbc::Decryptor::::new(&key, iv) .decrypt_padded_mut::(&mut data) - .map_err(|_| anyhow!("Failed to decrypt data")); - if let Err(e) = result { - return Err(e); - } + .map_err(|_| anyhow!("Failed to decrypt data"))?; + Ok(data) } diff --git a/apps/desktop/src/app/accounts/settings.component.html b/apps/desktop/src/app/accounts/settings.component.html index 4af12903a24..18f7f67abc2 100644 --- a/apps/desktop/src/app/accounts/settings.component.html +++ b/apps/desktop/src/app/accounts/settings.component.html @@ -339,16 +339,10 @@ formControlName="enableAutotype" (change)="saveEnableAutotype()" /> - {{ "enableAutotypeTransitionKey" | i18n }} - +
+ {{ "enableAutotypeTransitionKey" | i18n }} + +
diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index 3c21b3cf4b7..0ec77419d02 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -7,6 +7,7 @@ import { RouterModule } from "@angular/router"; import { BehaviorSubject, Observable, Subject, combineLatest, firstValueFrom, of } from "rxjs"; import { concatMap, map, pairwise, startWith, switchMap, takeUntil, timeout } from "rxjs/operators"; +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { VaultTimeoutInputComponent } from "@bitwarden/auth/angular"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -39,6 +40,7 @@ import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.e import { Utils } from "@bitwarden/common/platform/misc/utils"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { UserId } from "@bitwarden/common/types/guid"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CheckboxModule, DialogService, @@ -51,7 +53,6 @@ import { SelectModule, ToastService, TypographyModule, - BadgeComponent, } from "@bitwarden/components"; import { KeyService, BiometricStateService, BiometricsStatus } from "@bitwarden/key-management"; import { PermitCipherDetailsPopoverComponent } from "@bitwarden/vault"; @@ -60,17 +61,22 @@ import { SetPinComponent } from "../../auth/components/set-pin.component"; import { SshAgentPromptType } from "../../autofill/models/ssh-agent-setting"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopAutotypeService } from "../../autofill/services/desktop-autotype.service"; -import { PremiumComponent } from "../../billing/app/accounts/premium.component"; import { DesktopBiometricsService } from "../../key-management/biometrics/desktop.biometrics.service"; import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; +import { DesktopPremiumUpgradePromptService } from "../../services/desktop-premium-upgrade-prompt.service"; import { NativeMessagingManifestService } from "../services/native-messaging-manifest.service"; @Component({ selector: "app-settings", templateUrl: "settings.component.html", standalone: true, + providers: [ + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + ], imports: [ - BadgeComponent, CheckboxModule, CommonModule, FormFieldModule, @@ -87,6 +93,7 @@ import { NativeMessagingManifestService } from "../services/native-messaging-man TypographyModule, VaultTimeoutInputComponent, PermitCipherDetailsPopoverComponent, + PremiumBadgeComponent, ], }) export class SettingsComponent implements OnInit, OnDestroy { @@ -134,8 +141,6 @@ export class SettingsComponent implements OnInit, OnDestroy { pinEnabled$: Observable = of(true); - hasPremium: boolean = false; - form = this.formBuilder.group({ // Security vaultTimeout: [null as VaultTimeout | null], @@ -421,9 +426,7 @@ export class SettingsComponent implements OnInit, OnDestroy { .hasPremiumFromAnySource$(activeAccount.id) .pipe(takeUntil(this.destroy$)) .subscribe((hasPremium) => { - this.hasPremium = hasPremium; - - if (this.hasPremium) { + if (hasPremium) { this.form.controls.enableAutotype.enable(); } }); @@ -892,10 +895,6 @@ export class SettingsComponent implements OnInit, OnDestroy { } } - async openPremiumDialog() { - await this.dialogService.open(PremiumComponent); - } - async saveEnableAutotype() { await this.desktopAutotypeService.setAutotypeEnabledState(this.form.value.enableAutotype); } diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 7cdb45a6ca7..1c2d3aa464d 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -77,6 +77,7 @@ import { KeyService, BiometricStateService } from "@bitwarden/key-management"; import { AddEditFolderDialogComponent, AddEditFolderDialogResult } from "@bitwarden/vault"; import { DeleteAccountComponent } from "../auth/delete-account.component"; +import { DesktopAutotypeDefaultSettingPolicy } from "../autofill/services/desktop-autotype-policy.service"; import { PremiumComponent } from "../billing/app/accounts/premium.component"; import { MenuAccount, MenuUpdateRequest } from "../main/menu/menu.updater"; @@ -177,6 +178,7 @@ export class AppComponent implements OnInit, OnDestroy { private readonly documentLangSetter: DocumentLangSetter, private restrictedItemTypesService: RestrictedItemTypesService, private readonly tokenService: TokenService, + private desktopAutotypeDefaultSettingPolicy: DesktopAutotypeDefaultSettingPolicy, ) { this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe(); @@ -295,7 +297,7 @@ export class AppComponent implements OnInit, OnDestroy { await this.openModal(SettingsComponent, this.settingsRef); break; case "openPremium": - await this.openModal(PremiumComponent, this.premiumRef); + this.dialogService.open(PremiumComponent); break; case "showFingerprintPhrase": { const activeUserId = await firstValueFrom( diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 094dea2e215..9f2bb1acc90 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -37,7 +37,10 @@ import { } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; -import { PolicyService as PolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { + PolicyService as PolicyServiceAbstraction, + InternalPolicyService, +} from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService, AccountService as AccountServiceAbstraction, @@ -112,6 +115,7 @@ import { DesktopLoginComponentService } from "../../auth/login/desktop-login-com import { DesktopTwoFactorAuthDuoComponentService } from "../../auth/services/desktop-two-factor-auth-duo-component.service"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopAutofillService } from "../../autofill/services/desktop-autofill.service"; +import { DesktopAutotypeDefaultSettingPolicy } from "../../autofill/services/desktop-autotype-policy.service"; import { DesktopAutotypeService } from "../../autofill/services/desktop-autotype.service"; import { DesktopFido2UserInterfaceService } from "../../autofill/services/desktop-fido2-user-interface.service"; import { DesktopBiometricsService } from "../../key-management/biometrics/desktop.biometrics.service"; @@ -464,8 +468,14 @@ const safeProviders: SafeProvider[] = [ GlobalStateProvider, PlatformUtilsServiceAbstraction, BillingAccountProfileStateService, + DesktopAutotypeDefaultSettingPolicy, ], }), + safeProvider({ + provide: DesktopAutotypeDefaultSettingPolicy, + useClass: DesktopAutotypeDefaultSettingPolicy, + deps: [AccountServiceAbstraction, AuthServiceAbstraction, InternalPolicyService, ConfigService], + }), ]; @NgModule({ diff --git a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts index 44754995f72..53a1c7dbd4c 100644 --- a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts +++ b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts @@ -127,8 +127,7 @@ describe("DesktopSetInitialPasswordService", () => { credentials.newPasswordHint, credentials.orgSsoIdentifier, keysRequest, - credentials.kdfConfig.kdfType, - credentials.kdfConfig.iterations, + credentials.kdfConfig, ); }); diff --git a/apps/desktop/src/autofill/services/desktop-autotype-policy.service.spec.ts b/apps/desktop/src/autofill/services/desktop-autotype-policy.service.spec.ts new file mode 100644 index 00000000000..7fb30333e28 --- /dev/null +++ b/apps/desktop/src/autofill/services/desktop-autotype-policy.service.spec.ts @@ -0,0 +1,166 @@ +import { TestBed } from "@angular/core/testing"; +import { mock, MockProxy } from "jest-mock-extended"; +import { BehaviorSubject, firstValueFrom, take, timeout, TimeoutError } from "rxjs"; + +import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { Account, UserId } from "@bitwarden/common/platform/models/domain/account"; + +import { DesktopAutotypeDefaultSettingPolicy } from "./desktop-autotype-policy.service"; + +describe("DesktopAutotypeDefaultSettingPolicy", () => { + let service: DesktopAutotypeDefaultSettingPolicy; + let accountService: MockProxy; + let authService: MockProxy; + let policyService: MockProxy; + let configService: MockProxy; + + let mockAccountSubject: BehaviorSubject<{ id: UserId } | null>; + let mockFeatureFlagSubject: BehaviorSubject; + let mockAuthStatusSubject: BehaviorSubject; + let mockPolicyAppliesSubject: BehaviorSubject; + + const mockUserId = "user-123" as UserId; + + beforeEach(() => { + mockAccountSubject = new BehaviorSubject({ + id: mockUserId, + email: "test@example.com", + emailVerified: true, + name: "Test User", + }); + mockFeatureFlagSubject = new BehaviorSubject(true); + mockAuthStatusSubject = new BehaviorSubject( + AuthenticationStatus.Unlocked, + ); + mockPolicyAppliesSubject = new BehaviorSubject(false); + + accountService = mock(); + authService = mock(); + policyService = mock(); + configService = mock(); + + accountService.activeAccount$ = mockAccountSubject.asObservable(); + configService.getFeatureFlag$ = jest + .fn() + .mockReturnValue(mockFeatureFlagSubject.asObservable()); + authService.authStatusFor$ = jest + .fn() + .mockImplementation((_: UserId) => mockAuthStatusSubject.asObservable()); + policyService.policyAppliesToUser$ = jest + .fn() + .mockReturnValue(mockPolicyAppliesSubject.asObservable()); + + TestBed.configureTestingModule({ + providers: [ + DesktopAutotypeDefaultSettingPolicy, + { provide: AccountService, useValue: accountService }, + { provide: AuthService, useValue: authService }, + { provide: InternalPolicyService, useValue: policyService }, + { provide: ConfigService, useValue: configService }, + ], + }); + + service = TestBed.inject(DesktopAutotypeDefaultSettingPolicy); + }); + + afterEach(() => { + jest.clearAllMocks(); + mockAccountSubject.complete(); + mockFeatureFlagSubject.complete(); + mockAuthStatusSubject.complete(); + mockPolicyAppliesSubject.complete(); + }); + + describe("autotypeDefaultSetting$", () => { + it("should emit null when feature flag is disabled", async () => { + mockFeatureFlagSubject.next(false); + const result = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(result).toBeNull(); + }); + + it("should not emit when no active account", async () => { + mockAccountSubject.next(null); + await expect( + firstValueFrom(service.autotypeDefaultSetting$.pipe(timeout({ first: 30 }))), + ).rejects.toBeInstanceOf(TimeoutError); + }); + + it("should emit null when user is not unlocked", async () => { + mockAuthStatusSubject.next(AuthenticationStatus.Locked); + const result = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(result).toBeNull(); + }); + + it("should emit null when no autotype policy exists", async () => { + mockPolicyAppliesSubject.next(false); + const policy = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(policy).toBeNull(); + }); + + it("should emit true when autotype policy is enabled", async () => { + mockPolicyAppliesSubject.next(true); + const policyStatus = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(policyStatus).toBe(true); + }); + + it("should emit false when autotype policy is disabled", async () => { + mockPolicyAppliesSubject.next(false); + const policyStatus = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(policyStatus).toBeNull(); + }); + + it("should emit null when autotype policy does not apply", async () => { + mockPolicyAppliesSubject.next(false); + const policy = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(policy).toBeNull(); + }); + + it("should react to authentication status changes", async () => { + // Expect one emission when unlocked + mockAuthStatusSubject.next(AuthenticationStatus.Unlocked); + const first = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(first).toBeNull(); + + // Expect null emission when locked + mockAuthStatusSubject.next(AuthenticationStatus.Locked); + const lockedResult = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(lockedResult).toBeNull(); + }); + + it("should react to account changes", async () => { + const newUserId = "user-456" as UserId; + + // First value for original user + const firstValue = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(firstValue).toBeNull(); + + // Change account and expect a new emission + mockAccountSubject.next({ + id: newUserId, + }); + const secondValue = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(secondValue).toBeNull(); + + // Verify the auth lookup was switched to the new user + expect(authService.authStatusFor$).toHaveBeenCalledWith(newUserId); + }); + + it("should react to policy changes", async () => { + mockPolicyAppliesSubject.next(false); + const nullValue = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(nullValue).toBeNull(); + + mockPolicyAppliesSubject.next(true); + const trueValue = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(trueValue).toBe(true); + + mockPolicyAppliesSubject.next(false); + const nullValueAgain = await firstValueFrom(service.autotypeDefaultSetting$.pipe(take(1))); + expect(nullValueAgain).toBeNull(); + }); + }); +}); diff --git a/apps/desktop/src/autofill/services/desktop-autotype-policy.service.ts b/apps/desktop/src/autofill/services/desktop-autotype-policy.service.ts new file mode 100644 index 00000000000..887a30ef6f6 --- /dev/null +++ b/apps/desktop/src/autofill/services/desktop-autotype-policy.service.ts @@ -0,0 +1,60 @@ +import { Injectable } from "@angular/core"; +import { Observable, of } from "rxjs"; +import { distinctUntilChanged, filter, map, shareReplay, switchMap } from "rxjs/operators"; + +import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; + +@Injectable({ providedIn: "root" }) +export class DesktopAutotypeDefaultSettingPolicy { + constructor( + private readonly accountService: AccountService, + private readonly authService: AuthService, + private readonly policyService: InternalPolicyService, + private readonly configService: ConfigService, + ) {} + + /** + * Emits the autotype policy enabled status when account is unlocked and WindowsDesktopAutotype is enabled. + * - true: autotype policy exists and is enabled + * - null: no autotype policy exists for the user's organization + */ + readonly autotypeDefaultSetting$: Observable = this.configService + .getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype) + .pipe( + switchMap((autotypeFeatureEnabled) => { + if (!autotypeFeatureEnabled) { + return of(null); + } + + return this.accountService.activeAccount$.pipe( + filter((account) => account != null), + getUserId, + distinctUntilChanged(), + switchMap((userId) => { + const isUnlocked$ = this.authService.authStatusFor$(userId).pipe( + map((status) => status === AuthenticationStatus.Unlocked), + distinctUntilChanged(), + ); + + const policy$ = this.policyService + .policyAppliesToUser$(PolicyType.AutotypeDefaultSetting, userId) + .pipe( + map((appliesToUser) => (appliesToUser ? true : null)), + distinctUntilChanged(), + shareReplay({ bufferSize: 1, refCount: true }), + ); + + return isUnlocked$.pipe(switchMap((unlocked) => (unlocked ? policy$ : of(null)))); + }), + ); + }), + shareReplay({ bufferSize: 1, refCount: true }), + ); +} diff --git a/apps/desktop/src/autofill/services/desktop-autotype.service.ts b/apps/desktop/src/autofill/services/desktop-autotype.service.ts index 60e87aa2aa5..b156ffd3597 100644 --- a/apps/desktop/src/autofill/services/desktop-autotype.service.ts +++ b/apps/desktop/src/autofill/services/desktop-autotype.service.ts @@ -17,7 +17,9 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { UserId } from "@bitwarden/user-core"; -export const AUTOTYPE_ENABLED = new KeyDefinition( +import { DesktopAutotypeDefaultSettingPolicy } from "./desktop-autotype-policy.service"; + +export const AUTOTYPE_ENABLED = new KeyDefinition( AUTOTYPE_SETTINGS_DISK, "autotypeEnabled", { deserializer: (b) => b }, @@ -37,6 +39,7 @@ export class DesktopAutotypeService { private globalStateProvider: GlobalStateProvider, private platformUtilsService: PlatformUtilsService, private billingAccountProfileStateService: BillingAccountProfileStateService, + private desktopAutotypePolicy: DesktopAutotypeDefaultSettingPolicy, ) { ipc.autofill.listenAutotypeRequest(async (windowTitle, callback) => { const possibleCiphers = await this.matchCiphersToWindowTitle(windowTitle); @@ -50,9 +53,32 @@ export class DesktopAutotypeService { } async init() { - this.autotypeEnabledUserSetting$ = this.autotypeEnabledState.state$; - + // Currently Autotype is only supported for Windows if (this.platformUtilsService.getDevice() === DeviceType.WindowsDesktop) { + // If `autotypeDefaultPolicy` is `true` for a user's organization, and the + // user has never changed their local autotype setting (`autotypeEnabledState`), + // we set their local setting to `true` (once the local user setting is changed + // by this policy or the user themselves, the default policy should + // never change the user setting again). + combineLatest([ + this.autotypeEnabledState.state$, + this.desktopAutotypePolicy.autotypeDefaultSetting$, + ]) + .pipe( + map(async ([autotypeEnabledState, autotypeDefaultPolicy]) => { + if (autotypeDefaultPolicy === true && autotypeEnabledState === null) { + await this.setAutotypeEnabledState(true); + } + }), + ) + .subscribe(); + + // autotypeEnabledUserSetting$ publicly represents the value the + // user has set for autotyeEnabled in their local settings. + this.autotypeEnabledUserSetting$ = this.autotypeEnabledState.state$; + + // resolvedAutotypeEnabled$ represents the final determination if the Autotype + // feature should be on or off. this.resolvedAutotypeEnabled$ = combineLatest([ this.autotypeEnabledState.state$, this.configService.getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype), @@ -76,6 +102,8 @@ export class DesktopAutotypeService { ), ); + // When the resolvedAutotypeEnabled$ value changes, this might require + // hotkey registration / deregistration in the main process. this.resolvedAutotypeEnabled$.subscribe((enabled) => { ipc.autofill.configureAutotype(enabled); }); diff --git a/apps/desktop/src/billing/app/accounts/premium.component.html b/apps/desktop/src/billing/app/accounts/premium.component.html index c8179c6e486..d88602bed1e 100644 --- a/apps/desktop/src/billing/app/accounts/premium.component.html +++ b/apps/desktop/src/billing/app/accounts/premium.component.html @@ -61,7 +61,7 @@ > {{ "premiumPurchase" | i18n }} - +
diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index 141f05a28aa..5a6683ed904 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -13,6 +13,7 @@ import { firstValueFrom, Subject, takeUntil, switchMap, lastValueFrom, Observabl import { filter, map, take } from "rxjs/operators"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; @@ -101,6 +102,7 @@ const BroadcasterSubscriptionId = "VaultComponent"; I18nPipe, ItemModule, ButtonModule, + PremiumBadgeComponent, NavComponent, VaultFilterModule, VaultItemsV2Component, @@ -455,7 +457,6 @@ export class VaultV2Component async openAttachmentsDialog() { if (!this.userHasPremiumAccess) { - await this.premiumUpgradePromptService.promptForPremium(); return; } const dialogRef = AttachmentsV2Component.open(this.dialogService, { @@ -901,9 +902,7 @@ export class VaultV2Component title: undefined, message: this.i18nService.t("valueCopied", this.i18nService.t(labelI18nKey)), }); - if (this.action === "view") { - this.messagingService.send("minimizeOnCopy"); - } + this.messagingService.send("minimizeOnCopy"); })().catch(() => {}); }); } diff --git a/apps/web/src/app/admin-console/common/base-members.component.ts b/apps/web/src/app/admin-console/common/base-members.component.ts index 6fc0a70a8c8..21c52949254 100644 --- a/apps/web/src/app/admin-console/common/base-members.component.ts +++ b/apps/web/src/app/admin-console/common/base-members.component.ts @@ -208,7 +208,7 @@ export abstract class BaseMembersComponent { const dialogRef = UserConfirmComponent.open(this.dialogService, { data: { name: this.userNamePipe.transform(user), - userId: user.id, + userId: user.userId, publicKey: publicKey, confirmUser: () => confirmUser(publicKey), }, diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts index edd4ea3f9dc..64aa6936468 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts @@ -1132,7 +1132,7 @@ export class vNextVaultComponent implements OnInit, OnDestroy { const selectedCollection = await firstValueFrom(this.selectedCollection$); if (selectedCollection?.node.id === collection.id) { void this.router.navigate([], { - queryParams: { collectionId: selectedCollection.parent.node.id ?? null }, + queryParams: { collectionId: selectedCollection?.parent?.node.id ?? null }, queryParamsHandling: "merge", replaceUrl: true, }); diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.html new file mode 100644 index 00000000000..f110e7d34cd --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.html @@ -0,0 +1,4 @@ + + + {{ "turnOn" | i18n }} + diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts new file mode 100644 index 00000000000..ce62a7ff5a3 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts @@ -0,0 +1,25 @@ +import { Component } from "@angular/core"; + +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; + +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; + +export class DesktopAutotypeDefaultSettingPolicy extends BasePolicyEditDefinition { + name = "desktopAutotypePolicy"; + description = "desktopAutotypePolicyDesc"; + type = PolicyType.AutotypeDefaultSetting; + component = DesktopAutotypeDefaultSettingPolicyComponent; + + display$(organization: Organization, configService: ConfigService) { + return configService.getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype); + } +} +@Component({ + templateUrl: "autotype-policy.component.html", + imports: [SharedModule], +}) +export class DesktopAutotypeDefaultSettingPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts index 13f29ab68f7..bb2c40b7a76 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts @@ -1,4 +1,5 @@ export { DisableSendPolicy } from "./disable-send.component"; +export { DesktopAutotypeDefaultSettingPolicy } from "./autotype-policy.component"; export { MasterPasswordPolicy } from "./master-password.component"; export { OrganizationDataOwnershipPolicy } from "./organization-data-ownership.component"; export { PasswordGeneratorPolicy } from "./password-generator.component"; diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-register.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-register.ts index 3a4ba9a710f..5e63ba1358a 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-register.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-register.ts @@ -1,5 +1,6 @@ import { BasePolicyEditDefinition } from "./base-policy-edit.component"; import { + DesktopAutotypeDefaultSettingPolicy, DisableSendPolicy, MasterPasswordPolicy, OrganizationDataOwnershipPolicy, @@ -31,4 +32,5 @@ export const ossPolicyEditRegister: BasePolicyEditDefinition[] = [ new DisableSendPolicy(), new SendOptionsPolicy(), new RestrictedItemTypesPolicy(), + new DesktopAutotypeDefaultSettingPolicy(), ]; diff --git a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts index 2993a3774d3..70f7686a2cd 100644 --- a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts +++ b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts @@ -131,8 +131,7 @@ describe("WebSetInitialPasswordService", () => { credentials.newPasswordHint, credentials.orgSsoIdentifier, keysRequest, - credentials.kdfConfig.kdfType, - credentials.kdfConfig.iterations, + credentials.kdfConfig, ); }); diff --git a/apps/web/src/app/auth/settings/security/device-management-old.component.html b/apps/web/src/app/auth/settings/security/device-management-old.component.html deleted file mode 100644 index da01d0fe8f4..00000000000 --- a/apps/web/src/app/auth/settings/security/device-management-old.component.html +++ /dev/null @@ -1,104 +0,0 @@ - -
-
-

{{ "devices" | i18n }}

- - -

{{ "aDeviceIs" | i18n }}

-
- -
-
- -

{{ "deviceListDescriptionTemp" | i18n }}

- -
- -
- - - - - {{ col.title }} - - - - - - -
- -
-
- - - {{ row.displayName }} - - - - {{ "needsApproval" | i18n }} - - - - {{ row.displayName }} - - {{ "trusted" | i18n }} - - -
- - - {{ - "currentSession" | i18n - }} - {{ - "requestPending" | i18n - }} - - {{ row.firstLogin | date: "medium" }} - -
-
-
diff --git a/apps/web/src/app/auth/settings/security/device-management-old.component.spec.ts b/apps/web/src/app/auth/settings/security/device-management-old.component.spec.ts deleted file mode 100644 index 64fb9003ccf..00000000000 --- a/apps/web/src/app/auth/settings/security/device-management-old.component.spec.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { RouterTestingModule } from "@angular/router/testing"; -import { of, Subject } from "rxjs"; - -import { AuthRequestApiServiceAbstraction } from "@bitwarden/auth/common"; -import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; -import { DeviceView } from "@bitwarden/common/auth/abstractions/devices/views/device.view"; -import { DeviceType } from "@bitwarden/common/enums"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { MessageListener } from "@bitwarden/common/platform/messaging"; -import { - DialogService, - ToastService, - TableModule, - PopoverModule, - LayoutComponent, -} from "@bitwarden/components"; - -import { SharedModule } from "../../../shared"; -import { VaultBannersService } from "../../../vault/individual-vault/vault-banners/services/vault-banners.service"; - -import { DeviceManagementOldComponent } from "./device-management-old.component"; - -class MockResizeObserver { - observe = jest.fn(); - unobserve = jest.fn(); - disconnect = jest.fn(); -} - -global.ResizeObserver = MockResizeObserver; - -interface Message { - command: string; - notificationId?: string; -} - -describe("DeviceManagementOldComponent", () => { - let fixture: ComponentFixture; - let messageSubject: Subject; - let mockDevices: DeviceView[]; - let vaultBannersService: VaultBannersService; - - const mockDeviceResponse = { - id: "test-id", - requestDeviceType: "test-type", - requestDeviceTypeValue: DeviceType.Android, - requestDeviceIdentifier: "test-identifier", - requestIpAddress: "127.0.0.1", - creationDate: new Date().toISOString(), - responseDate: null, - key: "test-key", - masterPasswordHash: null, - publicKey: "test-public-key", - requestApproved: false, - origin: "test-origin", - }; - - beforeEach(async () => { - messageSubject = new Subject(); - mockDevices = []; - - await TestBed.configureTestingModule({ - imports: [ - RouterTestingModule, - SharedModule, - TableModule, - PopoverModule, - DeviceManagementOldComponent, - ], - providers: [ - { - provide: DevicesServiceAbstraction, - useValue: { - getDevices$: jest.fn().mockReturnValue(mockDevices), - getCurrentDevice$: jest.fn().mockReturnValue(of(null)), - getDeviceByIdentifier$: jest.fn().mockReturnValue(of(null)), - updateTrustedDeviceKeys: jest.fn(), - }, - }, - { - provide: AuthRequestApiServiceAbstraction, - useValue: { - getAuthRequest: jest.fn().mockResolvedValue(mockDeviceResponse), - }, - }, - { - provide: MessageListener, - useValue: { - allMessages$: messageSubject.asObservable(), - }, - }, - { - provide: DialogService, - useValue: { - openSimpleDialog: jest.fn(), - }, - }, - { - provide: ToastService, - useValue: { - success: jest.fn(), - error: jest.fn(), - }, - }, - { - provide: VaultBannersService, - useValue: { - shouldShowPendingAuthRequestBanner: jest.fn(), - }, - }, - { - provide: I18nService, - useValue: { - t: jest.fn((key: string) => key), - }, - }, - { - provide: ValidationService, - useValue: { - showError: jest.fn(), - }, - }, - { - provide: LayoutComponent, - useValue: { - mainContent: jest.fn(), - }, - }, - ], - }).compileComponents(); - - fixture = TestBed.createComponent(DeviceManagementOldComponent); - - vaultBannersService = TestBed.inject(VaultBannersService); - }); - - describe("message listener", () => { - beforeEach(() => { - jest.spyOn(vaultBannersService, "shouldShowPendingAuthRequestBanner").mockResolvedValue(true); - }); - - it("ignores other message types", async () => { - const initialDataLength = (fixture.componentInstance as any).dataSource.data.length; - const message: Message = { command: "other", notificationId: "test-id" }; - messageSubject.next(message); - await fixture.whenStable(); - - expect((fixture.componentInstance as any).dataSource.data.length).toBe(initialDataLength); - }); - - it("adds device to table when auth request message received", async () => { - const initialDataLength = (fixture.componentInstance as any).dataSource.data.length; - const message: Message = { - command: "openLoginApproval", - notificationId: "test-id", - }; - - messageSubject.next(message); - fixture.detectChanges(); - await fixture.whenStable(); - - const dataSource = (fixture.componentInstance as any).dataSource; - expect(dataSource.data.length).toBe(initialDataLength + 1); - - const addedDevice = dataSource.data[0]; - expect(addedDevice).toEqual({ - id: "", - type: mockDeviceResponse.requestDeviceTypeValue, - displayName: expect.any(String), - loginStatus: "requestPending", - firstLogin: expect.any(Date), - trusted: false, - devicePendingAuthRequest: { - id: mockDeviceResponse.id, - creationDate: mockDeviceResponse.creationDate, - }, - hasPendingAuthRequest: true, - identifier: mockDeviceResponse.requestDeviceIdentifier, - }); - }); - - it("stops listening when component is destroyed", async () => { - fixture.destroy(); - const message: Message = { - command: "openLoginApproval", - notificationId: "test-id", - }; - messageSubject.next(message); - expect((fixture.componentInstance as any).dataSource.data.length).toBe(0); - }); - }); -}); diff --git a/apps/web/src/app/auth/settings/security/device-management-old.component.ts b/apps/web/src/app/auth/settings/security/device-management-old.component.ts deleted file mode 100644 index 816da6e873f..00000000000 --- a/apps/web/src/app/auth/settings/security/device-management-old.component.ts +++ /dev/null @@ -1,373 +0,0 @@ -import { CommonModule } from "@angular/common"; -import { Component, DestroyRef } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; -import { firstValueFrom } from "rxjs"; - -import { LoginApprovalDialogComponent } from "@bitwarden/angular/auth/login-approval"; -import { AuthRequestApiServiceAbstraction } from "@bitwarden/auth/common"; -import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; -import { - DevicePendingAuthRequest, - DeviceResponse, -} from "@bitwarden/common/auth/abstractions/devices/responses/device.response"; -import { DeviceView } from "@bitwarden/common/auth/abstractions/devices/views/device.view"; -import { DeviceType, DeviceTypeMetadata } from "@bitwarden/common/enums"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { MessageListener } from "@bitwarden/common/platform/messaging"; -import { - DialogService, - ToastService, - TableDataSource, - TableModule, - PopoverModule, -} from "@bitwarden/components"; - -import { SharedModule } from "../../../shared"; - -/** - * Interface representing a row in the device management table - */ -interface DeviceTableData { - id: string; - type: DeviceType; - displayName: string; - loginStatus: string; - firstLogin: Date; - trusted: boolean; - devicePendingAuthRequest: DevicePendingAuthRequest | null; - hasPendingAuthRequest: boolean; - identifier: string; -} - -/** - * Provides a table of devices and allows the user to log out, approve or remove a device - */ -@Component({ - selector: "app-device-management", - templateUrl: "./device-management-old.component.html", - imports: [CommonModule, SharedModule, TableModule, PopoverModule], -}) -export class DeviceManagementOldComponent { - protected dataSource = new TableDataSource(); - protected currentDevice: DeviceView | undefined; - protected loading = true; - protected asyncActionLoading = false; - - constructor( - private i18nService: I18nService, - private devicesService: DevicesServiceAbstraction, - private dialogService: DialogService, - private toastService: ToastService, - private validationService: ValidationService, - private messageListener: MessageListener, - private authRequestApiService: AuthRequestApiServiceAbstraction, - private destroyRef: DestroyRef, - ) { - void this.initializeDevices(); - } - - /** - * Initialize the devices list and set up the message listener - */ - private async initializeDevices(): Promise { - try { - await this.loadDevices(); - - this.messageListener.allMessages$ - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((message) => { - if (message.command !== "openLoginApproval") { - return; - } - // Handle inserting a new device when an auth request is received - this.upsertDeviceWithPendingAuthRequest( - message as { command: string; notificationId: string }, - ).catch((error) => this.validationService.showError(error)); - }); - } catch (error) { - this.validationService.showError(error); - } - } - - /** - * Handle inserting a new device when an auth request is received - * @param message - The auth request message - */ - private async upsertDeviceWithPendingAuthRequest(message: { - command: string; - notificationId: string; - }): Promise { - const requestId = message.notificationId; - if (!requestId) { - return; - } - - const authRequestResponse = await this.authRequestApiService.getAuthRequest(requestId); - if (!authRequestResponse) { - return; - } - - // Add new device to the table - const upsertDevice: DeviceTableData = { - id: "", - type: authRequestResponse.requestDeviceTypeValue, - displayName: this.getHumanReadableDeviceType(authRequestResponse.requestDeviceTypeValue), - loginStatus: this.i18nService.t("requestPending"), - firstLogin: new Date(authRequestResponse.creationDate), - trusted: false, - devicePendingAuthRequest: { - id: authRequestResponse.id, - creationDate: authRequestResponse.creationDate, - }, - hasPendingAuthRequest: true, - identifier: authRequestResponse.requestDeviceIdentifier, - }; - - // If the device already exists in the DB, update the device id and first login date - if (authRequestResponse.requestDeviceIdentifier) { - const existingDevice = await firstValueFrom( - this.devicesService.getDeviceByIdentifier$(authRequestResponse.requestDeviceIdentifier), - ); - - if (existingDevice?.id && existingDevice.creationDate) { - upsertDevice.id = existingDevice.id; - upsertDevice.firstLogin = new Date(existingDevice.creationDate); - } - } - - const existingDeviceIndex = this.dataSource.data.findIndex( - (device) => device.identifier === upsertDevice.identifier, - ); - - if (existingDeviceIndex >= 0) { - // Update existing device - this.dataSource.data[existingDeviceIndex] = upsertDevice; - this.dataSource.data = [...this.dataSource.data]; - } else { - // Add new device - this.dataSource.data = [upsertDevice, ...this.dataSource.data]; - } - } - - /** - * Load current device and all devices - */ - private async loadDevices(): Promise { - try { - const currentDevice = await firstValueFrom(this.devicesService.getCurrentDevice$()); - const devices = await firstValueFrom(this.devicesService.getDevices$()); - - if (!currentDevice || !devices) { - this.loading = false; - return; - } - - this.currentDevice = new DeviceView(currentDevice); - this.updateDeviceTable(devices); - } catch (error) { - this.validationService.showError(error); - } finally { - this.loading = false; - } - } - - /** - * Updates the device table with the latest device data - * @param devices - Array of device views to display in the table - */ - private updateDeviceTable(devices: Array): void { - this.dataSource.data = devices - .map((device: DeviceView): DeviceTableData | null => { - if (!device.id) { - this.validationService.showError(new Error(this.i18nService.t("deviceIdMissing"))); - return null; - } - - if (device.type == undefined) { - this.validationService.showError(new Error(this.i18nService.t("deviceTypeMissing"))); - return null; - } - - if (!device.creationDate) { - this.validationService.showError( - new Error(this.i18nService.t("deviceCreationDateMissing")), - ); - return null; - } - - const hasPendingRequest = device.response - ? this.hasPendingAuthRequest(device.response) - : false; - return { - id: device.id, - type: device.type, - displayName: this.getHumanReadableDeviceType(device.type), - loginStatus: this.getLoginStatus(device), - firstLogin: new Date(device.creationDate), - trusted: device.response?.isTrusted ?? false, - devicePendingAuthRequest: device.response?.devicePendingAuthRequest ?? null, - hasPendingAuthRequest: hasPendingRequest, - identifier: device.identifier ?? "", - }; - }) - .filter((device): device is DeviceTableData => device !== null); - } - - /** - * Column configuration for the table - */ - protected readonly columnConfig = [ - { - name: "displayName", - title: this.i18nService.t("device"), - headerClass: "tw-w-1/3", - sortable: true, - }, - { - name: "loginStatus", - title: this.i18nService.t("loginStatus"), - headerClass: "tw-w-1/3", - sortable: true, - }, - { - name: "firstLogin", - title: this.i18nService.t("firstLogin"), - headerClass: "tw-w-1/3", - sortable: true, - }, - ]; - - /** - * Get the icon for a device type - * @param type - The device type - * @returns The icon for the device type - */ - getDeviceIcon(type: DeviceType): string { - const defaultIcon = "bwi bwi-desktop"; - const categoryIconMap: Record = { - webVault: "bwi bwi-browser", - desktop: "bwi bwi-desktop", - mobile: "bwi bwi-mobile", - cli: "bwi bwi-cli", - extension: "bwi bwi-puzzle", - sdk: "bwi bwi-desktop", - }; - - const metadata = DeviceTypeMetadata[type]; - return metadata ? (categoryIconMap[metadata.category] ?? defaultIcon) : defaultIcon; - } - - /** - * Get the login status of a device - * It will return the current session if the device is the current device - * It will return the date of the pending auth request when available - * @param device - The device - * @returns The login status - */ - private getLoginStatus(device: DeviceView): string { - if (this.isCurrentDevice(device)) { - return this.i18nService.t("currentSession"); - } - - if (device?.response?.devicePendingAuthRequest?.creationDate) { - return this.i18nService.t("requestPending"); - } - - return ""; - } - - /** - * Get a human readable device type from the DeviceType enum - * @param type - The device type - * @returns The human readable device type - */ - private getHumanReadableDeviceType(type: DeviceType): string { - const metadata = DeviceTypeMetadata[type]; - if (!metadata) { - return this.i18nService.t("unknownDevice"); - } - - // If the platform is "Unknown" translate it since it is not a proper noun - const platform = - metadata.platform === "Unknown" ? this.i18nService.t("unknown") : metadata.platform; - const category = this.i18nService.t(metadata.category); - return platform ? `${category} - ${platform}` : category; - } - - /** - * Check if a device is the current device - * @param device - The device or device table data - * @returns True if the device is the current device, false otherwise - */ - protected isCurrentDevice(device: DeviceView | DeviceTableData): boolean { - return "response" in device - ? device.id === this.currentDevice?.id - : device.id === this.currentDevice?.id; - } - - /** - * Check if a device has a pending auth request - * @param device - The device response - * @returns True if the device has a pending auth request, false otherwise - */ - private hasPendingAuthRequest(device: DeviceResponse): boolean { - return ( - device.devicePendingAuthRequest !== undefined && device.devicePendingAuthRequest !== null - ); - } - - /** - * Open a dialog to approve or deny a pending auth request for a device - */ - async managePendingAuthRequest(device: DeviceTableData) { - if (device.devicePendingAuthRequest === undefined || device.devicePendingAuthRequest === null) { - return; - } - - const dialogRef = LoginApprovalDialogComponent.open(this.dialogService, { - notificationId: device.devicePendingAuthRequest.id, - }); - - const result = await firstValueFrom(dialogRef.closed); - - if (result !== undefined && typeof result === "boolean") { - // auth request approved or denied so reset - device.devicePendingAuthRequest = null; - device.hasPendingAuthRequest = false; - } - } - - /** - * Remove a device - * @param device - The device - */ - protected async removeDevice(device: DeviceTableData) { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "removeDevice" }, - content: { key: "removeDeviceConfirmation" }, - type: "warning", - }); - - if (!confirmed) { - return; - } - - try { - this.asyncActionLoading = true; - await firstValueFrom(this.devicesService.deactivateDevice$(device.id)); - this.asyncActionLoading = false; - - // Remove the device from the data source - this.dataSource.data = this.dataSource.data.filter((d) => d.id !== device.id); - - this.toastService.showToast({ - title: "", - message: this.i18nService.t("deviceRemoved"), - variant: "success", - }); - } catch (error) { - this.validationService.showError(error); - } - } -} diff --git a/apps/web/src/app/auth/settings/security/security-routing.module.ts b/apps/web/src/app/auth/settings/security/security-routing.module.ts index f7586329380..ba476dc9106 100644 --- a/apps/web/src/app/auth/settings/security/security-routing.module.ts +++ b/apps/web/src/app/auth/settings/security/security-routing.module.ts @@ -2,12 +2,9 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; import { DeviceManagementComponent } from "@bitwarden/angular/auth/device-management/device-management.component"; -import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { TwoFactorSetupComponent } from "../two-factor/two-factor-setup.component"; -import { DeviceManagementOldComponent } from "./device-management-old.component"; import { PasswordSettingsComponent } from "./password-settings/password-settings.component"; import { SecurityKeysComponent } from "./security-keys.component"; import { SecurityComponent } from "./security.component"; @@ -34,15 +31,11 @@ const routes: Routes = [ component: SecurityKeysComponent, data: { titleId: "keys" }, }, - ...featureFlaggedRoute({ - defaultComponent: DeviceManagementOldComponent, - flaggedComponent: DeviceManagementComponent, - featureFlag: FeatureFlag.PM14938_BrowserExtensionLoginApproval, - routeOptions: { - path: "device-management", - data: { titleId: "devices" }, - }, - }), + { + path: "device-management", + component: DeviceManagementComponent, + data: { titleId: "devices" }, + }, ], }, ]; diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 06c31a0bfd4..29b84ddc382 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -98,6 +98,7 @@ import { DefaultThemeStateService, ThemeStateService, } from "@bitwarden/common/platform/theming/theme-state.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { DialogService, ToastService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { @@ -109,6 +110,7 @@ import { LockComponentService } from "@bitwarden/key-management-ui"; import { SerializedMemoryStorageService } from "@bitwarden/storage-core"; import { DefaultSshImportPromptService, SshImportPromptService } from "@bitwarden/vault"; import { WebOrganizationInviteService } from "@bitwarden/web-vault/app/auth/core/services/organization-invite/web-organization-invite.service"; +import { WebVaultPremiumUpgradePromptService } from "@bitwarden/web-vault/app/vault/services/web-premium-upgrade-prompt.service"; import { flagEnabled } from "../../utils/flags"; import { @@ -403,6 +405,11 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultDeviceManagementComponentService, deps: [], }), + safeProvider({ + provide: PremiumUpgradePromptService, + useClass: WebVaultPremiumUpgradePromptService, + deps: [DialogService, Router], + }), ]; @NgModule({ diff --git a/apps/web/src/app/key-management/change-kdf/change-kdf-confirmation.component.ts b/apps/web/src/app/key-management/change-kdf/change-kdf-confirmation.component.ts index 5aa8eeb907c..3e09f710062 100644 --- a/apps/web/src/app/key-management/change-kdf/change-kdf-confirmation.component.ts +++ b/apps/web/src/app/key-management/change-kdf/change-kdf-confirmation.component.ts @@ -4,13 +4,13 @@ import { Component, Inject } from "@angular/core"; import { FormGroup, FormControl, Validators } from "@angular/forms"; import { firstValueFrom } from "rxjs"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { KdfRequest } from "@bitwarden/common/models/request/kdf.request"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { ChangeKdfService } from "@bitwarden/common/key-management/kdf/change-kdf-service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { DIALOG_DATA, ToastService } from "@bitwarden/components"; -import { KdfConfig, KdfType, KeyService } from "@bitwarden/key-management"; +import { KdfConfig, KdfType } from "@bitwarden/key-management"; @Component({ selector: "app-change-kdf-confirmation", @@ -28,13 +28,12 @@ export class ChangeKdfConfirmationComponent { loading = false; constructor( - private apiService: ApiService, private i18nService: I18nService, - private keyService: KeyService, private messagingService: MessagingService, @Inject(DIALOG_DATA) params: { kdf: KdfType; kdfConfig: KdfConfig }, private accountService: AccountService, private toastService: ToastService, + private changeKdfService: ChangeKdfService, ) { this.kdfConfig = params.kdfConfig; this.masterPassword = null; @@ -56,37 +55,17 @@ export class ChangeKdfConfirmationComponent { }; private async makeKeyAndSaveAsync() { - const activeAccount = await firstValueFrom(this.accountService.activeAccount$); - if (activeAccount == null) { - throw new Error("No active account found."); - } const masterPassword = this.form.value.masterPassword; // Ensure the KDF config is valid. this.kdfConfig.validateKdfConfigForSetting(); - const request = new KdfRequest(); - request.kdf = this.kdfConfig.kdfType; - request.kdfIterations = this.kdfConfig.iterations; - if (this.kdfConfig.kdfType === KdfType.Argon2id) { - request.kdfMemory = this.kdfConfig.memory; - request.kdfParallelism = this.kdfConfig.parallelism; - } - const masterKey = await this.keyService.getOrDeriveMasterKey(masterPassword, activeAccount.id); - request.masterPasswordHash = await this.keyService.hashMasterKey(masterPassword, masterKey); + const activeAccountId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); - const newMasterKey = await this.keyService.makeMasterKey( + await this.changeKdfService.updateUserKdfParams( masterPassword, - activeAccount.email, this.kdfConfig, + activeAccountId, ); - request.newMasterPasswordHash = await this.keyService.hashMasterKey( - masterPassword, - newMasterKey, - ); - const newUserKey = await this.keyService.encryptUserKeyWithMasterKey(newMasterKey); - request.key = newUserKey[1].encryptedString; - - await this.apiService.postAccountKdf(request); } } diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts index 9a67506c400..f7f611b75ee 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts @@ -1,5 +1,5 @@ import { Injectable } from "@angular/core"; -import { firstValueFrom, Observable } from "rxjs"; +import { firstValueFrom } from "rxjs"; import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -7,6 +7,7 @@ import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/a import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; +import { firstValueFromOrThrow } from "@bitwarden/common/key-management/utils"; import { VaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -103,18 +104,18 @@ export class UserKeyRotationService { } // Read current cryptographic state / settings - const masterKeyKdfConfig: KdfConfig = (await this.firstValueFromOrThrow( + const masterKeyKdfConfig: KdfConfig = (await firstValueFromOrThrow( this.kdfConfigService.getKdfConfig$(user.id), "KDF config", ))!; // The masterkey salt used for deriving the masterkey always needs to be trimmed and lowercased. const masterKeySalt = user.email.trim().toLowerCase(); - const currentUserKey: UserKey = (await this.firstValueFromOrThrow( + const currentUserKey: UserKey = (await firstValueFromOrThrow( this.keyService.userKey$(user.id), "User key", ))!; const currentUserKeyWrappedPrivateKey = new EncString( - (await this.firstValueFromOrThrow( + (await firstValueFromOrThrow( this.keyService.userEncryptedPrivateKey$(user.id), "User encrypted private key", ))!, @@ -515,12 +516,4 @@ export class UserKeyRotationService { HashPurpose.ServerAuthorization, ); } - - async firstValueFromOrThrow(value: Observable, name: string): Promise { - const result = await firstValueFrom(value); - if (result == null) { - throw new Error(`Failed to get ${name}`); - } - return result; - } } diff --git a/apps/web/src/app/layouts/product-switcher/product-switcher.stories.ts b/apps/web/src/app/layouts/product-switcher/product-switcher.stories.ts index 1a44df4dd00..18cb8e26c70 100644 --- a/apps/web/src/app/layouts/product-switcher/product-switcher.stories.ts +++ b/apps/web/src/app/layouts/product-switcher/product-switcher.stories.ts @@ -124,6 +124,7 @@ export default { switchProducts: "Switch Products", secureYourInfrastructure: "Secure your infrastructure", protectYourFamilyOrBusiness: "Protect your family or business", + loading: "Loading", }); }, }, diff --git a/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.html b/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.html index cd091e11940..e93b788c528 100644 --- a/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.html +++ b/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.html @@ -6,8 +6,8 @@ [attr.aria-hidden]="!allVideosLoaded" class="tw-block tw-max-w-full tw-shadow-md tw-rounded-lg" (loadeddata)="onVideoLoad()" - src="https://assets.bitwarden.com/extension-animations/new-login-item.mp4" - appDarkImgSrc="https://assets.bitwarden.com/extension-animations/new-login-item-dark.mp4" + src="/videos/new-login-item.mp4" + appDarkImgSrc="/videos/new-login-item-dark.mp4" aria-hidden > @@ -20,8 +20,8 @@ [attr.aria-hidden]="!allVideosLoaded" class="tw-block tw-max-w-full tw-shadow-md tw-rounded-lg" (loadeddata)="onVideoLoad()" - src="https://assets.bitwarden.com/extension-animations/browser-extension-easy-access.mp4" - appDarkImgSrc="https://assets.bitwarden.com/extension-animations/browser-extension-easy-access-dark.mp4" + src="/videos/browser-extension-easy-access.mp4" + appDarkImgSrc="/videos/browser-extension-easy-access-dark.mp4" aria-hidden > @@ -34,8 +34,8 @@ [attr.aria-hidden]="!allVideosLoaded" class="tw-block tw-max-w-full tw-shadow-md tw-rounded-lg" (loadeddata)="onVideoLoad()" - src="https://assets.bitwarden.com/extension-animations/onboarding-autofill.mp4" - appDarkImgSrc="https://assets.bitwarden.com/extension-animations/onboarding-autofill-dark.mp4" + src="/videos/onboarding-autofill.mp4" + appDarkImgSrc="/videos/onboarding-autofill-dark.mp4" aria-hidden > diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.spec.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.spec.ts new file mode 100644 index 00000000000..e45a82d82ba --- /dev/null +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.spec.ts @@ -0,0 +1,132 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { provideNoopAnimations } from "@angular/platform-browser/animations"; +import { ActivatedRoute, Router } from "@angular/router"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; +import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { DialogRef, DIALOG_DATA, DialogService, ToastService } from "@bitwarden/components"; + +import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; + +import { VaultItemDialogComponent } from "./vault-item-dialog.component"; + +// Create a test subclass to more easily access protected members +class TestVaultItemDialogComponent extends VaultItemDialogComponent { + getTestTitle() { + this["updateTitle"](); + return this.title; + } + setTestParams(params: any) { + this.params = params; + } + setTestCipher(cipher: any) { + this.cipher = cipher; + } + setTestFormConfig(formConfig: any) { + this.formConfig = formConfig; + } +} + +describe("VaultItemDialogComponent", () => { + let fixture: ComponentFixture; + let component: TestVaultItemDialogComponent; + + const baseFormConfig = { + mode: "edit", + cipherType: CipherType.Login, + collections: [], + organizations: [], + admin: false, + organizationDataOwnershipDisabled: false, + folders: [], + }; + + const baseParams = { + mode: "view", + formConfig: { ...baseFormConfig }, + disableForm: false, + activeCollectionId: undefined, + isAdminConsoleAction: false, + restore: undefined, + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TestVaultItemDialogComponent], + providers: [ + provideNoopAnimations(), + { provide: I18nService, useValue: { t: (key: string) => key } }, + { provide: DIALOG_DATA, useValue: { ...baseParams } }, + { provide: DialogRef, useValue: {} }, + { provide: DialogService, useValue: {} }, + { provide: ToastService, useValue: {} }, + { provide: MessagingService, useValue: {} }, + { provide: LogService, useValue: {} }, + { provide: CipherService, useValue: {} }, + { provide: AccountService, useValue: { activeAccount$: { pipe: () => ({}) } } }, + { provide: Router, useValue: {} }, + { provide: ActivatedRoute, useValue: {} }, + { + provide: BillingAccountProfileStateService, + useValue: { hasPremiumFromAnySource$: () => ({}) }, + }, + { provide: PremiumUpgradePromptService, useValue: {} }, + { provide: CipherAuthorizationService, useValue: {} }, + { provide: ApiService, useValue: {} }, + { provide: EventCollectionService, useValue: {} }, + { provide: RoutedVaultFilterService, useValue: {} }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(TestVaultItemDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + describe("dialog title", () => { + it("sets title for view mode and Login type", () => { + component.setTestCipher({ type: CipherType.Login }); + component.setTestParams({ mode: "view" }); + component.setTestFormConfig({ ...baseFormConfig, cipherType: CipherType.Login }); + expect(component.getTestTitle()).toBe("viewItemHeaderLogin"); + }); + + it("sets title for form mode (edit) and Card type", () => { + component.setTestCipher(undefined); + component.setTestParams({ mode: "form" }); + component.setTestFormConfig({ ...baseFormConfig, mode: "edit", cipherType: CipherType.Card }); + expect(component.getTestTitle()).toBe("editItemHeaderCard"); + }); + + it("sets title for form mode (add) and Identity type", () => { + component.setTestCipher(undefined); + component.setTestParams({ mode: "form" }); + component.setTestFormConfig({ + ...baseFormConfig, + mode: "add", + cipherType: CipherType.Identity, + }); + expect(component.getTestTitle()).toBe("newItemHeaderIdentity"); + }); + + it("sets title for form mode (clone) and Card type", () => { + component.setTestCipher(undefined); + component.setTestParams({ mode: "form" }); + component.setTestFormConfig({ + ...baseFormConfig, + mode: "clone", + cipherType: CipherType.Card, + }); + expect(component.getTestTitle()).toBe("newItemHeaderCard"); + }); + }); +}); diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 88e2a40ec6a..b48db2bba91 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -1,7 +1,15 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule } from "@angular/common"; -import { Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { + Component, + ElementRef, + forwardRef, + Inject, + OnDestroy, + OnInit, + ViewChild, +} from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { Router } from "@angular/router"; import { firstValueFrom, Subject, switchMap } from "rxjs"; @@ -56,10 +64,10 @@ import { } from "@bitwarden/vault"; import { SharedModule } from "../../../shared/shared.module"; -import { WebVaultPremiumUpgradePromptService } from "../../../vault/services/web-premium-upgrade-prompt.service"; import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; import { RoutedVaultFilterModel } from "../../individual-vault/vault-filter/shared/models/routed-vault-filter.model"; import { WebCipherFormGenerationService } from "../../services/web-cipher-form-generation.service"; +import { WebVaultPremiumUpgradePromptService } from "../../services/web-premium-upgrade-prompt.service"; export type VaultItemDialogMode = "view" | "form"; @@ -136,7 +144,10 @@ export type VaultItemDialogResult = UnionOfValues; PremiumBadgeComponent, ], providers: [ - { provide: PremiumUpgradePromptService, useClass: WebVaultPremiumUpgradePromptService }, + { + provide: PremiumUpgradePromptService, + useClass: forwardRef(() => WebVaultPremiumUpgradePromptService), + }, { provide: ViewPasswordHistoryService, useClass: VaultViewPasswordHistoryService }, { provide: CipherFormGenerationService, useClass: WebCipherFormGenerationService }, RoutedVaultFilterService, @@ -452,7 +463,7 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { const canAccessAttachments = await firstValueFrom(this.canAccessAttachments$); if (!canAccessAttachments) { - await this.premiumUpgradeService.promptForPremium(); + await this.premiumUpgradeService.promptForPremium(this.cipher?.organizationId); return; } @@ -535,8 +546,6 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { } private updateTitle(): void { - const mode = this.formConfig.mode || this.params.mode; - const type = this.cipher?.type ?? this.formConfig.cipherType; const translation: { [key: string]: { [key: number]: string } } = { view: { [CipherType.Login]: "viewItemHeaderLogin", @@ -560,11 +569,15 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { [CipherType.SshKey]: "editItemHeaderSshKey", }, }; + const type = this.cipher?.type ?? this.formConfig.cipherType; + let mode: "view" | "edit" | "new" = "view"; - const effectiveMode = - mode === "partial-edit" || mode === "edit" ? "edit" : translation[mode] ? mode : "new"; + if (this.params.mode === "form") { + mode = + this.formConfig.mode === "edit" || this.formConfig.mode === "partial-edit" ? "edit" : "new"; + } - const fullTranslation = translation[effectiveMode][type]; + const fullTranslation = translation[mode][type]; this.title = this.i18nService.t(fullTranslation); } diff --git a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html index cb7120cca91..c1955e56903 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.html @@ -22,7 +22,7 @@ bitLink [disabled]="disabled" type="button" - class="tw-flex tw-text-start tw-leading-snug" + class="tw-flex tw-text-start tw-leading-snug tw-truncate" linkType="secondary" title="{{ 'viewCollectionWithName' | i18n: collection.name }}" [routerLink]="[]" diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts index 7aa1d441589..6feaa52d190 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts @@ -346,9 +346,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { .map((r) => r.cipherType); const toExclude = [...excludeTypes, ...restrictedForUser]; - return this.allTypeFilters.filter( - (f) => typeof f.type === "string" || !toExclude.includes(f.type), - ); + return this.allTypeFilters.filter((f) => !toExclude.includes(f.type)); }), switchMap((allowed) => this.vaultFilterService.buildTypeTree(allFilter, allowed)), distinctUntilChanged(), diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts index f2c60651ed9..c05459250c0 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts @@ -247,6 +247,9 @@ describe("vault filter service", () => { createCollectionView("id-3", "Collection 1/Collection 3", "org test id"), ]; collectionViews.next(storedCollections); + collectionService.groupByOrganization.mockReturnValue( + new Map([["org test id" as OrganizationId, storedCollections]]), + ); const result = await firstValueFrom(vaultFilterService.collectionTree$); @@ -260,6 +263,9 @@ describe("vault filter service", () => { createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"), ]; collectionViews.next(storedCollections); + collectionService.groupByOrganization.mockReturnValue( + new Map([["org test id" as OrganizationId, storedCollections]]), + ); const result = await firstValueFrom(vaultFilterService.collectionTree$); @@ -276,6 +282,9 @@ describe("vault filter service", () => { createCollectionView("id-4", "Collection 1/Collection 4", "org test id"), ]; collectionViews.next(storedCollections); + collectionService.groupByOrganization.mockReturnValue( + new Map([["org test id" as OrganizationId, storedCollections]]), + ); const result = await firstValueFrom(vaultFilterService.collectionTree$); @@ -294,6 +303,9 @@ describe("vault filter service", () => { createCollectionView("id-3", "Collection 1/Collection 2/Collection 3", "org test id"), ]; collectionViews.next(storedCollections); + collectionService.groupByOrganization.mockReturnValue( + new Map([["org test id" as OrganizationId, storedCollections]]), + ); const result = await firstValueFrom(vaultFilterService.collectionTree$); @@ -302,7 +314,7 @@ describe("vault filter service", () => { expect(c3.parent.node.id).toEqual("id-1"); }); - it.only("calls sortDefaultCollections with the correct args", async () => { + it("calls sortDefaultCollections with the correct args", async () => { const storedOrgs = [ createOrganization("id-defaultOrg1", "org1"), createOrganization("id-defaultOrg2", "org2"), @@ -326,6 +338,9 @@ describe("vault filter service", () => { ), ]; collectionViews.next(storedCollections); + collectionService.groupByOrganization.mockReturnValue( + new Map([["org test id" as OrganizationId, storedCollections]]), + ); await firstValueFrom(vaultFilterService.collectionTree$); diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts index eeecccc87d6..5897ea8c2ce 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts @@ -243,22 +243,28 @@ export class VaultFilterService implements VaultFilterServiceAbstraction { if (!collections) { return headNode; } - const nodes: TreeNode[] = []; + const all: TreeNode[] = []; if (defaultCollectionsFlagEnabled) { collections = sortDefaultCollections(collections, orgs, this.i18nService.collator); } - collections.forEach((c) => { - const collectionCopy = cloneCollection( - new CollectionView({ ...c, name: c.name }), - ) as CollectionFilter; - collectionCopy.icon = "bwi-collection-shared"; - const parts = c.name != null ? c.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : []; - ServiceUtils.nestedTraverse(nodes, 0, parts, collectionCopy, null, NestingDelimiter); - }); + const groupedByOrg = this.collectionService.groupByOrganization(collections); - nodes.forEach((n) => { + for (const group of groupedByOrg.values()) { + const nodes: TreeNode[] = []; + for (const c of group) { + const collectionCopy = cloneCollection( + new CollectionView({ ...c, name: c.name }), + ) as CollectionFilter; + collectionCopy.icon = "bwi-collection-shared"; + const parts = c.name ? c.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : []; + ServiceUtils.nestedTraverse(nodes, 0, parts, collectionCopy, undefined, NestingDelimiter); + } + all.push(...nodes); + } + + all.forEach((n) => { n.parent = headNode; headNode.children.push(n); }); diff --git a/apps/web/src/app/vault/individual-vault/view.component.ts b/apps/web/src/app/vault/individual-vault/view.component.ts index ea0b66f12d0..6de29f8e328 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.ts @@ -15,7 +15,6 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { CollectionId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -33,7 +32,6 @@ import { import { CipherViewComponent } from "@bitwarden/vault"; import { SharedModule } from "../../shared/shared.module"; -import { WebVaultPremiumUpgradePromptService } from "../../vault/services/web-premium-upgrade-prompt.service"; export interface ViewCipherDialogParams { cipher: CipherView; @@ -75,10 +73,7 @@ export interface ViewCipherDialogCloseResult { selector: "app-vault-view", templateUrl: "view.component.html", imports: [CipherViewComponent, CommonModule, AsyncActionsModule, DialogModule, SharedModule], - providers: [ - { provide: ViewPasswordHistoryService, useClass: VaultViewPasswordHistoryService }, - { provide: PremiumUpgradePromptService, useClass: WebVaultPremiumUpgradePromptService }, - ], + providers: [{ provide: ViewPasswordHistoryService, useClass: VaultViewPasswordHistoryService }], }) export class ViewComponent implements OnInit { cipher: CipherView; diff --git a/apps/web/src/app/vault/services/web-premium-upgrade-prompt.service.ts b/apps/web/src/app/vault/services/web-premium-upgrade-prompt.service.ts index 7dfd1146469..87fcdc345d8 100644 --- a/apps/web/src/app/vault/services/web-premium-upgrade-prompt.service.ts +++ b/apps/web/src/app/vault/services/web-premium-upgrade-prompt.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from "@angular/core"; +import { Injectable, Optional } from "@angular/core"; import { Router } from "@angular/router"; import { Subject } from "rxjs"; @@ -16,7 +16,7 @@ export class WebVaultPremiumUpgradePromptService implements PremiumUpgradePrompt constructor( private dialogService: DialogService, private router: Router, - private dialog: DialogRef, + @Optional() private dialog?: DialogRef, ) {} /** @@ -53,7 +53,7 @@ export class WebVaultPremiumUpgradePromptService implements PremiumUpgradePrompt if (route) { await this.router.navigate(route); } - if (confirmed) { + if (confirmed && this.dialog) { this.dialog.close(VaultItemDialogResult.PremiumUpgrade); } } diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index b4d73ccf0d2..fcfd90269f2 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Weens ’n ondernemingsbeleid mag u geen wagwoorde in u persoonlike kluis bewaar nie. Verander die eienaarskap na ’n organisasie en kies uit ’n van die beskikbare versamelings." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Deaktiveer Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO gedeaktiveer" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Geaktiveerde Key Connector" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index b17d503b3ad..fd0214fa71d 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index fc6db3b6695..8af382e2ad4 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Müəssisə siyasətinə görə, elementləri şəxsi seyfinizdə saxlamağınız məhdudlaşdırılıb. Sahiblik seçimini təşkilat olaraq dəyişdirin və mövcud kolleksiyalar arasından seçim edin." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "\"Send\"i sıradan çıxart" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO sıradan çıxarılıb" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector aktivdir" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Ödənişsiz təşkilatların ən çox 2 kolleksiyası ola bilər. Daha çox kolleksiya əlavə etmək üçün ödənişli bir plana yüksəldin." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Biznes vahidi" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "İndi doğrula." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 4c1337caa6b..91eb4328810 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "У адпаведнасці з палітыкай прадпрыемства вам забаронена захоўваць элементы ў асабістым сховішчы. Змяніце параметры ўласнасці на арганізацыю і выберыце з даступных калекцый." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Выдаліць Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO адключаны" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector уключаны" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index 7d7ca9a49e7..c1519a74b36 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Заради някоя политика за голяма организация не може да запазвате елементи в собствения си трезор. Променете собствеността да е на организация и изберете от наличните колекции." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Без изпращане" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "Еднократното удостоверяване е изключено" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ трябва да се впише чрез еднократно удостоверяване", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Конекторът за ключове е включен" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Безплатните организации могат да имат не повече от 2 колекции. Надградете до платен план, ако искате да имате повече колекции." }, + "searchArchive": { + "message": "Търсене в архива" + }, + "archive": { + "message": "Архив" + }, + "noItemsInArchive": { + "message": "Няма елементи в архива" + }, + "archivedItemsDescription": { + "message": "Архивираните елементи ще се показват тук и ще бъдат изключени от общите резултати при търсене и от предложенията за автоматично попълване." + }, "businessUnit": { "message": "Бизнес единица" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Потвърдете сега." + }, + "additionalStorageGB": { + "message": "Допълнително място в ГБ" + }, + "additionalServiceAccountsV2": { + "message": "Допълнителни машинни акаунти" + }, + "secretsManagerSeats": { + "message": "Брой потребители за Управлението на тайни" + }, + "additionalStorage": { + "message": "Допълнително място" + }, + "expandPurchaseDetails": { + "message": "Разгъване на подробностите за покупката" + }, + "collapsePurchaseDetails": { + "message": "Свиване на подробностите за покупката" + }, + "familiesMembership": { + "message": "Членство за семейства" + }, + "planDescPremium": { + "message": "Пълна сигурност в Интернет" + }, + "planDescFamiliesV2": { + "message": "Допълнителна защита за Вашето семейство" + }, + "planDescFreeV2": { + "message": "Споделяне с още $COUNT$ потребител", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Допълнителни възможности за всякакви организации" + }, + "planNameCustom": { + "message": "Персонализиран план" + }, + "planDescCustom": { + "message": "Битуорден работи за компании от всякакви размери, когато става въпрос за пароли и поверителна информация. Ако сте част от голяма компания, свържете се с отдела по продажби, за да попитате за цена." + }, + "builtInAuthenticator": { + "message": "Вграден удостоверител" + }, + "breachMonitoring": { + "message": "Наблюдение за пробиви" + }, + "andMoreFeatures": { + "message": "И още!" + }, + "secureFileStorage": { + "message": "Сигурно съхранение на файлове" + }, + "familiesUnlimitedSharing": { + "message": "Неограничено споделяне – изберете кой и какво може да вижда" + }, + "familiesUnlimitedCollections": { + "message": "Неограничен брой колекции за семейства" + }, + "familiesSharedStorage": { + "message": "Споделено пространство за важните семейни данни" + }, + "limitedUsersV2": { + "message": "До $COUNT$ членове", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "До $COUNT$ колекции", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Винаги безплатно" + }, + "twoSecretsIncluded": { + "message": "2 тайни" + }, + "projectsIncludedV2": { + "message": "$COUNT$ проект(а)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Сигурно споделяне на елементи" + }, + "scimSupport": { + "message": "Поддръжка на SCIM" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ машинни акаунти", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Политики за сигурност за големи компании" + }, + "selfHostOption": { + "message": "Възможност за собствен хостинг" + }, + "complimentaryFamiliesPlan": { + "message": "Допълнителен безплатен семеен план за всички потребители" + }, + "strengthenCybersecurity": { + "message": "Затвърдете киберсигурността" + }, + "boostProductivity": { + "message": "Увеличете продуктивността" + }, + "seamlessIntegration": { + "message": "Безпроблемна интеграция" } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 66f95891598..53f4cc91ae1 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 67fa94124a0..f53fc830b7a 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 01d1ea8a88f..d9dfa7c1eac 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "A causa d'una política empresarial, no podeu guardar elements a la vostra caixa forta personal. Canvieu l'opció Propietat en organització i trieu entre les col·leccions disponibles." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Suprimeix Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO inhabilitat" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Connector de claus habilitat" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Unitat de negoci" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index b414c0c3305..576fe8b4d3f 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Z důvodu podnikových zásad nemůžete ukládat položky do svého osobního trezoru. Změňte vlastnictví položky na organizaci a poté si vyberte z dostupných sbírek." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Odebrat Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO je vypnuto" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ se musí přihlásit jednotným přihlášením", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Je aktivován Key Connector" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Bezplatné organizace mohou mít až 2 sbírky. Chcete-li přidat více sbírek, přejděte na placený tarif." }, + "searchArchive": { + "message": "Hledat v archivu" + }, + "archive": { + "message": "Archivovat" + }, + "noItemsInArchive": { + "message": "Žádné položky v archivu" + }, + "archivedItemsDescription": { + "message": "Zde se zobrazí archivované položky a budou vyloučeny z obecných výsledků vyhledávání a návrhů automatického vyplňování." + }, "businessUnit": { "message": "Obchodní jednotka" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Ověřit nyní" + }, + "additionalStorageGB": { + "message": "Další úložiště (GB)" + }, + "additionalServiceAccountsV2": { + "message": "Další strojové účty" + }, + "secretsManagerSeats": { + "message": "Uživatelé Správce tajných klíčů" + }, + "additionalStorage": { + "message": "Další úložiště" + }, + "expandPurchaseDetails": { + "message": "Rozbalit podrobnosti o nákupu" + }, + "collapsePurchaseDetails": { + "message": "Sbalit podrobnosti o nákupu" + }, + "familiesMembership": { + "message": "Členství v rodinách" + }, + "planDescPremium": { + "message": "Dokončit online zabezpečení" + }, + "planDescFamiliesV2": { + "message": "Prémiové zabezpečení pro Vaši rodinu" + }, + "planDescFreeV2": { + "message": "Sdílet s dalšími $COUNT$ uživateli", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Pokročilé funkce pro jakékoli organizace" + }, + "planNameCustom": { + "message": "Vlastní plán" + }, + "planDescCustom": { + "message": "Bitwarden se přizpůsobí firmám všech velikostí a zajistí bezpečnost hesel a citlivých informací. Pokud jste součástí velké společnosti, kontaktujte obchodní oddělení a požádejte o cenovou nabídku." + }, + "builtInAuthenticator": { + "message": "Vestavěný autentifikátor" + }, + "breachMonitoring": { + "message": "Sledování úniků" + }, + "andMoreFeatures": { + "message": "A ještě více!" + }, + "secureFileStorage": { + "message": "Zabezpečené úložiště souborů" + }, + "familiesUnlimitedSharing": { + "message": "Neomezené sdílení - vyberte kdo co vidí" + }, + "familiesUnlimitedCollections": { + "message": "Neomezené rodinné sbírky" + }, + "familiesSharedStorage": { + "message": "Sdílené úložiště pro důležité rodinné informace" + }, + "limitedUsersV2": { + "message": "Pro až $COUNT$ členů", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Pro až $COUNT$ sbírek", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Vždy zdarma" + }, + "twoSecretsIncluded": { + "message": "2 tajné klíče" + }, + "projectsIncludedV2": { + "message": "$COUNT$ projektů", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Zabezpečené sdílení položek" + }, + "scimSupport": { + "message": "Podpora SCIM" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ strojových účtů", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Bezpečnosti zásady podniků" + }, + "selfHostOption": { + "message": "Volba vlastního hostitele" + }, + "complimentaryFamiliesPlan": { + "message": "Bezplatný rodinný plán pro všechny uživatele" + }, + "strengthenCybersecurity": { + "message": "Posílená kybernetická bezpečnost" + }, + "boostProductivity": { + "message": "Zvýšená produktivita" + }, + "seamlessIntegration": { + "message": "Hladká integrace" } } diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 7755341aae1..43fc75230a9 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 086fb3b7dac..f5441544b96 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Grundet en virksomhedspolitik kan du ikke gemme emner i din personlige boks. Skift ejerskabsindstillingen til en organisation, og vælg fra de tilgængelige samlinger." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Fjern Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO deaktiveret" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector aktiveret" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index f2186926cfd..012f562fe8c 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Aufgrund einer Unternehmensrichtlinie darfst du keine Einträge in deinem persönlichen Tresor speichern. Ändere die Eigentümer-Option in eine Organisation und wähle aus den verfügbaren Sammlungen." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Send entfernen" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO deaktiviert" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector aktiviert" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Kostenlose Organisationen können bis zu 2 Sammlungen haben. Upgrade auf ein kostenpflichtiges Abo, um mehr Sammlungen hinzuzufügen." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Geschäftsbereich" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index 0f4695e0293..0c876ddd282 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Λόγω μιας Πολιτικής Επιχειρήσεων, δεν επιτρέπεται η αποθήκευση στοιχείων στο προσωπικό σας vault. Αλλάξτε την επιλογή Ιδιοκτησίας σε έναν οργανισμό και επιλέξτε από τις διαθέσιμες Συλλογές." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Απενεργοποίηση Αποστολής" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "Απενεργοποιημένο SSO" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Ενεργοποιημένος Σύνδεσμος Κλειδιών" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 46e4f066525..09d88d8ee1a 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -5561,6 +5561,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 867d4a483d6..edf4b59c5bd 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organisation and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned off" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organisations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organisation" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 4c079978ca4..ed092371092 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Disable Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organisations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organisation" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 8cc3eb5ade2..39ee969de3b 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Pro entreprena politiko, vi ne rajtas konservi artikolojn al via persona trezorejo. Ŝanĝu la opcion Proprieto al organizo kaj elektu el disponeblaj Kolektoj." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Neebligi la Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Aktiviĝis Key Connector" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 0d1e9586acd..7d76870ddc6 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Debido a una política empresarial, usted está restringido a guardar artículos en su caja fuerte personal. Cambie la opción Propiedad a una organización y elija de entre las colecciones disponibles." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Desactivar envío" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO desactivado" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Conector de claves habilitado" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 88382dff976..83ff0028e0d 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Ettevõtte poliitika tõttu ei saa sa andmeid oma personaalsesse Hoidlasse salvestada. Vali Omanikuks organisatsioon ja vali mõni saadavaolevatest Kogumikest." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Keela Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO välja lülitatud" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 4178d4f02b2..0dc74e0bb94 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Erakundeko politika bat dela eta, ezin dituzu elementuak zure kutxa gotor pertsonalean gorde. Aldatu jabe aukera erakunde aukera batera, eta aukeratu bilduma erabilgarrien artean." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Kendu Send-ak" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO desgaituta" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector gaituta" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 1b351197aad..0211b3a7d12 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "به دلیل سیاست پرمیوم، برای ذخیره موارد در گاوصندوق شخصی خود محدود شده اید. گزینه مالکیت را به یک سازمان تغییر دهید و مجموعه های موجود را انتخاب کنید." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "حذف ارسال" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO روشن شد" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "رابط کلید فعال شد" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "سازمان‌های رایگان می‌توانند حداکثر تا ۲ مجموعه داشته باشند. برای اضافه کردن مجموعه‌های بیشتر، به طرح پولی ارتقا دهید." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "واحد کسب و کار" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 6f84d9f3640..98f37b43cf4 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Yrityskäytännön johdosta kohteiden tallennus yksityiseen holviin ei ole mahdollista. Muuta omistusasetus organisaatiolle ja valitse käytettävissä olevista kokoelmista." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Poista Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "Kertakirjautuminen poistettiin käytöstä" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector otettiin käyttöön" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 201e84c42c4..8bb6d1fa53c 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Dahil sa Enterprise Policy, ikaw ay hindi pinapayagan na mag-save ng mga item sa iyong personal vault. Baguhin ang Ownership option sa isang organisasyon at pumili mula sa mga available na collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Alisin ang Ipadala" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "Nakabukas ang SSO" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Pinagana ang Key Connector" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index 45d7410a44d..2f056f4e384 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "En raison d'une politique de sécurité Entreprise, il vous est interdit d'enregistrer des éléments dans votre coffre personnel. Sélectionnez une organisation dans l'option Propriété et choisissez parmi les collections disponibles." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Supprimer le Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO désactivé" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ doit se connecter avec une Authentification Unique (SSO)", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activé" }, @@ -10464,7 +10480,7 @@ "message": "Gestionnaire de Mots de Passe Bitwarden" }, "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." + "message": "Votre abonnement offert gratuit 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." @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Les organisations gratuites peuvent avoir jusqu'à 2 collections. Passez à une offre payante pour ajouter plus de collections." }, + "searchArchive": { + "message": "Rechercher dans l'archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "Aucun élément dans l'archive" + }, + "archivedItemsDescription": { + "message": "Les éléments archivés apparaîtront ici et seront exclus des résultats de recherche généraux et des suggestions de remplissage automatique." + }, "businessUnit": { "message": "Unité d'affaires" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Vérifier maintenant." + }, + "additionalStorageGB": { + "message": "Stockage additionnel (Go)" + }, + "additionalServiceAccountsV2": { + "message": "Comptes de machine supplémentaires" + }, + "secretsManagerSeats": { + "message": "Places du Secrets Manager" + }, + "additionalStorage": { + "message": "Stockage supplémentaire" + }, + "expandPurchaseDetails": { + "message": "Développer les détails de l'achat" + }, + "collapsePurchaseDetails": { + "message": "Réduire les détails de l'achat" + }, + "familiesMembership": { + "message": "Abonnement à Familles" + }, + "planDescPremium": { + "message": "Sécurité en ligne complète" + }, + "planDescFamiliesV2": { + "message": "Sécurité Premium pour votre famille" + }, + "planDescFreeV2": { + "message": "Partager avec $COUNT$ autres utilisateurs", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Fonctionnalités avancées pour toute organisation" + }, + "planNameCustom": { + "message": "Forfait personnalisé" + }, + "planDescCustom": { + "message": "Bitwarden évolue avec des entreprises de toutes tailles pour sécuriser les mots de passe et les informations sensibles. Si vous faites partie d'une grande entreprise, contactez les ventes pour demander une soumission." + }, + "builtInAuthenticator": { + "message": "Authentificateur intégré" + }, + "breachMonitoring": { + "message": "Surveillance des fuites" + }, + "andMoreFeatures": { + "message": "Et encore plus !" + }, + "secureFileStorage": { + "message": "Stockage sécurisé de fichier" + }, + "familiesUnlimitedSharing": { + "message": "Partage illimité - choisissez qui voit quoi" + }, + "familiesUnlimitedCollections": { + "message": "Collections familiales illimitées" + }, + "familiesSharedStorage": { + "message": "Stockage partagé pour les informations importantes de la famille" + }, + "limitedUsersV2": { + "message": "Jusqu'à $COUNT$ membres", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Jusqu'à $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Toujours gratuit" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ projet(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Partage sécurisé d’éléments" + }, + "scimSupport": { + "message": "Support SCIM" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ comptes machine", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Politiques de sécurité de l'entreprise" + }, + "selfHostOption": { + "message": "Option d'auto-hébergement" + }, + "complimentaryFamiliesPlan": { + "message": "Plan pour familles offert gratuitement pour tous les utilisateurs" + }, + "strengthenCybersecurity": { + "message": "Renforcer la cybersécurité" + }, + "boostProductivity": { + "message": "Améliore la productivité" + }, + "seamlessIntegration": { + "message": "Intégrations transparentes" } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index 73a32f6bcc6..8c163ef4f6a 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 1fa6e8813e5..72a63ab210a 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "בשל מדיניות ארגונית, אתה מוגבל מלשמור פריטים לכספת האישית שלך. שנה את אפשרות הבעלות לארגון ובחר מאוספים זמינים." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "הסר סֵנְד" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO כבוי" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector הופעל" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "לארגונים חינמיים יכולים להיות עד 2 אוספים. שדרג לתוכנית בתשלום כדי להוסיף עוד אוספים." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "יחידת עסקים" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "אמת כעת." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 3cfddded149..9cdcabf7423 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 0fa944ecc32..66eca3f69a9 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -3,7 +3,7 @@ "message": "Sve aplikacije" }, "activity": { - "message": "Activity" + "message": "Aktivnost" }, "appLogoLabel": { "message": "Bitwarden logo" @@ -60,7 +60,7 @@ "message": "Stvori novu stavku prijave" }, "criticalApplicationsActivityDescription": { - "message": "Once you mark applications critical, they will display here." + "message": "Aplikacije označene kao kritične će biti prikazane ovdje." }, "criticalApplicationsWithCount": { "message": "Kritične aplikacije ($COUNT$)", @@ -72,7 +72,7 @@ } }, "countOfCriticalApplications": { - "message": "$COUNT$ critical applications", + "message": "Kritičnih aplikacija: $COUNT$", "placeholders": { "count": { "content": "$1", @@ -135,10 +135,10 @@ "message": "Rizični korisnici" }, "membersAtRiskActivityDescription": { - "message": "Members with edit access to at-risk items for critical applications" + "message": "Članovi koji mogu uređivati stavke za aplikacije označene kao kritične" }, "membersAtRisk": { - "message": "$COUNT$ members at risk", + "message": "Rizičnih članova: $COUNT$", "placeholders": { "count": { "content": "$1", @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Pravila tvrtke onemogućuju spremanje stavki u osobni trezor. Promijeni vlasništvo stavke na tvrtku i odaberi dostupnu Zbirku." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Onemogući Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO onemogućen" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ se mora prijavljivati sa SSO", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Konektor za ključ omogućen" }, @@ -9839,7 +9855,7 @@ "message": "Token nositelja" }, "repositoryNameHint": { - "message": "Name of the repository to ingest into" + "message": "Naziv repozitorija u koji se unosi" }, "index": { "message": "Indeks" @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Besplatne organizacije mogu imati do 2 zbirke. Nadogradi na plaćeni plan za dodavanje više zbirki." }, + "searchArchive": { + "message": "Pretraži arhivu" + }, + "archive": { + "message": "Arhiva" + }, + "noItemsInArchive": { + "message": "Nema stavki u arhivi" + }, + "archivedItemsDescription": { + "message": "Arhivirane stavke biti će prikazane ovdje i biti će izuzete iz rezultata općih pretraga i preporuka auto-ispune." + }, "businessUnit": { "message": "Poslovna jedinica" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Potvrdi sada." + }, + "additionalStorageGB": { + "message": "Dodati GB pohrane" + }, + "additionalServiceAccountsV2": { + "message": "Dodatni mašinski računi" + }, + "secretsManagerSeats": { + "message": "Secret Manager licence" + }, + "additionalStorage": { + "message": "Dodatna pohrana" + }, + "expandPurchaseDetails": { + "message": "Proširi detalje kupnje" + }, + "collapsePurchaseDetails": { + "message": "Skupi detalje kupnje" + }, + "familiesMembership": { + "message": "Obiteljsko članstvo" + }, + "planDescPremium": { + "message": "Dovrši online sigurnost" + }, + "planDescFamiliesV2": { + "message": "Premium sigurnost za tvoju obitelj" + }, + "planDescFreeV2": { + "message": "Podijeli s ovoliko korisnika: $COUNT$", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Napredne mogućnosti za bilo koju organizaciju" + }, + "planNameCustom": { + "message": "Vlastiti plan" + }, + "planDescCustom": { + "message": "Bitwarden se prilagođava tvrtkama svih veličina kako bi osigurao lozinke i osjetljive podatke. Ako si dio velike tvrtke, zatraži ponudu od odjela prodaje." + }, + "builtInAuthenticator": { + "message": "Ugrađeni autentifikator" + }, + "breachMonitoring": { + "message": "Nadzor proboja" + }, + "andMoreFeatures": { + "message": "I više!" + }, + "secureFileStorage": { + "message": "Sigurna pohrana datoteka" + }, + "familiesUnlimitedSharing": { + "message": "Neograničeno dijeljenje - odaberi tko vidi što" + }, + "familiesUnlimitedCollections": { + "message": "Neograničene obiteljske zbirke" + }, + "familiesSharedStorage": { + "message": "Dijeljena pohrana za važbe obiteljske informacije" + }, + "limitedUsersV2": { + "message": "Najveći broj članova: $COUNT$", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Najveći broj zbirki: $COUNT$", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Zauvijek besplatno" + }, + "twoSecretsIncluded": { + "message": "2 tajne" + }, + "projectsIncludedV2": { + "message": "Broj projekata: $COUNT$", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Sigurno dijeljenje stavki" + }, + "scimSupport": { + "message": "SCIM podrška" + }, + "includedMachineAccountsV2": { + "message": "Mašinskih računa: $COUNT$", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Sigurnosne politike tvrtke" + }, + "selfHostOption": { + "message": "Mogućnost samostalnog hostinga" + }, + "complimentaryFamiliesPlan": { + "message": "Besplatni obiteljski plan za sve korisnike" + }, + "strengthenCybersecurity": { + "message": "Jačanje kibernetičke sigurnosti" + }, + "boostProductivity": { + "message": "Povećanje produktivnosti" + }, + "seamlessIntegration": { + "message": "Jednostavna integracija" } } diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 4a4b9b1e039..4a3ae2eb194 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Egy vállalati házirend miatt korlátozásra került az elemek személyes tárolóba történő mentése. Módosítsuk a Tulajdon opciót egy szervezetre és válasszunk az elérhető gyűjtemények közül." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Send letiltása" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "Az SSO bekapcsolásra került." }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ segítségével be kell jelentkezni egyszeri bejelentkezéssel.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Bekapcsolt kulcskapcsoló" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Az ingyenes szervezeteknek legfeljebb 2 gyűjteményük lehet. Térjünk át egy fizetett csomagra további gyűjtemények hozzáadásához." }, + "searchArchive": { + "message": "Keresés archívum" + }, + "archive": { + "message": "Archívum" + }, + "noItemsInArchive": { + "message": "Nincs elem az archívumban." + }, + "archivedItemsDescription": { + "message": "Az archivált elemek itt jelennek meg és kizárásra kerülnek az általános keresési eredményekből és az automatikus kitöltési javaslatokból." + }, "businessUnit": { "message": "Üzleti egység" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Ellenőrzés most" + }, + "additionalStorageGB": { + "message": "Kiegészítő tárhely (GB)" + }, + "additionalServiceAccountsV2": { + "message": "Kiegészítő gépi fiókok" + }, + "secretsManagerSeats": { + "message": "Titkos kód kezelő helyek" + }, + "additionalStorage": { + "message": "Kiegészítő tárhely" + }, + "expandPurchaseDetails": { + "message": "Vásárlás részletek kinyitása" + }, + "collapsePurchaseDetails": { + "message": "Vásárlás részletek összezárása" + }, + "familiesMembership": { + "message": "Családi tagság" + }, + "planDescPremium": { + "message": "Teljes körű online biztonság" + }, + "planDescFamiliesV2": { + "message": "Prémium biztonság a család számára" + }, + "planDescFreeV2": { + "message": "Megosztás $COUNT$ másik felhasználóval", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Fejlett képességek bármely szervezet számára" + }, + "planNameCustom": { + "message": "Egyedi csomag" + }, + "planDescCustom": { + "message": "A Bitwarden bármilyen méretű vállalkozásra kiterjed a jelszavak és érzékeny információk biztonsága érdekében. Ha egy nagyvállalat tagja vagyunk, lépjünk kapcsolatba az értékesítéssel ajánlat kéréshez." + }, + "builtInAuthenticator": { + "message": "Beépített hitelesítés" + }, + "breachMonitoring": { + "message": "Adatszivárgás figyelés" + }, + "andMoreFeatures": { + "message": "És sok más!" + }, + "secureFileStorage": { + "message": "Biztonságos fájl tárolás" + }, + "familiesUnlimitedSharing": { + "message": "Korlátlan megosztás - válasszuk ki, ki mit láthat" + }, + "familiesUnlimitedCollections": { + "message": "Korlátlan családi gyűjtemény" + }, + "familiesSharedStorage": { + "message": "Megosztott tárhely a fontos családi információkhoz" + }, + "limitedUsersV2": { + "message": "Legfeljebb $COUNT$ tag részére", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Legfeljebb $COUNT$ gyűjtemény", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Mindig ingyenes" + }, + "twoSecretsIncluded": { + "message": "2 titkos kód" + }, + "projectsIncludedV2": { + "message": "$COUNT$ projekt", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Biztonságos elem megosztás" + }, + "scimSupport": { + "message": "SCIM támogatás" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ gépi fiók", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Vállalati biztonsági irányelvek" + }, + "selfHostOption": { + "message": "Öntároló opció" + }, + "complimentaryFamiliesPlan": { + "message": "Kiegészítő családi csomag minden felhasználó számára" + }, + "strengthenCybersecurity": { + "message": "A kiberbiztonság megerősítése" + }, + "boostProductivity": { + "message": "Produktivitás növelése" + }, + "seamlessIntegration": { + "message": "Zökkenőmentes integráció" } } diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 2783d236264..39f4232a707 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Karena Kebijakan Perusahaan, Anda dilarang menyimpan item ke lemari besi pribadi Anda. Ubah opsi Kepemilikan ke organisasi dan pilih dari Koleksi yang tersedia." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Nonaktifkan Kirim" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO tidak aktif" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Konektor Kunci diaktifkan" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index 7ed99e0ae29..3875bcb42f2 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -3,7 +3,7 @@ "message": "Tutte le applicazioni" }, "activity": { - "message": "Activity" + "message": "Attività" }, "appLogoLabel": { "message": "Logo Bitwarden" @@ -762,23 +762,23 @@ "message": "Visualizza elemento" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "Nuovo Login", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "Nuova Carta", "description": "Header for new card item type" }, "newItemHeaderIdentity": { - "message": "New Identity", + "message": "Nuova Identità", "description": "Header for new identity item type" }, "newItemHeaderNote": { - "message": "New Note", + "message": "Nuova Nota", "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "Nuova chiave SSH", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { @@ -790,15 +790,15 @@ "description": "Header for new file send" }, "editItemHeaderLogin": { - "message": "Edit Login", + "message": "Modifica Login", "description": "Header for edit login item type" }, "editItemHeaderCard": { - "message": "Edit Card", + "message": "Modifica Carta", "description": "Header for edit card item type" }, "editItemHeaderIdentity": { - "message": "Edit Identity", + "message": "Modifica Identità", "description": "Header for edit identity item type" }, "editItemHeaderNote": { @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "A causa di una politica aziendale, non puoi salvare elementi nella tua cassaforte personale. Cambia l'opzione di proprietà in un'organizzazione e scegli tra le raccolte disponibili." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Rimuovi Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO disattivato" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector attivato" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Le organizzazioni gratuite possono avere fino a 2 raccolte. Aggiorna ad un piano a pagamento per crearne di più." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 5edd1b7a97b..3f2a2ada371 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "組織のポリシーにより、個人保管庫へのアイテムの保存が制限されています。 「所有権」オプションを組織に変更し、利用可能なコレクションから選択してください。" }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Send を無効化" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSOを無効にしました" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "キーコネクターを有効にしました" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "無料版の組織ではコレクションは 2 つまでです。さらにコレクションを追加するには有料プランにアップグレードしてください。" }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index d2ccbe5e3d5..170b989ff5d 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 6c6c71237bb..526b7567d99 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 739d82939e7..13f72bed6c0 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "ಎಂಟರ್‌ಪ್ರೈಸ್ ನೀತಿಯಿಂದಾಗಿ, ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ವಾಲ್ಟ್‌ಗೆ ವಸ್ತುಗಳನ್ನು ಉಳಿಸುವುದರಿಂದ ನಿಮ್ಮನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ. ಮಾಲೀಕತ್ವದ ಆಯ್ಕೆಯನ್ನು ಸಂಸ್ಥೆಗೆ ಬದಲಾಯಿಸಿ ಮತ್ತು ಲಭ್ಯವಿರುವ ಸಂಗ್ರಹಗಳಿಂದ ಆರಿಸಿಕೊಳ್ಳಿ." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "ಕಳುಹಿಸುವುದನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 89b8792a876..33ccaee2b5c 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "엔터프라이즈 정책으로 인해 개인 보관함에 항목을 저장할 수 없습니다. 조직에서 소유권 설정을 변경한 다음, 사용 가능한 컬렉션 중에서 선택해주세요." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Send 비활성화" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO 비활성화됨" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "키 커넥터 활성화됨" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index f57f0d92448..568e23d45b7 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Uzņēmuma nosacījumi liedz saglabāt vienumus privātajā glabātavā. Ir jānorāda piederība apvienībai un jāizvēlas kāds no pieejamajiem krājumiem." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Noņemt Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "Atspējota vienotā pieteikšanās" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ jāpiesakās ar vienoto pieteikšanos", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Iespējots Key Connector" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Apvienībās, kuras izmanto Bitwarden bez maksas, var būt līdz 2 krājumiem. Jāpāriet uz maksas plānu, lai pievienotu vairāk krājumu." }, + "searchArchive": { + "message": "Meklēt arhīvā" + }, + "archive": { + "message": "Arhivēt" + }, + "noItemsInArchive": { + "message": "Arhīvā nav vienumu" + }, + "archivedItemsDescription": { + "message": "Šeit parādīsies arhivētie vienumi, un tie netiks iekļauti vispārējās meklēšanas iznākumos un automātiskās aizpildes ieteikumos." + }, "businessUnit": { "message": "Uzņēmējdarbības vienība" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Apliecini tagad!" + }, + "additionalStorageGB": { + "message": "Papildu krātuve GB" + }, + "additionalServiceAccountsV2": { + "message": "Papildu mašīnu konti" + }, + "secretsManagerSeats": { + "message": "Noslēpumu pārvaldnieka vietas" + }, + "additionalStorage": { + "message": "Papildu krātuve" + }, + "expandPurchaseDetails": { + "message": "Izvērst informāciju par pirkumu" + }, + "collapsePurchaseDetails": { + "message": "Sakļaut informāciju par pirkumu" + }, + "familiesMembership": { + "message": "Dalība ģimeņu plānā" + }, + "planDescPremium": { + "message": "Pilnīga drošība tiešsaistē" + }, + "planDescFamiliesV2": { + "message": "Augstākā labuma drošība ģimenei" + }, + "planDescFreeV2": { + "message": "Kopīgot ar $COUNT$ citiem lietotājiem", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Papildu spējas jebkurai apvienībai" + }, + "planNameCustom": { + "message": "Pielāgots plāns" + }, + "planDescCustom": { + "message": "Bitwarden piemērojas visu lielumu uzņēmējdarbībai, lai aizsargātu paroles un jūtīgu informāciju. Ja esi daļa no liela uzņēmuma, sazinies ar pārdošanas nodaļu, lai pieprasītu cenu piedāvājumu!" + }, + "builtInAuthenticator": { + "message": "Iebūvēts autentificētājs" + }, + "breachMonitoring": { + "message": "Noplūžu pārraudzīšana" + }, + "andMoreFeatures": { + "message": "Un vēl!" + }, + "secureFileStorage": { + "message": "Droša datņu krātuve" + }, + "familiesUnlimitedSharing": { + "message": "Neierobežota kopīgošana - izvēlies, ko kurš redz" + }, + "familiesUnlimitedCollections": { + "message": "Neierobežoti ģimeņu krājumi" + }, + "familiesSharedStorage": { + "message": "Koplietojama krātuve svarīgai ģimenes informācijai" + }, + "limitedUsersV2": { + "message": "Līdz $COUNT$ dalībniekiem", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Līdz $COUNT$ krājumiem", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Vienmēr bez maksas" + }, + "twoSecretsIncluded": { + "message": "2 noslēpumi" + }, + "projectsIncludedV2": { + "message": "$COUNT$ projekts(i)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Droša vienumu kopīgošana" + }, + "scimSupport": { + "message": "SCIM atbalsts" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ mašīnu konti", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Uzņēmumu drošības pamatnostādnes" + }, + "selfHostOption": { + "message": "Pašmitināšanas iespēja" + }, + "complimentaryFamiliesPlan": { + "message": "Bezmaksas ģimeņu plāns visiem lietotājiem" + }, + "strengthenCybersecurity": { + "message": "Stiprini kiberdrošību" + }, + "boostProductivity": { + "message": "Uzlabo ražīgumu" + }, + "seamlessIntegration": { + "message": "Plūdena iekļaušana" } } diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index a13420e9a7e..7f345a5b85c 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 99220e8a7ff..b25c6fb16ea 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 6c6c71237bb..526b7567d99 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 0e2b1bfecad..3cfcd87ec64 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "På grunn av en virksomhetsregel er du begrenset fra å lagre elementer til ditt personlige hvelv. Endre eierskapet til en organisasjon og velg blant tilgjengelige samlinger." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Deaktiver Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "Skrudde av SSO" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector aktivert" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 034f642dc7a..7dff2786a5d 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 275ec1e3c41..1ad5b20745a 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Wegens bedrijfsbeleid mag je geen wachtwoorden opslaan in je persoonlijke kluis. Verander het eigenaarschap naar een organisatie en kies uit een van de beschikbare collecties." }, + "desktopAutotypePolicy": { + "message": "Standaardinstelling Desktop Autotype" + }, + "desktopAutotypePolicyDesc": { + "message": "Desktop Autotype standaard inschakelen voor leden. Leden kunnen Autotype uitzetten in de Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Send uitschakelen" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO uitgeschakeld" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ moet met Single Sign-on inloggen", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector ingeschakeld" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Gratis organisaties kunnen maximaal twee collecties hebben. Upgrade naar een betaald abonnement voor het toevoegen van meer collecties." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Bedrijfseenheid" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Nu verifiëren." + }, + "additionalStorageGB": { + "message": "Extra opslagruimte (GB)" + }, + "additionalServiceAccountsV2": { + "message": "Extra machine-accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager-licenties" + }, + "additionalStorage": { + "message": "Extra opslagruimte" + }, + "expandPurchaseDetails": { + "message": "Aankoopgegevens uitklappen" + }, + "collapsePurchaseDetails": { + "message": "Aankoopgegevens inklappen" + }, + "familiesMembership": { + "message": "Lidmaatschap families" + }, + "planDescPremium": { + "message": "Online beveiliging voltooien" + }, + "planDescFamiliesV2": { + "message": "Premium beveiliging voor je familie" + }, + "planDescFreeV2": { + "message": "Delen met $COUNT$ andere gebruiker", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Geavanceerde mogelijkheden voor iedere bedrijf" + }, + "planNameCustom": { + "message": "Aangepast abonnement" + }, + "planDescCustom": { + "message": "Bitwarden schaalt met bedrijven van ieder formaat om wachtwoorden en gevoelige informatie te beveiligen. Als je deel uitmaakt van een grote onderneming, neem dan contact op met verkoop om een offerte aan te vragen." + }, + "builtInAuthenticator": { + "message": "Ingebouwde authenticator" + }, + "breachMonitoring": { + "message": "Lek-monitoring" + }, + "andMoreFeatures": { + "message": "En meer!" + }, + "secureFileStorage": { + "message": "Beveiligde bestandsopslag" + }, + "familiesUnlimitedSharing": { + "message": "Onbeperkt delen - kies wie wat ziet" + }, + "familiesUnlimitedCollections": { + "message": "Onbeperkte familiecollecties" + }, + "familiesSharedStorage": { + "message": "Gedeelde opslag voor belangrijke familie-informatie" + }, + "limitedUsersV2": { + "message": "Tot $COUNT$ leden", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Tot $COUNT$ collectie(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Altijd gratis" + }, + "twoSecretsIncluded": { + "message": "2 geheimen" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(en)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Beveiligd delen van items" + }, + "scimSupport": { + "message": "SCIM ondersteuning" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine-account(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise-beveiligingsbeleid" + }, + "selfHostOption": { + "message": "Optie tot zelf-hosten" + }, + "complimentaryFamiliesPlan": { + "message": "Gratis familie-abonnement voor alle gebruikers" + }, + "strengthenCybersecurity": { + "message": "Cyberbeveiliging versterken" + }, + "boostProductivity": { + "message": "Productiviteit verhogen" + }, + "seamlessIntegration": { + "message": "Naadloze integratie" } } diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index d99e1767eb0..54323d9b12a 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 6c6c71237bb..526b7567d99 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 5f6f15a8661..8254d28f142 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Ze względu na zasadę przedsiębiorstwa, nie możesz zapisywać elementów w osobistym sejfie. Zmień właściciela elementu na organizację i wybierz jedną z dostępnych kolekcji." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Usuń wysyłkę" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "Logowanie jednokrotne SSO zostało wyłączone" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Serwer Key Connector został włączony" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Darmowe organizacje mogą posiadać maksymalnie 2 kolekcje. Aby dodać więcej kolekcji, przejdź na plan płatny." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Jednostka Biznesowa" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index f8e4e695f4e..b2a2924b28b 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Devido a uma Política Empresarial, você está restrito de salvar itens para seu cofre pessoal. Altere a opção de propriedade para uma organização e escolha entre as Coleções disponíveis." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Desabilitar Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "Desativado o SSO" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Ativado o Conector de Chave" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Organizações gratuitas podem ter até duas coleções. Faça o upgrade para um plano pago para adicionar mais coleções." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Unidades de Negócio" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 3ccb06518c4..6f8e4d9c11c 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Devido a uma política empresarial, está impedido de guardar itens no seu cofre pessoal. Altere a opção Propriedade para uma organização e escolha entre as coleções disponíveis." }, + "desktopAutotypePolicy": { + "message": "Definição predefinida da digitação automática no computador" + }, + "desktopAutotypePolicyDesc": { + "message": "Ativar a digitação automática no computador por defeito para os membros. Os membros podem desativar manualmente a escrita automática no cliente Desktop.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remover Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO desativado" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ deve iniciar sessão com o início de sessão único", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector ativado" }, @@ -9839,7 +9855,7 @@ "message": "Token de portador" }, "repositoryNameHint": { - "message": "Name of the repository to ingest into" + "message": "Nome do repositório para ingestão" }, "index": { "message": "Índice" @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "As organizações gratuitas podem ter até 2 coleções. Atualize para um plano pago para adicionar mais coleções." }, + "searchArchive": { + "message": "Procurar no arquivo" + }, + "archive": { + "message": "Arquivar" + }, + "noItemsInArchive": { + "message": "Nenhum item no arquivo" + }, + "archivedItemsDescription": { + "message": "Os itens arquivados aparecerão aqui e serão excluídos dos resultados gerais da pesquisa e das sugestões de preenchimento automático." + }, "businessUnit": { "message": "Unidade de negócio" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verificar agora." + }, + "additionalStorageGB": { + "message": "Armazenamento adicional (GB)" + }, + "additionalServiceAccountsV2": { + "message": "Contas automáticas adicionais" + }, + "secretsManagerSeats": { + "message": "Lugares do Gestor de Segredos" + }, + "additionalStorage": { + "message": "Armazenamento adicional" + }, + "expandPurchaseDetails": { + "message": "Expandir os detalhes da compra" + }, + "collapsePurchaseDetails": { + "message": "Recolher os detalhes da compra" + }, + "familiesMembership": { + "message": "Adesão familiar" + }, + "planDescPremium": { + "message": "Segurança total online" + }, + "planDescFamiliesV2": { + "message": "Segurança de topo para a sua família" + }, + "planDescFreeV2": { + "message": "Partilhar com $COUNT$ outro utilizador", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Capacidades avançadas para qualquer organização" + }, + "planNameCustom": { + "message": "Plano personalizado" + }, + "planDescCustom": { + "message": "O Bitwarden é dimensionado para empresas de todos os tamanhos para proteger palavras-passe e informações confidenciais. Se faz parte de uma grande empresa, contacte o departamento de vendas para solicitar um orçamento." + }, + "builtInAuthenticator": { + "message": "Autenticador incorporado" + }, + "breachMonitoring": { + "message": "Monitorização de violações" + }, + "andMoreFeatures": { + "message": "E muito mais!" + }, + "secureFileStorage": { + "message": "Armazenamento seguro de ficheiros" + }, + "familiesUnlimitedSharing": { + "message": "Partilha ilimitada - escolha quem vê o quê" + }, + "familiesUnlimitedCollections": { + "message": "Coleções familiares ilimitadas" + }, + "familiesSharedStorage": { + "message": "Armazenamento partilhado para informações importantes da família" + }, + "limitedUsersV2": { + "message": "Até $COUNT$ membros", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Até $COUNT$ coleções", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Sempre gratuito" + }, + "twoSecretsIncluded": { + "message": "2 segredos" + }, + "projectsIncludedV2": { + "message": "$COUNT$ projeto(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Partilha segura de itens" + }, + "scimSupport": { + "message": "Suporte SCIM" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ contas automáticas", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Políticas de segurança da empresa" + }, + "selfHostOption": { + "message": "Opção de auto-hospedagem" + }, + "complimentaryFamiliesPlan": { + "message": "Plano familiar gratuito para todos os utilizadores" + }, + "strengthenCybersecurity": { + "message": "Cibersegurança reforçada" + }, + "boostProductivity": { + "message": "Produtividade aumentada" + }, + "seamlessIntegration": { + "message": "Integração perfeita" } } diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 8ea49ad4472..364e7f86e64 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Din cauza unei politici de Întreprindere, nu puteți salva articole în seiful dvs. individual. Schimbați opțiunea de proprietate la o organizație și alegeți din colecțiile disponibile." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Înlăturare Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO a fost activat" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Conector cheie activat" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index b611e925c2e..753b5745f36 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "В соответствии с корпоративной политикой вам запрещено сохранять элементы в личном хранилище. Измените владельца на организацию и выберите из доступных Коллекций." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Отключить Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO отключен" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ должен авторизоваться с использованием SSO", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Соединитель ключей активирован" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "В бесплатных организациях может быть до 2 коллекций. Перейдите на платный план, чтобы добавить больше коллекций." }, + "searchArchive": { + "message": "Поиск в архиве" + }, + "archive": { + "message": "Архив" + }, + "noItemsInArchive": { + "message": "В архиве нет элементов" + }, + "archivedItemsDescription": { + "message": "Архивированные элементы появятся здесь и будут исключены из общих результатов поиска и предложений автозаполнения." + }, "businessUnit": { "message": "Бизнес-единица" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Подтвердить сейчас." + }, + "additionalStorageGB": { + "message": "Дополнительные ГБ хранилища" + }, + "additionalServiceAccountsV2": { + "message": "Дополнительные аккаунты компьютеров" + }, + "secretsManagerSeats": { + "message": "Места менеджера секретов" + }, + "additionalStorage": { + "message": "Дополнительное хранилище" + }, + "expandPurchaseDetails": { + "message": "Отобразить информацию о покупке" + }, + "collapsePurchaseDetails": { + "message": "Скрыть информацию о покупке" + }, + "familiesMembership": { + "message": "Членство Families" + }, + "planDescPremium": { + "message": "Полная онлайн-защищенность" + }, + "planDescFamiliesV2": { + "message": "Премиальная защищенность \n для вашей семьи" + }, + "planDescFreeV2": { + "message": "Поделиться с другими пользователями: $COUNT$", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Расширенные возможности для любой организации" + }, + "planNameCustom": { + "message": "Индивидуальный план" + }, + "planDescCustom": { + "message": "Bitwarden сотрудничает с компаниями любого размера, чтобы обеспечить защищенность паролей и конфиденциальной информации. Если вы являетесь частью крупного предприятия, обратитесь в отдел продаж, чтобы запросить ценовое предложение." + }, + "builtInAuthenticator": { + "message": "Встроенный аутентификатор" + }, + "breachMonitoring": { + "message": "Мониторинг нарушений" + }, + "andMoreFeatures": { + "message": "И многое другое!" + }, + "secureFileStorage": { + "message": "Защищенное хранилище файлов" + }, + "familiesUnlimitedSharing": { + "message": "Неограниченный доступ - выбирайте, кто и что видит" + }, + "familiesUnlimitedCollections": { + "message": "Неограниченные семейные коллекции" + }, + "familiesSharedStorage": { + "message": "Общее хранилище для важной семейной информации" + }, + "limitedUsersV2": { + "message": "До $COUNT$ участников", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "До $COUNT$ коллекций", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Всегда бесплатно" + }, + "twoSecretsIncluded": { + "message": "2 секрета" + }, + "projectsIncludedV2": { + "message": "$COUNT$ проектов", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Защищенный обмен элементами" + }, + "scimSupport": { + "message": "Поддержка SCIM" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ аккаунтов компьютеров", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Политики безопасности предприятия" + }, + "selfHostOption": { + "message": "Собственный хостинг" + }, + "complimentaryFamiliesPlan": { + "message": "Бесплатный семейный тариф для всех пользователей" + }, + "strengthenCybersecurity": { + "message": "Повышение кибербезопасности" + }, + "boostProductivity": { + "message": "Повышение производительности" + }, + "seamlessIntegration": { + "message": "Простая интеграция" } } diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 4c5b7eea1e9..b2b9714e766 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index c5e088c63a0..ab5864579d4 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -3,7 +3,7 @@ "message": "Všetky aplikácie" }, "activity": { - "message": "Activity" + "message": "Aktivita" }, "appLogoLabel": { "message": "Logo Bitwarden" @@ -60,7 +60,7 @@ "message": "Pridať novu položku s prihlásením" }, "criticalApplicationsActivityDescription": { - "message": "Once you mark applications critical, they will display here." + "message": "Tu sa zobrazia aplikácie, ktoré označíte za kritické." }, "criticalApplicationsWithCount": { "message": "Kritické aplikácie ($COUNT$)", @@ -72,7 +72,7 @@ } }, "countOfCriticalApplications": { - "message": "$COUNT$ critical applications", + "message": "$COUNT$ kritických aplikácií", "placeholders": { "count": { "content": "$1", @@ -135,10 +135,10 @@ "message": "Ohrozených členov" }, "membersAtRiskActivityDescription": { - "message": "Members with edit access to at-risk items for critical applications" + "message": "Členovia s oprávnením upravovať ohrozené položky kritických aplikácii" }, "membersAtRisk": { - "message": "$COUNT$ members at risk", + "message": "$COUNT$ ohrozených členov", "placeholders": { "count": { "content": "$1", @@ -762,79 +762,79 @@ "message": "Zobraziť položku" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "Nové prihlásenie", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "Nová karta", "description": "Header for new card item type" }, "newItemHeaderIdentity": { - "message": "New Identity", + "message": "Nová identita", "description": "Header for new identity item type" }, "newItemHeaderNote": { - "message": "New Note", + "message": "Nová poznámka", "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "Nový kľúč SSH", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { - "message": "New Text Send", + "message": "Nový textový Send", "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "New File Send", + "message": "Nový súborový Send", "description": "Header for new file send" }, "editItemHeaderLogin": { - "message": "Edit Login", + "message": "Upraviť prihlásenie", "description": "Header for edit login item type" }, "editItemHeaderCard": { - "message": "Edit Card", + "message": "Upraviť kartu", "description": "Header for edit card item type" }, "editItemHeaderIdentity": { - "message": "Edit Identity", + "message": "Upraviť identitu", "description": "Header for edit identity item type" }, "editItemHeaderNote": { - "message": "Edit Note", + "message": "Upraviť poznámku", "description": "Header for edit note item type" }, "editItemHeaderSshKey": { - "message": "Edit SSH key", + "message": "Upraviť kľúč SSH", "description": "Header for edit SSH key item type" }, "editItemHeaderTextSend": { - "message": "Edit Text Send", + "message": "Upraviť textový Send", "description": "Header for edit text send" }, "editItemHeaderFileSend": { - "message": "Edit File Send", + "message": "Upraviť súborový Send", "description": "Header for edit file send" }, "viewItemHeaderLogin": { - "message": "View Login", + "message": "Zobraziť prihlásenie", "description": "Header for view login item type" }, "viewItemHeaderCard": { - "message": "View Card", + "message": "Zobraziť kartu", "description": "Header for view card item type" }, "viewItemHeaderIdentity": { - "message": "View Identity", + "message": "Zobraziť identitu", "description": "Header for view identity item type" }, "viewItemHeaderNote": { - "message": "View Note", + "message": "Zobraziť poznámku", "description": "Header for view note item type" }, "viewItemHeaderSshKey": { - "message": "View SSH key", + "message": "Zobraziť kľúč SSH", "description": "Header for view SSH key item type" }, "new": { @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Z dôvodu podnikovej politiky máte obmedzené ukladanie položiek do osobného trezora. Zmeňte možnosť vlastníctvo na organizáciu a vyberte si z dostupných zbierok." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Zakázať Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO zakázané" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ sa musí prihlásiť cez prihlasovací formulár spoločnosti ", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -9740,7 +9756,7 @@ "message": "Nepodarilo sa uložiť integráciu. Prosím skúste to neskôr." }, "failedToDeleteIntegration": { - "message": "Failed to delete integration. Please try again later." + "message": "Nepodarilo sa odstrániť integráciu. Prosím skúste to neskôr." }, "deviceIdMissing": { "message": "Chýba ID zariadenia" @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Bezplatné organizácie môžu mat maximálne dve zbierky. Ak chcete pridať viac zbierok povýšte na platené predplatné." }, + "searchArchive": { + "message": "Prehľadať archív" + }, + "archive": { + "message": "Archív" + }, + "noItemsInArchive": { + "message": "Žiadne položky v archíve" + }, + "archivedItemsDescription": { + "message": "Tu sa zobrazia archivované položky, ktoré budú vylúčené zo všeobecného vyhľadávania a z návrhov automatického vypĺňania." + }, "businessUnit": { "message": "Organizačná jednotka" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Overiť teraz." + }, + "additionalStorageGB": { + "message": "Dodatočné úložisko GB" + }, + "additionalServiceAccountsV2": { + "message": "Dodatočné strojové účty" + }, + "secretsManagerSeats": { + "message": "Sedenia pre Secrets Manager" + }, + "additionalStorage": { + "message": "Dodatočné úložisko" + }, + "expandPurchaseDetails": { + "message": "Rozbaliť detaily nákupu" + }, + "collapsePurchaseDetails": { + "message": "Zložiť detaily nákupu" + }, + "familiesMembership": { + "message": "Členstvo pre rodiny" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index dc5e3d76433..e54ef8bb4b6 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 14983e17bdc..8b8edaa1ec3 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/sr_CY/messages.json b/apps/web/src/locales/sr_CY/messages.json index 128058dd092..4ad3b3723cf 100644 --- a/apps/web/src/locales/sr_CY/messages.json +++ b/apps/web/src/locales/sr_CY/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Због смерница за предузећа, ограничено вам је чување предмета у вашем личном трезору. Промените опцију власништва у организацију и изаберите из доступних колекција." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Онемогући слање" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO онемогућен" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Омогућити Key Connector" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Бесплатне организације могу имати до 2 колекције. Надоградите на плаћени план за додавање више колекција." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index c6efcd6ab38..6c5bb424a61 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "På grund av en av företagets policyer är du begränsad från att spara objekt till ditt personliga valv. Ändra ägarskap till en organisation och välj från tillgängliga samlingar." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Radera Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO inaktiverad" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Nyckelkontakt aktiverad" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Gratisorganisationer kan ha upp till 2 samlingar. Uppgradera till en betald plan för att lägga till fler samlingar." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Affärsenhet" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verifiera nu." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Ytterligare lagring" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Familjemedlemskap" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/ta/messages.json b/apps/web/src/locales/ta/messages.json index c97b501a082..dc3e32726d4 100644 --- a/apps/web/src/locales/ta/messages.json +++ b/apps/web/src/locales/ta/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "எண்டர்பிரைஸ் கொள்கையின் காரணமாக, உங்கள் தனிப்பட்ட வால்ட்டில் பொருட்களைச் சேமிப்பதில் இருந்து நீங்கள் கட்டுப்படுத்தப்பட்டுள்ளீர்கள். உரிமை விருப்பத்தை ஒரு நிறுவனத்திற்கு மாற்றி, கிடைக்கக்கூடிய கலெக்‌ஷன்களிலிருந்து தேர்வு செய்யவும்." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Send-ஐ அகற்று" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO இயக்கப்பட்டது" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "கீ கனெக்டர் செயல்படுத்தப்பட்டது" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "இலவச நிறுவனங்களில் 2 சேகரிப்புகள் வரை இருக்கலாம். கூடுதல் சேகரிப்புகளைச் சேர்க்க கட்டணத் திட்டத்திற்கு மேம்படுத்தவும்." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "வணிகப் பிரிவு" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 6c6c71237bb..526b7567d99 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 628544f37af..0f9c3bea24d 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Due to an Enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Remove Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO turned on" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector activated" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index 874795b9f8b..f36a336a4d1 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -60,7 +60,7 @@ "message": "Yeni hesap kaydı oluştur" }, "criticalApplicationsActivityDescription": { - "message": "Once you mark applications critical, they will display here." + "message": "Uygulamaları kritik olarak işaretlediğinizde, bunlar burada görüntülenir." }, "criticalApplicationsWithCount": { "message": "Kritik uygulamalar ($COUNT$)", @@ -72,7 +72,7 @@ } }, "countOfCriticalApplications": { - "message": "$COUNT$ critical applications", + "message": "$COUNT$ kritik uygulama", "placeholders": { "count": { "content": "$1", @@ -135,10 +135,10 @@ "message": "Riskli üyeler" }, "membersAtRiskActivityDescription": { - "message": "Members with edit access to at-risk items for critical applications" + "message": "Kritik uygulamalar için risk altındaki kayıtlara düzenleme erişimi olan üyeler" }, "membersAtRisk": { - "message": "$COUNT$ members at risk", + "message": "$COUNT$ üye risk altında", "placeholders": { "count": { "content": "$1", @@ -450,7 +450,7 @@ "message": "Kimlik doğrulama anahtarı (TOTP)" }, "totpHelperTitle": { - "message": "Make 2-step verification seamless" + "message": "2 adımlı doğrulamayı sorunsuz hale getirin" }, "totpHelper": { "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." @@ -1999,7 +1999,7 @@ "message": "Yeni bir cihazdan giriş yaptığınızda Bitwarden'ın size doğrulama e-postaları göndermesi için aşağıdan devam edin." }, "turnOffNewDeviceLoginProtectionWarning": { - "message": "With new device login protection turned off, anyone with your master password can access your account from any device. To protect your account without verification emails, set up two-step login." + "message": "Yeni cihaz oturum açma koruması kapalıyken, ana parolanızı bilen herkes herhangi bir cihazdan hesabınıza erişebilir. Doğrulama e-postaları olmadan hesabınızı korumak için iki adımlı oturum açma özelliğini ayarlayın." }, "accountNewDeviceLoginProtectionSaved": { "message": "Yeni cihaz oturum açma koruması değişiklikleri kaydedildi" @@ -5180,13 +5180,13 @@ "message": "Üyelerin diğer kuruluşlara katılmasını kısıtlayın." }, "singleOrgPolicyDesc": { - "message": "Restrict members from joining other organizations. This policy is required for organizations that have enabled domain verification." + "message": "Üyelerin diğer kuruluşlara katılmasını kısıtlayın. Bu ilke, etki alanı doğrulamasını etkinleştirmiş kuruluşlar için gereklidir." }, "singleOrgBlockCreateMessage": { "message": "Mevcut kuruluşunuzun birden fazla kuruluşa katılmanıza izin vermeyen bir ilkesi var. Lütfen kuruluş yöneticilerinizle iletişime geçin veya farklı bir Bitwarden hesabı açın." }, "singleOrgPolicyMemberWarning": { - "message": "Non-compliant members will be placed in revoked status until they leave all other organizations. Administrators are exempt and can restore members once compliance is met." + "message": "Uygun olmayan üyeler, diğer tüm kuruluşlardan ayrılana kadar üyelikleri iptal edilmiş durumuna alınacaktır. Yöneticiler bu kuralın dışında tutulur ve uygunluk sağlandığında üyelerin üyeliklerini geri yükleyebilirler." }, "requireSso": { "message": "Çoklu oturum açma kimlik doğrulamasını zorunlu tut" @@ -5515,11 +5515,11 @@ "message": "Kuruluş veri sahipliğini zorunlu kılın" }, "organizationDataOwnershipDesc": { - "message": "Require all items to be owned by an organization, removing the option to store items at the account level.", + "message": "Tüm kayıtların bir kuruluşa ait olmasını zorunlu kılın ve kayıtları hesap düzeyinde depolama seçeneğini kaldırın.", "description": "This is the policy description shown in the policy list." }, "organizationDataOwnershipContent": { - "message": "All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection be available for each member to store items. Learn more about managing the ", + "message": "Tüm kayıtlar kuruluşun mülkiyetinde olacak ve kuruluşta saklanacak, böylece kuruluş genelinde kontrol, görünürlük ve raporlama sağlanacaktır. Etkinleştirildiğinde, her üyenin kayıtları depolaması için varsayılan bir koleksiyon kullanılabilir hale gelir. Yönetme hakkında daha fazla bilgi edinin ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection be available for each member to store items. Learn more about managing the credential lifecycle.'" }, "organizationDataOwnershipContentAnchor": { @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Bir kuruluş ilkesi nedeniyle kişisel kasanıza hesap kaydetmeniz kısıtlanmış. Sahip seçeneğini bir kuruluş olarak değiştirin ve mevcut koleksiyonlar arasından seçim yapın." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Send'i sil" }, @@ -6063,7 +6070,7 @@ "message": "Uyumlu olmayan üyeler" }, "nonCompliantMembersError": { - "message": "Members that are non-compliant with the Single organization or Two-step login policy cannot be restored until they adhere to the policy requirements" + "message": "Tek kuruluş veya İki aşamalı oturum açma ilkesine uymayan üyeler, ilke gerekliliklerine uymadıkları sürece geri yüklenemezler" }, "fingerprint": { "message": "Parmak izi" @@ -6247,7 +6254,7 @@ "message": "İzin verilen uygulamalar için kullanıcıların otomatik olarak oturum açması" }, "automaticAppLoginDesc": { - "message": "Login forms will automatically be filled and submitted for apps launched from your configured identity provider." + "message": "Hesap formları, yapılandırılmış kimlik sağlayıcınızdan başlatılan uygulamalar için otomatik olarak doldurulacak ve gönderilecektir." }, "automaticAppLoginIdpHostLabel": { "message": "Kimlik sağlayıcı ana bilgisayarı" @@ -6494,10 +6501,10 @@ "message": "Sponsorlu üye olmayan ailelerin planları burada görüntülenecektir" }, "sponsorshipFreeBitwardenFamilies": { - "message": "Members of your organization are eligible for Free Bitwarden Families. You can sponsor Free Bitwarden Families for employees who are not a member of your Bitwarden organization. Sponsoring a non-member requires an available seat within your organization." + "message": "Kuruluşunuzun üyeleri Ücretsiz Bitwarden Aileler'e hak kazanır. Bitwarden kuruluşunuzun üyesi olmayan çalışanlar için Ücretsiz Bitwarden Aileler'e sponsor olabilirsiniz. Üye olmayan bir kişiye sponsor olmak için kuruluşunuzda kullanılabilir bir yer olması gerekir." }, "sponsoredFamiliesRemoveActiveSponsorship": { - "message": "When you remove an active sponsorship, a seat within your organization will be available after the renewal date of the sponsored organization." + "message": "Etkin bir sponsorluğu kaldırdığınızda, sponsorlu kuruluşun yenileme tarihinden sonra kuruluşunuzdaki bir yer boşalacaktır." }, "sponsoredFamiliesEligible": { "message": "Siz ve aileniz Ücretsiz Bitwarden Aileleri için uygunsunuz. Verilerinizi işte olmadığınızda bile güvende tutmak için kişisel e-postanızla kullanın." @@ -6524,7 +6531,7 @@ "message": "Aile planlarını kullanan üyeler burada görüntülenecektir" }, "membersWithSponsoredFamilies": { - "message": "Members of your organization are eligible for Free Bitwarden Families. Here you can see members who have sponsored a Families organization." + "message": "Kuruluşunuzun üyeleri, Ücretsiz Bitwarden Aileler'e hak kazanır. Burada, Aileler kuruluşuna sponsor olan üyeleri görebilirsiniz." }, "organizationHasMemberMessage": { "message": "Kuruluşunuzun bir üyesi olduğu için $EMAIL$ adresine sponsorluk gönderilemez.", @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO etkinleştirildi" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ çoklu oturum açma (SSO) ile giriş yapmalıdır", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector etkinleştirildi" }, @@ -6842,7 +6858,7 @@ "message": "Faturalandırma Eşitleme Anahtarı" }, "automaticBillingSyncDesc": { - "message": "Automatic sync unlocks Families sponsorships and allows you to sync your license without uploading a file. After making updates in the Bitwarden cloud server, select Sync License to apply changes." + "message": "Otomatik eşitleme, Aileler sponsorluklarını etkinleştirir ve dosya yüklemeden lisansınızı eşitlemenizi sağlar. Bitwarden bulut sunucusunda güncellemeleri yaptıktan sonra, değişiklikleri uygulamak için Lisansı Eşitle veya Lisansı Senkronize Et seçeneğini seçin." }, "active": { "message": "Aktif" @@ -8295,7 +8311,7 @@ "message": "Elle yükleme" }, "manualBillingTokenUploadDesc": { - "message": "If you do not want to opt into billing sync, manually upload your license here. This will not automatically unlock Families sponsorships." + "message": "Fatura eşitlemesini seçmek istemiyorsanız, lisansınızı buradan manuel olarak yükleyin. Bu, Aileler sponsorluklarını otomatik olarak açmaz." }, "syncLicense": { "message": "Lisansı eşitle" @@ -8367,7 +8383,7 @@ "message": "Yeni güvenlik önerilerini karşılamak ve hesap korumasını iyileştirmek için şifreleme ayarlarınızı güncelleyin." }, "kdfSettingsChangeLogoutWarning": { - "message": "Proceeding will log you out of all active sessions. You will need to log back in and complete two-step login, if any. We recommend exporting your vault before changing your encryption settings to prevent data loss." + "message": "Devam ettiğinizde tüm aktif oturumlardan çıkış yapacaksınız. Tekrar oturum açmanız ve varsa iki aşamalı oturum açma işlemini tamamlamanız gerekecektir. Veri kaybını önlemek için şifreleme ayarlarınızı değiştirmeden önce kasayı dışa aktarmanızı öneririz." }, "secretsManager": { "message": "Sır Yöneticisi" @@ -8779,7 +8795,7 @@ "message": "Erişimi etkinleştir" }, "bulkEnableSecretsManagerDescription": { - "message": "Grant the following members access to Secrets Manager. The role granted in the Password Manager will apply to Secrets Manager.", + "message": "Aşağıdaki üyelere Sır Yöneticisi için erişim izni verin. Parola Yöneticisi'nde verilen rol, Sır Yöneticisi'nde de uygulanacaktır.", "description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager." }, "activateSecretsManager": { @@ -8943,7 +8959,7 @@ } }, "secretsManagerForPlanDesc": { - "message": "For engineering and DevOps teams to manage secrets throughout the software development lifecycle." + "message": "Mühendislik ve DevOps ekiplerinin yazılım geliştirme yaşam döngüsü boyunca sırları yönetmesi için." }, "free2PersonOrganization": { "message": "Ücretsiz 2 Kişilik Kuruluşlar" @@ -9430,7 +9446,7 @@ "description": "The date header used when a subscription is past due." }, "pastDueWarningForChargeAutomatically": { - "message": "You have a grace period of $DAYS$ days from your subscription expiration date to maintain your subscription. Please resolve the past due invoices by $SUSPENSION_DATE$.", + "message": "Aboneliğinizi sürdürmek için abonelik son kullanma tarihinden itibaren $DAYS$ gün süreli bir ödemesiz döneminiz vardır. Lütfen vadesi geçmiş faturaları $SUSPENSION_DATE$ tarihine kadar ödeyiniz.", "placeholders": { "days": { "content": "$1", @@ -9444,7 +9460,7 @@ "description": "A warning shown to the user when their subscription is past due and they are charged automatically." }, "pastDueWarningForSendInvoice": { - "message": "You have a grace period of $DAYS$ days from the date your first unpaid invoice is due to maintain your subscription. Please resolve the past due invoices by $SUSPENSION_DATE$.", + "message": "Aboneliğinizi sürdürmek için, ilk ödenmemiş faturanızın vadesi geldiği tarihten itibaren $DAYS$ gün süreli bir ödemesiz döneminiz vardır. Lütfen vadesi geçmiş faturaları $SUSPENSION_DATE$ tarihine kadar ödeyiniz.", "placeholders": { "days": { "content": "$1", @@ -9639,7 +9655,7 @@ "message": "Sağlayıcıyı sil" }, "deleteProviderConfirmation": { - "message": "Deleting a provider is permanent and irreversible. Enter your master password to confirm the deletion of the provider and all associated data." + "message": "Bir sağlayıcıyı silmek kalıcı ve geri alınamaz bir işlemdir. Sağlayıcıyı ve ilgili tüm verileri silmeyi onaylamak için ana parolanızı girin." }, "deleteProviderName": { "message": "$ID$ silinemedi", @@ -9712,20 +9728,20 @@ "description": "This represents the beginning of a sentence, broken up to include links. The full sentence will be 'Configure SCIM (System for Cross-domain Identity Management) to automatically provision users and groups to Bitwarden using the implementation guide for your Identity Provider" }, "scimIntegrationDescEnd": { - "message": "(System for Cross-domain Identity Management) to automatically provision users and groups to Bitwarden using the implementation guide for your Identity Provider.", + "message": "(Etki Alanları Arası Kimlik Yönetimi Sistemi (System for Cross-domain Identity Management (SCIM))) Kimlik Sağlayıcınızın uygulama kılavuzunu kullanarak kullanıcıları ve grupları Bitwarden'e otomatik olarak ekleyin.", "description": "This represents the end of a sentence, broken up to include links. The full sentence will be 'Configure SCIM (System for Cross-domain Identity Management) to automatically provision users and groups to Bitwarden using the implementation guide for your Identity Provider" }, "bwdc": { "message": "Bitwarden Dizin Bağlayıcısı" }, "bwdcDesc": { - "message": "Configure Bitwarden Directory Connector to automatically provision users and groups using the implementation guide for your Identity Provider." + "message": "Kimlik Sağlayıcınızın uygulama kılavuzunu kullanarak kullanıcıları ve grupları otomatik olarak sağlamak için Bitwarden Dizin Bağlayıcısını yapılandırın." }, "eventManagement": { "message": "Olay yönetimi" }, "eventManagementDesc": { - "message": "Integrate Bitwarden event logs with your SIEM (system information and event management) system by using the implementation guide for your platform." + "message": "Platformunuz için uygulama kılavuzunu kullanarak Bitwarden olay günlüklerini SIEM (sistem bilgisi ve olay yönetimi) sisteminizle entegre edin." }, "deviceManagement": { "message": "Cihaz yönetimi" @@ -9839,7 +9855,7 @@ "message": "Taşıyıcı Erişim Anahtarı" }, "repositoryNameHint": { - "message": "Name of the repository to ingest into" + "message": "İçe aktarılacak deponun adı" }, "index": { "message": "İndeks" @@ -9996,7 +10012,7 @@ "message": "İzleme raporları ile güvenlik açıklarını kapatın" }, "upgradeOrganizationCloseSecurityGapsDesc": { - "message": "Stay ahead of security vulnerabilities by upgrading to a paid plan for enhanced monitoring." + "message": "Gelişmiş izleme için ücretli bir plana geçerek güvenlik açıklarının önüne geçin." }, "approveAllRequests": { "message": "Tüm istekleri onayla" @@ -10045,7 +10061,7 @@ "message": "Üye erişimini denetleyerek güvenlik risklerini belirleyin" }, "onlyAvailableForEnterpriseOrganization": { - "message": "Quickly view member access across the organization by upgrading to an Enterprise plan." + "message": "Kurumsal plana geçerek kuruluş genelinde üye erişimini hızlıca görüntüleyin." }, "date": { "message": "Tarih" @@ -10057,10 +10073,10 @@ "message": "Üye erişimi" }, "memberAccessReportDesc": { - "message": "Ensure members have access to the right credentials and their accounts are secure. Use this report to obtain a CSV of member access and account configurations." + "message": "Üyelerin doğru kimlik bilgilerine erişebildiğinden ve hesaplarının güvenli olduğundan emin olun. Bu raporu kullanarak üye erişimi ve hesap yapılandırmalarının CSV dosyasını elde edin." }, "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." + "message": "Gruplar, koleksiyonlar ve koleksiyon kayıtları genelinde denetim kuruluşu üyelerinin erişimini denetleyin. CSV dışa aktarımı, koleksiyon izinleri ve hesap yapılandırmaları hakkında bilgiler dahil olmak üzere üye başına ayrıntılı bir döküm sağlar." }, "memberAccessReportNoCollection": { "message": "(Koleksiyon yok)" @@ -10156,13 +10172,13 @@ "message": "CSV'yi indir" }, "monthlySubscriptionUserSeatsMessage": { - "message": "Adjustments to your subscription will result in prorated charges to your billing totals on your next billing period. " + "message": "Aboneliğinizde yapılan değişiklikler, bir sonraki fatura döneminizde fatura toplamınıza orantılı olarak yansıtılacaktır. " }, "annualSubscriptionUserSeatsMessage": { - "message": "Adjustments to your subscription will result in prorated charges on a monthly billing cycle. " + "message": "Aboneliğinizde yapılan değişiklikler, aylık fatura döngüsünde orantılı olarak hesaplanan ücretlere neden olacaktır. " }, "billingHistoryDescription": { - "message": "Download a CSV to obtain client details for each billing date. Prorated charges are not included in the CSV and may vary from the linked invoice. For the most accurate billing details, refer to your monthly invoices.", + "message": "Her fatura tarihi için müşteri bilgilerini almak üzere bir CSV dosyası indirin. Orantılı ücretler CSV dosyasına dahil değildir ve bağlantılı faturadan farklılık gösterebilir. En doğru fatura bilgileri için aylık faturalarınıza bakın.", "description": "A paragraph on the Billing History page of the Provider Portal letting users know they can download a CSV report for their invoices that does not include prorations." }, "noInvoicesToList": { @@ -10170,7 +10186,7 @@ "description": "A paragraph on the Billing History page of the Provider Portal letting users know they can download a CSV report for their invoices that does not include prorations." }, "providerClientVaultPrivacyNotification": { - "message": "Notice: Later this month, client vault privacy will be improved and provider members will no longer have direct access to client vault items. For questions,", + "message": "Bildirim: Bu ayın sonlarında, istemci kasası gizliliği iyileştirilecek ve sağlayıcı üyeleri artık müşteri kasası kayıtlarına doğrudan erişemeyecektir. Sorularınız için,", "description": "This will be displayed as part of a larger sentence. The whole sentence reads: 'Notice: Later this month, client vault privacy will be improved and provider members will no longer have direct access to client vault items. For questions, please contact Bitwarden support'." }, "contactBitwardenSupport": { @@ -10464,7 +10480,7 @@ "message": "Bitwarden Parola Yöneticisi" }, "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." + "message": "Ücretsiz bir yıllık Parola Yöneticisi aboneliğiniz, seçtiğiniz plana yükseltilecektir. Ücretsiz süre sona erene kadar sizden herhangi bir ücret talep edilmeyecektir." }, "fileSavedToDevice": { "message": "Dosya cihaza kaydedildi. Cihazınızın indirilenler klasöründen yönetebilirsiniz." @@ -10539,7 +10555,7 @@ "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." }, "toHostBitwardenOnYourOwnServer": { - "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + "message": "Bitwarden'ı kendi sunucunuzda barındırmak için lisans dosyanızı yüklemeniz gerekir. Kendi sunucunuzda barındırılan kuruluşunuz için Ücretsiz Aileler planlarını ve gelişmiş faturalandırma özelliklerini desteklemek için, kendi sunucunuzda barındırılan kuruluşunuzda otomatik eşitlemeyi ayarlamanız gerekir." }, "selfHostingTitleProper": { "message": "Kendi Kendinize Barındırma" @@ -10548,7 +10564,7 @@ "message": "Bir alan adının talep edilmesi tek kuruluş ilkesini etkinleştirir." }, "single-org-revoked-user-warning": { - "message": "Non-compliant members will be revoked. Administrators can restore members once they leave all other organizations." + "message": "Uygun olmayan üyeler iptal edilecektir. Yöneticiler, üyeler diğer tüm kuruluşlardan ayrıldıktan sonra onları geri yükleyebilirler." }, "deleteOrganizationUser": { "message": "$NAME$ - sil", @@ -10635,10 +10651,10 @@ "message": "Üyelerin bu kuruluş aracılığıyla bir Aile planını kullanmalarına izin vermeyin." }, "verifyBankAccountWithStatementDescriptorWarning": { - "message": "Payment with a bank account is only available to customers in the United States. You will be required to verify your bank account. We will make a micro-deposit within the next 1-2 business days. Enter the statement descriptor code from this deposit on the organization's billing page to verify the bank account. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "Banka hesabı ile ödeme yalnızca Amerika Birleşik Devletleri'ndeki müşteriler için geçerlidir. Banka hesabınızı doğrulamanız gerekecektir. Önümüzdeki 1-2 iş günü içinde mikro bir para yatırma işlemi gerçekleştireceğiz. Banka hesabını doğrulamak için kuruluşun fatura sayfasına bu para yatırma işleminin açıklama kodunu girin. Banka hesabının doğrulanmaması, ödemenin yapılmaması ve aboneliğinizin askıya alınmasıyla sonuçlanacaktır." }, "verifyBankAccountWithStatementDescriptorInstructions": { - "message": "We have made a micro-deposit to your bank account (this may take 1-2 business days). Enter the six-digit code starting with 'SM' found on the deposit description. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "Banka hesabınıza mikro bir para yatırımı yaptık (bu işlem 1-2 iş günü sürebilir). Para yatırma açıklamasında bulunan 'SM' ile başlayan altı haneli kodu girin. Banka hesabının doğrulanmaması, ödemenin yapılmaması ve aboneliğinizin askıya alınmasıyla sonuçlanacaktır." }, "descriptorCode": { "message": "Tanımlayıcı kodu" @@ -10680,7 +10696,7 @@ "message": "Otomatik Alınan Alan Adları" }, "automaticDomainClaimProcess": { - "message": "Bitwarden will attempt to claim the domain 3 times during the first 72 hours. If the domain can’t be claimed, check the DNS record in your host and manually claim. The domain will be removed from your organization in 7 days if it is not claimed." + "message": "Bitwarden, ilk 72 saat içinde etki alanını 3 kez talep etmeye çalışacaktır. Etki alanı talep edilemezse, barındırıcınızdaki DNS kaydını kontrol edin ve manuel olarak talep edin. Etki alanı talep edilmezse, 7 gün içinde kuruluşunuzdan kaldırılacaktır." }, "domainNotClaimed": { "message": "$DOMAIN$ alınmadı. DNS kayıtlarınızı kontrol edin.", @@ -10698,7 +10714,7 @@ "message": "Doğrulama altında" }, "claimedDomainsDescription": { - "message": "Claim a domain to own member accounts. The SSO identifier page will be skipped during login for members with claimed domains and administrators will be able to delete claimed accounts." + "message": "Üye hesaplarını sahip olmak için bir alan adı talep edin. Alan adı talep eden üyelerin oturum açma sırasında SSO tanımlayıcı sayfası atlanacak ve yöneticiler talep edilen hesapları silebilecek." }, "invalidDomainNameClaimMessage": { "message": "Giriş geçerli bir format değil. Biçim: mydomain.com. Alt alan adlarının alınması için ayrı girişler gerekir." @@ -10845,7 +10861,7 @@ "description": "This will be used as part of a larger sentence, broken up to include the Bitwarden icon. The full sentence will read 'We had trouble opening the Bitwarden browser extension. Open the Bitwarden icon [Bitwarden Icon] from the toolbar.'" }, "resellerRenewalWarningMsg": { - "message": "Your subscription will renew soon. To ensure uninterrupted service, contact $RESELLER$ to confirm your renewal before $RENEWAL_DATE$.", + "message": "Aboneliğiniz yakında yenilenecektir. Kesintisiz hizmet için, $RENEWAL_DATE$ tarihinden önce $RESELLER$ ile iletişime geçerek yenileme işleminizi onaylayın.", "placeholders": { "reseller": { "content": "$1", @@ -10858,7 +10874,7 @@ } }, "resellerOpenInvoiceWarningMgs": { - "message": "An invoice for your subscription was issued on $ISSUED_DATE$. To ensure uninterrupted service, contact $RESELLER$ to confirm your renewal before $DUE_DATE$.", + "message": "Aboneliğiniz için fatura $ISSUED_DATE$ tarihinde düzenlenmiştir. Hizmetin kesintisiz devam etmesi için, $DUE_DATE$ tarihinden önce $RESELLER$ ile iletişime geçerek yenileme işleminizi onaylatın.", "placeholders": { "reseller": { "content": "$1", @@ -10875,7 +10891,7 @@ } }, "resellerPastDueWarningMsg": { - "message": "The invoice for your subscription has not been paid. To ensure uninterrupted service, contact $RESELLER$ to confirm your renewal before $GRACE_PERIOD_END$.", + "message": "Aboneliğinizin faturası ödenmemiştir. Hizmetin kesintisiz devam etmesi için, $RESELLER$ ile iletişime geçerek $GRACE_PERIOD_END$ tarihinden önce yenileme işleminizi onaylatın.", "placeholders": { "reseller": { "content": "$1", @@ -10903,10 +10919,10 @@ } }, "accountDeprovisioningNotification": { - "message": "Administrators now have the ability to delete member accounts that belong to a claimed domain." + "message": "Yöneticiler artık talep edilen bir etki alanına ait üye hesaplarını silme yetkisine sahiptir." }, "deleteManagedUserWarningDesc": { - "message": "This action will delete the member account including all items in their vault. This replaces the previous Remove action." + "message": "Bu işlem, üye hesabını ve kasasındaki tüm kayıtları siler. Bu, önceki Kaldır işleminin yerini alır." }, "deleteManagedUserWarning": { "message": "Silme yeni bir eylemdir!" @@ -10970,7 +10986,7 @@ } }, "userkeyRotationDisclaimerDescription": { - "message": "Rotating your encryption keys will require you to trust keys of any organizations that can recover your account, and any contacts that you have enabled emergency access for. To continue, make sure you can verify the following:" + "message": "Şifreleme anahtarlarınızı değiştirmek için, hesabınızı kurtarabilecek tüm kuruluşların anahtarlarına ve acil durum erişimi etkinleştirdiğiniz tüm kişilere güvenmeniz gerekir. Devam etmek için aşağıdakileri doğruladığınızdan emin olun:" }, "userkeyRotationDisclaimerTitle": { "message": "Güvenilmeyen şifreleme anahtarları" @@ -10991,7 +11007,7 @@ "message": "Üyelerin hesaplarının kilidini PIN ile açmalarına izin vermeyin." }, "upgradeForFullEventsMessage": { - "message": "Event logs are not stored for your organization. Upgrade to a Teams or Enterprise plan to get full access to organization event logs." + "message": "Olay günlükleri kuruluşunuz için depolanmaz. Kuruluş olay günlüklerine tam erişim elde etmek için Ekip veya Kurumsal planına yükseltin." }, "upgradeEventLogTitleMessage": { "message": "Kuruluşunuzdaki olay günlüklerini görmek için yükseltin." @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Ücretsiz kuruluşların en fazla 2 koleksiyonu olabilir. Daha fazla koleksiyon eklemek için ücretli bir plana geçin." }, + "searchArchive": { + "message": "Arşivde ara" + }, + "archive": { + "message": "Arşiv" + }, + "noItemsInArchive": { + "message": "Arşivde kayıt yok" + }, + "archivedItemsDescription": { + "message": "Arşivlenen kayıtlar burada görünecek ve genel arama sonuçlarından ve otomatik doldurma önerilerinden hariç tutulacaktır." + }, "businessUnit": { "message": "İş Birimi" }, @@ -11137,13 +11165,13 @@ "message": "Yeniden başlat" }, "verifyProviderBankAccountWithStatementDescriptorWarning": { - "message": "Payment with a bank account is only available to customers in the United States. You will be required to verify your bank account. We will make a micro-deposit within the next 1-2 business days. Enter the statement descriptor code from this deposit on the provider's subscription page to verify the bank account. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "Banka hesabı ile ödeme yalnızca Amerika Birleşik Devletleri'ndeki müşteriler için geçerlidir. Banka hesabınızı doğrulamanız gerekecektir. Önümüzdeki 1-2 iş günü içinde mikro bir para yatırma işlemi gerçekleştireceğiz. Banka hesabını doğrulamak için, sağlayıcının abonelik sayfasına bu para yatırma işleminin ekstre tanımlama kodunu girin. Banka hesabının doğrulanmaması, ödemenin yapılmaması ve aboneliğinizin askıya alınmasıyla sonuçlanacaktır." }, "clickPayWithPayPal": { "message": "Ödeme yönteminizi eklemek için lütfen PayPal ile Öde düğmesine tıklayın." }, "revokeActiveSponsorshipConfirmation": { - "message": "If you remove $EMAIL$, the sponsorship for this Family plan will end. A seat within your organization will become available for members or sponsorships after the sponsored organization renewal date on $DATE$.", + "message": "$EMAIL$ adresini kaldırırsanız, bu Aile planının sponsorluğu sona erecektir. $DATE$ tarihinde sponsorlu kuruluşun yenileme tarihinden sonra, kuruluşunuzdaki bir yer üyeler veya sponsorluklar için kullanılabilir hale gelecektir.", "placeholders": { "email": { "content": "$1", @@ -11202,7 +11230,7 @@ "message": "CVV veya CVC olarak da bilinen kart güvenlik kodu, tipik olarak kredi kartınızın arkasında basılı 3 haneli bir sayı veya kart numaranızın üzerinde ön tarafta basılı 4 haneli bir sayıdır." }, "verifyBankAccountWarning": { - "message": "Payment with a bank account is only available to customers in the United States. You will be required to verify your bank account. We will make a micro-deposit within the next 1-2 business days. Enter the statement descriptor code from this deposit on the Payment Details page to verify the bank account. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "Banka hesabı ile ödeme yalnızca Amerika Birleşik Devletleri'ndeki müşteriler için geçerlidir. Banka hesabınızı doğrulamanız gerekecektir. Önümüzdeki 1-2 iş günü içinde mikro bir para yatırma işlemi gerçekleştireceğiz. Banka hesabını doğrulamak için Ödeme Ayrıntıları sayfasına bu para yatırma işleminin ekstre tanımlama kodunu girin. Banka hesabının doğrulanmaması, ödemenin kaçırılmasına ve aboneliğinizin askıya alınmasına neden olacaktır." }, "taxId": { "message": "Vergi Kimliği: $TAX_ID$", @@ -11217,7 +11245,7 @@ "message": "Ödenmemiş faturalar" }, "unpaidInvoicesForServiceUser": { - "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.", + "message": "Aboneliğiniz ödenmemiştir. Hizmetin size ve müşterilerinize yeniden sağlanması için sağlayıcı yöneticinizle iletişime geçin.", "description": "A message shown in a non-dismissible dialog to service users of unpaid providers." }, "providerSuspended": { @@ -11230,11 +11258,11 @@ } }, "restoreProviderPortalAccessViaCustomerSupport": { - "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.", + "message": "Sağlayıcı portalına erişiminizi geri yüklemek için Bitwarden Müşteri Desteği ile iletişime geçerek aboneliğinizi yenileyin.", "description": "A message shown in a non-dismissible dialog to any user of a suspended providers." }, "restoreProviderPortalAccessViaPaymentMethod": { - "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.", + "message": "Aboneliğiniz ödenmemiştir. Size ve müşterilerinize hizmeti geri yüklemek için, $CANCELLATION_DATE$ tarihine kadar bir ödeme yöntemi ekleyin.", "placeholders": { "cancellation_date": { "content": "$1", @@ -11280,7 +11308,7 @@ "message": "Vergi Kimliği Eksik" }, "missingTaxIdWarning": { - "message": "Action required: You're missing a Tax ID number in payment details. If a Tax ID is not added, your invoices may include additional tax." + "message": "İşlem gerekli: Ödeme ayrıntılarında vergi kimlik numarası eksik. Vergi kimlik numarası eklenmezse, faturalarınızda ek vergi uygulanabilir." }, "moreBreadcrumbs": { "message": "Daha fazla gezinme izi", @@ -11330,12 +11358,144 @@ "message": "Key Connector alan adını doğrulayın" }, "requiredToVerifyBankAccountWithStripe": { - "message": "Payment with a bank account is only available to customers in the United States. You will be required to verify your bank account. We will make a micro-deposit within the next 1-2 business days. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "Banka hesabı ile ödeme yalnızca Amerika Birleşik Devletleri'ndeki müşteriler için geçerlidir. Banka hesabınızı doğrulamanız gerekecektir. Önümüzdeki 1-2 iş günü içinde mikro bir para yatırma işlemi gerçekleştireceğiz. Banka hesabının doğrulanmaması, ödemenin kaçırılmasına ve aboneliğinizin askıya alınmasına neden olacaktır." }, "verifyBankAccountWithStripe": { - "message": "We have made a micro-deposit to your bank account. This may take 1-2 business days. When you see the deposit in your account, you can verify your bank account. Failure to verify your bank account will result in a missed payment and your subscription will be suspended." + "message": "Banka hesabınıza mikro bir para yatırımı yaptık. Bu işlem 1-2 iş günü sürebilir. Hesabınızda para yatırımı gördüğünüzde, banka hesabınızı doğrulayabilirsiniz. Banka hesabınızı doğrulamamanız durumunda ödeme yapılmayacak ve aboneliğiniz askıya alınacaktır." }, "verifyNow": { "message": "Şimdi doğrulayın." + }, + "additionalStorageGB": { + "message": "Ek depolama alanı GB" + }, + "additionalServiceAccountsV2": { + "message": "İlave makine hesapları" + }, + "secretsManagerSeats": { + "message": "Sır Yöneticisi yerleri" + }, + "additionalStorage": { + "message": "Ek depolama" + }, + "expandPurchaseDetails": { + "message": "Satın alma ayrıntılarını genişlet" + }, + "collapsePurchaseDetails": { + "message": "Satın alma ayrıntılarını daralt" + }, + "familiesMembership": { + "message": "Aile üyeliği" + }, + "planDescPremium": { + "message": "Tam çevrimiçi güvenlik" + }, + "planDescFamiliesV2": { + "message": "Aileniz için Premium güvenlik" + }, + "planDescFreeV2": { + "message": "$COUNT$ diğer kullanıcıyla paylaş", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Herhangi bir kuruluş için gelişmiş yetenekler" + }, + "planNameCustom": { + "message": "Özel plan" + }, + "planDescCustom": { + "message": "Bitwarden, her büyüklükteki işletmeyle uyumlu olarak parolaları ve hassas bilgileri güvence altına alır. Büyük kurumsal bir işletmenin parçasıysanız, satış ekibiyle iletişime geçerek fiyat teklifi isteyin." + }, + "builtInAuthenticator": { + "message": "Dahili kimlik doğrulayıcı" + }, + "breachMonitoring": { + "message": "İhlal izleme" + }, + "andMoreFeatures": { + "message": "Ve daha fazlası!" + }, + "secureFileStorage": { + "message": "Güvenli dosya depolama" + }, + "familiesUnlimitedSharing": { + "message": "Sınırsız paylaşım - kimin neyi göreceğini seçin" + }, + "familiesUnlimitedCollections": { + "message": "Sınırsız aile koleksiyonları" + }, + "familiesSharedStorage": { + "message": "Önemli aile bilgileri için paylaşılmış depolama" + }, + "limitedUsersV2": { + "message": "$COUNT$ üyeye kadar", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "$COUNT$ koleksiyona kadar", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Her zaman ücretsiz" + }, + "twoSecretsIncluded": { + "message": "2 sır" + }, + "projectsIncludedV2": { + "message": "$COUNT$ proje", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Güvenli kayıt paylaşımı" + }, + "scimSupport": { + "message": "SCIM desteği" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ makine hesabı", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Kurumsal güvenlik ilkeleri" + }, + "selfHostOption": { + "message": "Kendi sunucunuzda barındırma seçeneği" + }, + "complimentaryFamiliesPlan": { + "message": "Tüm kullanıcılar için ücretsiz aile planı" + }, + "strengthenCybersecurity": { + "message": "Siber güvenliği güçlendirin" + }, + "boostProductivity": { + "message": "Verimliliği artırın" + }, + "seamlessIntegration": { + "message": "Sorunsuz entegrasyon" } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index df20e2db5ee..c79e6460c33 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "У зв'язку з політикою компанії, вам не дозволено зберігати записи до особистого сховища. Змініть налаштування власності на організацію та виберіть серед доступних збірок." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Вилучити відправлення" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO вимкнено" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector увімкнено" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Безплатні організації можуть мати до 2 збірок. Передплатіть тарифний план, щоб додати більше збірок." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Бізнес-підрозділ" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 4367d3a04eb..155a08a76be 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "Do chính sách của doanh nghiệp, bạn không thể lưu trữ các mục vào kho cá nhân của mình. Hãy thay đổi tùy chọn Quyền sở hữu thành tổ chức và chọn từ các bộ sưu tập có sẵn." }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "Xóa Send" }, @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO đã được bật" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector đã được kích hoạt" }, @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Các tổ chức miễn phí có thể có tối đa 2 bộ sưu tập. Nâng cấp lên gói trả phí để thêm nhiều bộ sưu tập hơn." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Bộ phận kinh doanh" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Xác minh ngay." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 6ff2caaa5d9..423290706e0 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -1432,7 +1432,7 @@ "message": "账户创建成功。" }, "masterPassSent": { - "message": "我们已经为您发送了包含主密码提示的电子邮件。" + "message": "我们已经向您发送了一封包含主密码提示的电子邮件。" }, "unexpectedError": { "message": "发生意外错误。" @@ -2188,7 +2188,7 @@ "message": "更改网页密码库的语言。" }, "showIconsChangePasswordUrls": { - "message": "显示网站图标并检索更改密码的 URL" + "message": "显示网站图标并获取更改密码的 URL" }, "default": { "message": "默认" @@ -2579,7 +2579,7 @@ "message": "未激活两步登录" }, "inactive2faReportDesc": { - "message": "两步登录为您的账户增加了一层保护。使用 Bitwarden 验证器或其他方式为这些账户开启两步登录。" + "message": "两步登录为您的账户增加了一层保护。使用 Bitwarden Authenticator 或其他方式为这些账户开启两步登录。" }, "inactive2faFound": { "message": "发现未启用两步登录的登录项目" @@ -2771,7 +2771,7 @@ "message": "计费" }, "billingPlanLabel": { - "message": "计费计划" + "message": "计费方案" }, "paymentType": { "message": "付款类型" @@ -2855,7 +2855,7 @@ } }, "bitwardenFamiliesPlan": { - "message": "Bitwarden 家庭计划。" + "message": "Bitwarden 家庭方案。" }, "addons": { "message": "附加项目" @@ -2883,7 +2883,7 @@ "message": "# GB 附加存储" }, "additionalStorageIntervalDesc": { - "message": "您的计划包含 $SIZE$ 的加密存储空间。您也可以以每 GB $PRICE$ /$INTERVAL$ 购买附加存储。", + "message": "您的方案包含 $SIZE$ 加密文件存储空间。您也可以以每 GB $PRICE$ /$INTERVAL$ 购买附加存储。", "placeholders": { "size": { "content": "$1", @@ -2940,7 +2940,7 @@ "message": "任何未付费订阅都将通过您的付款方式收取费用。" }, "paymentChargedWithTrial": { - "message": "您的计划包含了 7 天的免费试用。在试用期结束前,不会从您的付款方式中扣款。您可以随时取消。" + "message": "您的方案包含了 7 天的免费试用。在试用期结束前,不会从您的付款方式中扣款。您可以随时取消。" }, "paymentInformation": { "message": "支付信息" @@ -3162,7 +3162,7 @@ "message": "公司名称" }, "chooseYourPlan": { - "message": "选择您的计划" + "message": "选择您的方案" }, "users": { "message": "用户" @@ -3177,7 +3177,7 @@ "message": "# 用户席位" }, "userSeatsAdditionalDesc": { - "message": "您的计划包含 $BASE_SEATS$ 个用户席位。您也可以以每用户 $SEAT_PRICE$ /月购买附加用户。", + "message": "您的方案包含 $BASE_SEATS$ 个用户席位。您也可以以每用户 $SEAT_PRICE$ /月购买附加用户。", "placeholders": { "base_seats": { "content": "$1", @@ -3193,7 +3193,7 @@ "message": "您需要多少个用户席位?您也可以在以后需要的时候添加附加席位。" }, "planNameFree": { - "message": "免费", + "message": "免费版", "description": "Free as in 'free beer'." }, "planDescFree": { @@ -3206,7 +3206,7 @@ } }, "planNameFamilies": { - "message": "家庭" + "message": "家庭版" }, "planDescFamilies": { "message": "适用于个人使用,与家人和朋友共享。" @@ -3323,7 +3323,7 @@ } }, "trialThankYou": { - "message": "感谢您注册适用于 $PLAN$ 的 Bitwarden!", + "message": "感谢您注册 $PLAN$ Bitwarden!", "placeholders": { "plan": { "content": "$1", @@ -3332,7 +3332,7 @@ } }, "trialSecretsManagerThankYou": { - "message": "感谢您注册适用于 $PLAN$ 的 Bitwarden 机密管理器!", + "message": "感谢您注册 $PLAN$ Bitwarden 机密管理器!", "placeholders": { "plan": { "content": "$1", @@ -4382,15 +4382,15 @@ "message": "对于美国境内的客户,需要提供邮政编码以满足销售税要求。对于其他国家,您可以选择提供一个税号 (VAT/GST) 和/或地址来显示在您的账单上。" }, "billingPlan": { - "message": "计划", + "message": "方案", "description": "A billing plan/package. For example: Families, Teams, Enterprise, etc." }, "changeBillingPlan": { - "message": "升级计划", + "message": "升级方案", "description": "A billing plan/package. For example: Families, Teams, Enterprise, etc." }, "changeBillingPlanUpgrade": { - "message": "要将您的账户升级到另一个计划,请提供以下信息。同时请确保您账户已添加有一个有效的付款方式。", + "message": "要将您的账户升级到另一个方案,请提供以下信息。同时请确保您账户已添加有一个有效的付款方式。", "description": "A billing plan/package. For example: Families, Teams, Enterprise, etc." }, "invoiceNumber": { @@ -4529,7 +4529,7 @@ } }, "subscriptionUserSeatsWithoutAdditionalSeatsOption": { - "message": "您最多可邀请 $COUNT$ 名成员,而无需额外付费。要升级您的计划并邀请更多成员,请联系客户支持。", + "message": "您最多可邀请 $COUNT$ 名成员,而无需额外付费。要升级您的方案并邀请更多成员,请联系客户支持。", "placeholders": { "count": { "content": "$1", @@ -4538,7 +4538,7 @@ } }, "subscriptionFreePlan": { - "message": "如果不升级您的计划,您最多只能邀请 $COUNT$ 位成员。", + "message": "如果不升级您的方案,您最多只能邀请 $COUNT$ 位成员。", "placeholders": { "count": { "content": "$1", @@ -4547,7 +4547,7 @@ } }, "subscriptionUpgrade": { - "message": "如果不升级您的计划,您最多只能邀请 $COUNT$ 位成员。", + "message": "如果不升级您的方案,您最多只能邀请 $COUNT$ 位成员。", "placeholders": { "count": { "content": "$1", @@ -4556,7 +4556,7 @@ } }, "subscriptionSponsoredFamiliesPlan": { - "message": "您的订阅一共允许 $COUNT$ 位成员。您的计划由一个外部组织赞助和支付费用。", + "message": "您的订阅一共允许 $COUNT$ 位成员。您的方案由一个外部组织赞助和支付费用。", "placeholders": { "count": { "content": "$1", @@ -4677,7 +4677,7 @@ "message": "升级组织" }, "upgradeOrganizationDesc": { - "message": "此功能不适用于免费组织。请切换到付费计划以解锁更多功能。" + "message": "此功能不适用于免费组织。请切换到付费方案以解锁更多功能。" }, "createOrganizationStep1": { "message": "创建组织:第一步" @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "由于某个企业策略,您不能将项目保存到您的个人密码库。请将所有权选项更改为组织,然后选择可用的集合。" }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "禁用 Send" }, @@ -5600,7 +5607,7 @@ } }, "planPrice": { - "message": "计划价格" + "message": "方案价格" }, "estimatedTax": { "message": "预估税额" @@ -5624,7 +5631,7 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'" }, "customNonEnterpriseError": { - "message": "要启用自定义权限,该组织必须处于 2020 企业计划中。" + "message": "要启用自定义权限,该组织必须处于 2020 企业方案中。" }, "permissions": { "message": "权限" @@ -5736,10 +5743,10 @@ "message": "使用端到端加密保护机密。而不再需要硬编码机密或通过 .env 文件分享机密。" }, "enhanceDeveloperProductivity": { - "message": "提高开发人员的生产力。" + "message": "提升开发人员的工作效率。" }, "enhanceDeveloperProductivityDescription": { - "message": "程序化地在运行时检索和部署机密,使开发人员可以专注于最重要的事情,例如提高代码质量。" + "message": "程序化地在运行时获取和部署机密,使开发人员可以专注于最重要的事情,例如提高代码质量。" }, "strengthenBusinessSecurity": { "message": "加强企业安全。" @@ -6491,7 +6498,7 @@ "message": "没有赞助的家庭" }, "nosponsoredFamiliesDetails": { - "message": "已赞助的非成员家庭计划将显示在这里" + "message": "已赞助的非成员家庭方案将显示在这里" }, "sponsorshipFreeBitwardenFamilies": { "message": "您的组织成员有资格获得免费的 Bitwarden 家庭计划。您可以为不是您的 Bitwarden 组织成员的员工赞助免费 Bitwarden 家庭。赞助非成员需要您的组织内有可用的席位。" @@ -6503,16 +6510,16 @@ "message": "您和您的家人有资格获得免费的 Bitwarden 家庭版计划。使用您的个人电子邮箱兑换,即使您不在工作中,也能确保您的数据安全。" }, "sponsoredFamiliesEligibleCard": { - "message": "立即兑换免费的 Bitwarden 家庭计划,即使您不在工作中,也能确保您的数据安全。" + "message": "立即兑换免费的 Bitwarden 家庭方案,即使您不在工作中,也能确保您的数据安全。" }, "sponsoredFamiliesIncludeMessage": { - "message": "Bitwarden 家庭计划包含" + "message": "Bitwarden 家庭方案包含" }, "sponsoredFamiliesPremiumAccess": { "message": "最多 6 个用户的高级访问权限" }, "sponsoredFamiliesSharedCollectionsForFamilyMembers": { - "message": "适用于家庭成员的共享集合" + "message": "为家庭成员提供共享集合" }, "memberFamilies": { "message": "成员家庭" @@ -6521,7 +6528,7 @@ "message": "没有成员家庭" }, "noMemberFamiliesDescription": { - "message": "已兑换家庭计划的成员将在这里显示" + "message": "已兑换家庭方案的成员将在这里显示" }, "membersWithSponsoredFamilies": { "message": "您的组织成员有资格获得免费的 Bitwarden 家庭计划。在这里,您可以看到已赞助了家庭组织的成员。" @@ -6539,7 +6546,7 @@ "message": "链接已失效。请让赞助方重新发送邀请。" }, "reclaimedFreePlan": { - "message": "收回了免费计划" + "message": "收回了免费方案" }, "redeem": { "message": "兑换" @@ -6560,7 +6567,7 @@ "message": "接受现有组织的邀请或创建一个新的家庭组织。" }, "setupSponsoredFamiliesLoginDesc": { - "message": "您已被邀请加入免费的 Bitwarden 家庭计划组织。要继续,您需要登录到接收邀请的账户。" + "message": "您已被邀请加入免费的 Bitwarden 家庭方案组织。要继续,您需要登录到接收邀请的账户。" }, "sponsoredFamiliesAcceptFailed": { "message": "无法接受邀请。请通过您的企业账户重新发送邀请邮件,然后重试。" @@ -6605,7 +6612,7 @@ } }, "freeFamiliesPlan": { - "message": "免费家庭计划" + "message": "免费家庭方案" }, "redeemNow": { "message": "立即兑换" @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "SSO 已关闭" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ 必须使用单点登录", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "Key Connector 已启用" }, @@ -6830,7 +6846,7 @@ "message": "自托管" }, "selfHostingEnterpriseOrganizationSectionCopy": { - "message": "要在您自己的服务器上设置您的组织,您需要上传您的许可证文件。要为您的自托管组织提供免费家庭计划和高级计费功能,您需要设置计费同步。" + "message": "要在您自己的服务器上设置您的组织,您需要上传您的许可证文件。要为您的自托管组织提供免费家庭方案和高级计费功能,您需要设置计费同步。" }, "billingSyncApiKeyRotated": { "message": "令牌已轮换" @@ -8190,7 +8206,7 @@ "message": "切换产品" }, "freeOrgInvLimitReachedManageBilling": { - "message": "免费组织最多拥有 $SEATCOUNT$ 位成员。要邀请更多成员,请升级到付费计划。", + "message": "免费组织最多拥有 $SEATCOUNT$ 位成员。要邀请更多成员,请升级到付费方案。", "placeholders": { "seatcount": { "content": "$1", @@ -8208,7 +8224,7 @@ } }, "teamsStarterPlanInvLimitReachedManageBilling": { - "message": "团队入门版计划最多拥有 $SEATCOUNT$ 位成员。要邀请更多成员,请升级您的计划。", + "message": "团队入门版方案最多拥有 $SEATCOUNT$ 位成员。要邀请更多成员,请升级您的方案。", "placeholders": { "seatcount": { "content": "$1", @@ -8217,7 +8233,7 @@ } }, "teamsStarterPlanInvLimitReachedNoManageBilling": { - "message": "团队入门版计划最多拥有 $SEATCOUNT$ 位成员。要升级您的计划并邀请更多成员,请联系您的组织所有者。", + "message": "团队入门版方案最多拥有 $SEATCOUNT$ 位成员。要升级您的方案并邀请更多成员,请联系您的组织所有者。", "placeholders": { "seatcount": { "content": "$1", @@ -8226,7 +8242,7 @@ } }, "freeOrgMaxCollectionReachedManageBilling": { - "message": "免费组织最多拥有 $COLLECTIONCOUNT$ 个集合。要添加更多集合,请升级到付费计划。", + "message": "免费组织最多拥有 $COLLECTIONCOUNT$ 个集合。要添加更多集合,请升级到付费方案。", "placeholders": { "COLLECTIONCOUNT": { "content": "$1", @@ -8934,7 +8950,7 @@ "message": "邀请用户" }, "secretsManagerForPlan": { - "message": "适用于 $PLAN$ 的机密管理器", + "message": "$PLAN$ 机密管理器", "placeholders": { "plan": { "content": "$1", @@ -8985,13 +9001,13 @@ "message": "订阅机密管理器" }, "addSecretsManagerUpgradeDesc": { - "message": "将机密管理器添加到升级后的计划中,以保留对使用之前的计划创建的所有机密的访问权限。" + "message": "将机密管理器添加到升级后的方案中,以保留对使用之前的方案创建的所有机密的访问权限。" }, "additionalServiceAccounts": { "message": "附加服务账户" }, "includedServiceAccounts": { - "message": "您的计划包含 $COUNT$ 个服务账户。", + "message": "您的方案包含 $COUNT$ 个服务账户。", "placeholders": { "count": { "content": "$1", @@ -9035,10 +9051,10 @@ "message": "更新了集合管理设置" }, "passwordManagerPlanPrice": { - "message": "密码管理器计划价格" + "message": "密码管理器方案价格" }, "secretsManagerPlanPrice": { - "message": "机密管理器计划价格" + "message": "机密管理器方案价格" }, "passwordManager": { "message": "密码管理器" @@ -9600,7 +9616,7 @@ "message": "附加机器账户" }, "includedMachineAccounts": { - "message": "您的计划包含 $COUNT$ 个机器账户。", + "message": "您的方案包含 $COUNT$ 个机器账户。", "placeholders": { "count": { "content": "$1", @@ -9845,7 +9861,7 @@ "message": "索引" }, "selectAPlan": { - "message": "选择一个计划" + "message": "选择一个方案" }, "thirtyFivePercentDiscount": { "message": "35% 折扣" @@ -9993,10 +10009,10 @@ "message": "保护您的家庭或企业" }, "upgradeOrganizationCloseSecurityGaps": { - "message": "通过监控报告弥补安全漏洞" + "message": "通过监测报告弥补安全漏洞" }, "upgradeOrganizationCloseSecurityGapsDesc": { - "message": "升级到付费计划以加强监控,从而提前发现安全漏洞。" + "message": "升级到付费方案以加强监测,从而提前发现安全漏洞。" }, "approveAllRequests": { "message": "批准所有请求" @@ -10045,7 +10061,7 @@ "message": "通过审计成员访问权限来识别安全风险" }, "onlyAvailableForEnterpriseOrganization": { - "message": "通过升级为企业版计划,快速查看整个组织的成员访问权限。" + "message": "通过升级为企业版方案,快速查看整个组织的成员访问权限。" }, "date": { "message": "日期" @@ -10329,7 +10345,7 @@ "message": "SSO 身份验证" }, "familiesPlanInvLimitReachedManageBilling": { - "message": "家庭组织最多拥有 $SEATCOUNT$ 位成员。要邀请更多成员,请升级到付费计划。", + "message": "家庭组织最多拥有 $SEATCOUNT$ 位成员。要邀请更多成员,请升级到付费方案。", "placeholders": { "seatcount": { "content": "$1", @@ -10347,7 +10363,7 @@ } }, "upgradePlans": { - "message": "升级您的计划以邀请成员并体验强大的安全功能。" + "message": "升级您的方案以邀请成员并体验强大的安全功能。" }, "upgradeDiscount": { "message": "节省 $AMOUNT$%", @@ -10359,7 +10375,7 @@ } }, "enterprisePlanUpgradeMessage": { - "message": "适用于大型组织的高级功能" + "message": "为大型组织提供高级功能" }, "teamsPlanUpgradeMessage": { "message": "为成长中的团队提供弹性保护" @@ -10458,13 +10474,13 @@ "message": "当前" }, "secretsManagerSubscriptionInfo": { - "message": "您的机密管理器订阅将基于选择的计划升级" + "message": "您的机密管理器订阅将基于选择的方案升级" }, "bitwardenPasswordManager": { "message": "Bitwarden 密码管理器" }, "secretsManagerComplimentaryPasswordManager": { - "message": "您的一年免费密码管理器订阅将升级到所选计划。在免费期结束前,我们不会向您收取费用。" + "message": "您的一年免费密码管理器订阅将升级到所选方案。在免费期结束前,我们不会向您收取费用。" }, "fileSavedToDevice": { "message": "文件已保存到设备。可以在设备下载中进行管理。" @@ -10539,7 +10555,7 @@ "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." }, "toHostBitwardenOnYourOwnServer": { - "message": "要在您自己的服务器上托管 Bitwarden,您需要上传许可证文件。要支持自托管组织的免费家庭计划和高级计费功能,您需要在自托管组织中设置自动同步。" + "message": "要在您自己的服务器上托管 Bitwarden,您需要上传许可证文件。要支持自托管组织的免费家庭版方案和高级计费功能,您需要在自托管组织中设置自动同步。" }, "selfHostingTitleProper": { "message": "自托管" @@ -10632,7 +10648,7 @@ "message": "禁用免费 Bitwarden 家庭赞助" }, "freeFamiliesSponsorshipPolicyDesc": { - "message": "不允许成员通过此组织兑换家庭计划。" + "message": "不允许成员通过此组织兑换家庭版方案。" }, "verifyBankAccountWithStatementDescriptorWarning": { "message": "使用银行账户付款仅对美国用户开放。您将被要求验证您的银行账户。我们将在 1-2 个工作日内进行一笔小额转账,请在组织的计费页面输入该转账的对账单描述符代码以验证银行账户。验证银行账户失败将会错过支付,您的订阅将暂停。" @@ -10722,7 +10738,7 @@ } }, "updatedRevokeSponsorshipConfirmationForSentSponsorship": { - "message": "如果您移除 $EMAIL$,将无法兑换此家庭计划赞助。确定要继续吗?", + "message": "如果您移除 $EMAIL$,将无法兑换此家庭版方案赞助。确定要继续吗?", "placeholders": { "email": { "content": "$1", @@ -10731,7 +10747,7 @@ } }, "updatedRevokeSponsorshipConfirmationForAcceptedSponsorship": { - "message": "如果您移除 $EMAIL$,此家庭计划赞助将终止,并且将于 $DATE$ 向已保存的付款方式收取 $40 + 相关税费。在 $DATE$ 之前您将无法兑换新的赞助。确定要继续吗?", + "message": "如果您移除 $EMAIL$,此家庭版方案赞助将终止,并且将于 $DATE$ 向已保存的付款方式收取 $40 + 相关税费。在 $DATE$ 之前您将无法兑换新的赞助。确定要继续吗?", "placeholders": { "email": { "content": "$1", @@ -10991,7 +11007,7 @@ "message": "不允许成员使用 PIN 码解锁他们的账户。" }, "upgradeForFullEventsMessage": { - "message": "不会为您的组织存储事件日志。升级到团队计划或企业计划以获取组织事件日志的完整访问权限。" + "message": "不会为您的组织存储事件日志。升级到团队版或企业版方案以获取组织事件日志的完整访问权限。" }, "upgradeEventLogTitleMessage": { "message": "升级以查看您组织中的事件日志。" @@ -11003,7 +11019,19 @@ "message": "查看事件" }, "cannotCreateCollection": { - "message": "免费组织最多拥有 2 个集合。要添加更多集合,请升级到付费计划。" + "message": "免费组织最多拥有 2 个集合。要添加更多集合,请升级到付费方案。" + }, + "searchArchive": { + "message": "搜索归档" + }, + "archive": { + "message": "归档" + }, + "noItemsInArchive": { + "message": "归档中没有项目" + }, + "archivedItemsDescription": { + "message": "已归档的项目将显示在此处,并将被排除在一般搜索结果和自动填充建议之外。" }, "businessUnit": { "message": "业务单元" @@ -11143,7 +11171,7 @@ "message": "请点击「使用 PayPal 付款」按钮以添加您的付款方式。" }, "revokeActiveSponsorshipConfirmation": { - "message": "如果您移除 $EMAIL$,此家庭计划的赞助将结束。在被赞助组织的续费日期 $DATE$ 之后,您的组织中将释放一个可用席位,可供成员或赞助使用。", + "message": "如果您移除 $EMAIL$,此家庭版方案赞助将终止。在被赞助组织的续费日期 $DATE$ 之后,您的组织中将释放一个可用席位,可供成员或赞助使用。", "placeholders": { "email": { "content": "$1", @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": " 立即验证。" + }, + "additionalStorageGB": { + "message": "附加存储 GB" + }, + "additionalServiceAccountsV2": { + "message": "附加机器账户" + }, + "secretsManagerSeats": { + "message": "机密管理器席位" + }, + "additionalStorage": { + "message": "附加存储" + }, + "expandPurchaseDetails": { + "message": "展开购买详细信息" + }, + "collapsePurchaseDetails": { + "message": "折叠购买详细信息" + }, + "familiesMembership": { + "message": "家庭成员" + }, + "planDescPremium": { + "message": "全面的在线安全防护" + }, + "planDescFamiliesV2": { + "message": "为您的家庭提供高级安全防护" + }, + "planDescFreeV2": { + "message": "与 $COUNT$ 位其他用户共享", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "为任何组织提供高级功能" + }, + "planNameCustom": { + "message": "自定义方案" + }, + "planDescCustom": { + "message": "Bitwarden 适用于各种规模的企业,为密码和敏感信息提供安全保障。若您属于大型企业,请联系销售人员获取报价。" + }, + "builtInAuthenticator": { + "message": "内置身份验证器" + }, + "breachMonitoring": { + "message": "数据泄露监测" + }, + "andMoreFeatures": { + "message": "以及更多!" + }, + "secureFileStorage": { + "message": "安全文件存储" + }, + "familiesUnlimitedSharing": { + "message": "不限数量的共享 - 自主掌控可见范围" + }, + "familiesUnlimitedCollections": { + "message": "不限数量的家庭集合" + }, + "familiesSharedStorage": { + "message": "为重要的家庭信息提供共享存储" + }, + "limitedUsersV2": { + "message": "最多 $COUNT$ 位成员", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "最多 $COUNT$ 个集合", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "永远免费" + }, + "twoSecretsIncluded": { + "message": "2 个机密" + }, + "projectsIncludedV2": { + "message": "$COUNT$ 个工程", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "安全项目共享" + }, + "scimSupport": { + "message": "SCIM 支持" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ 个机器账户", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "企业安全策略" + }, + "selfHostOption": { + "message": "自托管选项" + }, + "complimentaryFamiliesPlan": { + "message": "为所有用户提供免费家庭版方案" + }, + "strengthenCybersecurity": { + "message": "加强网络安全" + }, + "boostProductivity": { + "message": "提高工作效率" + }, + "seamlessIntegration": { + "message": "无缝集成" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index 91c4dfdbcda..4f9a78c2c02 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -5557,6 +5557,13 @@ "personalOwnershipSubmitError": { "message": "由於某個企業原則,您被限制為儲存項目至您的個人密碼庫。將擁有權變更為組織,並從可用的集合中選擇。" }, + "desktopAutotypePolicy": { + "message": "Desktop Autotype Default Setting" + }, + "desktopAutotypePolicyDesc": { + "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "description": "This policy will enable Desktop Autotype by default for members on Unlock." + }, "disableSend": { "message": "停用 Send" }, @@ -6701,7 +6708,7 @@ "message": "Key Connector" }, "memberDecryptionKeyConnectorDescStart": { - "message": "將 SSO 登入連接到您的自我裝載解密金鑰伺服器。使用此選項,成員無需使用其主密碼來解密密碼庫資料。需要", + "message": "將 SSO 登入連結至您的自架解密金鑰伺服器。使用此選項,成員無需使用其主密碼來解密密碼庫資料。需要", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members won’t need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'" }, "memberDecryptionKeyConnectorDescLink": { @@ -6709,7 +6716,7 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members won’t need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'" }, "memberDecryptionKeyConnectorDescEnd": { - "message": "以用於設定 Key Connector 解密。聯絡 Bitwarden 支援以獲取設定協助。", + "message": "以用於設定 Key Connector 解密。聯絡 Bitwarden 支援以取得設定協助。", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members won’t need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'" }, "keyConnectorPolicyRestriction": { @@ -6721,6 +6728,15 @@ "disabledSso": { "message": "已停用 SSO" }, + "emailMustLoginWithSso": { + "message": "$EMAIL$ must login with Single Sign-on", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, "enabledKeyConnector": { "message": "已啟用 Key Connector" }, @@ -6770,7 +6786,7 @@ "message": "Generate billing token" }, "copyPasteBillingSync": { - "message": "請將本權杖複製後,貼至您自我裝載組織的「計費同步」設定當中。" + "message": "請將本權杖複製後,貼至您自架組織的「計費同步」設定當中。" }, "billingSyncCanAccess": { "message": "您的計費同步權杖可以讀取及編輯此組織的訂閱設定。" @@ -6788,25 +6804,25 @@ "message": "輪換權杖" }, "rotateBillingSyncTokenWarning": { - "message": "如果繼續,您需要重新在自我裝載伺服器上設定計費同步。" + "message": "如果繼續,您需要重新在自架伺服器上設定計費同步。" }, "rotateBillingSyncTokenTitle": { "message": "輪換「計費同步權杖」會導致之前的權杖失效。" }, "selfHostedServer": { - "message": "自建" + "message": "自架" }, "customEnvironment": { "message": "自訂環境" }, "selfHostedBaseUrlHint": { - "message": "Specify the base URL of your on-premises hosted Bitwarden installation. Example: https://bitwarden.company.com" + "message": "指定您自架的 Bitwarden 伺服器的網域 URL。例如:https://bitwarden.company.com" }, "selfHostedCustomEnvHeader": { - "message": "For advanced configuration, you can specify the base URL of each service independently." + "message": "適用於進階設定。您可以單獨指定各個服務的網域 URL。" }, "selfHostedEnvFormInvalid": { - "message": "You must add either the base Server URL or at least one custom environment." + "message": "您必須新增伺服器網域 URL 或至少一個自訂環境。" }, "apiUrl": { "message": "API 伺服器網址" @@ -6827,10 +6843,10 @@ "message": "環境網址已儲存" }, "selfHostingTitle": { - "message": "自我裝載" + "message": "自架" }, "selfHostingEnterpriseOrganizationSectionCopy": { - "message": "若要在您自己的伺服器上設定組織,您需要上傳授權檔案。若要使您的自我裝載組織支援「免費家庭」方案及進階計費功能,您需要設定計費同步。" + "message": "若要在您自己的伺服器上設定組織,您需要上傳授權檔案。若要使您的自架組織支援「免費家庭」方案及進階計費功能,您需要設定計費同步。" }, "billingSyncApiKeyRotated": { "message": "權杖已輪換" @@ -7129,7 +7145,7 @@ } }, "awaitingSyncSingular": { - "message": "權杖已在 $DAYS$ 天前輪換。請在您的自我裝載組織設定中,更新計費同步權杖。", + "message": "權杖已在 $DAYS$ 天前輪換。請在您的自架組織設定中,更新計費同步權杖。", "placeholders": { "days": { "content": "$1", @@ -7138,7 +7154,7 @@ } }, "awaitingSyncPlural": { - "message": "權杖已在 $DAYS$ 天前輪換。請在您的自我裝載組織設定中,更新計費同步權杖。", + "message": "權杖已在 $DAYS$ 天前輪換。請在您的自架組織設定中,更新計費同步權杖。", "placeholders": { "days": { "content": "$1", @@ -7151,7 +7167,7 @@ "description": "Used as a prefix to indicate the last time a sync occurred. Example \"Last sync 1968-11-16 00:00:00\"" }, "sponsorshipsSynced": { - "message": "已同步自我裝載贊助。" + "message": "已同步自架贊助。" }, "billingManagedByProvider": { "message": "由 $PROVIDER$ 管理", @@ -9074,7 +9090,7 @@ "message": "伺服器網址" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "自架伺服器 URL", "description": "Label for field requesting a self-hosted integration service URL" }, "alreadyHaveAccount": { @@ -10542,7 +10558,7 @@ "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." }, "selfHostingTitleProper": { - "message": "Self-Hosting" + "message": "自架" }, "claim-domain-single-org-warning": { "message": "Claiming a domain will turn on the single organization policy." @@ -11005,6 +11021,18 @@ "cannotCreateCollection": { "message": "Free organizations may have up to 2 collections. Upgrade to a paid plan to add more collections." }, + "searchArchive": { + "message": "Search archive" + }, + "archive": { + "message": "Archive" + }, + "noItemsInArchive": { + "message": "No items in archive" + }, + "archivedItemsDescription": { + "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + }, "businessUnit": { "message": "Business Unit" }, @@ -11337,5 +11365,137 @@ }, "verifyNow": { "message": "Verify now." + }, + "additionalStorageGB": { + "message": "Additional storage GB" + }, + "additionalServiceAccountsV2": { + "message": "Additional machine accounts" + }, + "secretsManagerSeats": { + "message": "Secrets Manager seats" + }, + "additionalStorage": { + "message": "Additional Storage" + }, + "expandPurchaseDetails": { + "message": "Expand purchase details" + }, + "collapsePurchaseDetails": { + "message": "Collapse purchase details" + }, + "familiesMembership": { + "message": "Families membership" + }, + "planDescPremium": { + "message": "Complete online security" + }, + "planDescFamiliesV2": { + "message": "Premium security for your family" + }, + "planDescFreeV2": { + "message": "Share with $COUNT$ other user", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "planDescEnterpriseV2": { + "message": "Advanced capabilities for any organization" + }, + "planNameCustom": { + "message": "Custom plan" + }, + "planDescCustom": { + "message": "Bitwarden scales with businesses of all sizes to secure passwords and sensitive information. If you're part of a large enterprise, contact sales to request a quote." + }, + "builtInAuthenticator": { + "message": "Built-in authenticator" + }, + "breachMonitoring": { + "message": "Breach monitoring" + }, + "andMoreFeatures": { + "message": "And more!" + }, + "secureFileStorage": { + "message": "Secure file storage" + }, + "familiesUnlimitedSharing": { + "message": "Unlimited sharing - choose who sees what" + }, + "familiesUnlimitedCollections": { + "message": "Unlimited family collections" + }, + "familiesSharedStorage": { + "message": "Shared storage for important family info" + }, + "limitedUsersV2": { + "message": "Up to $COUNT$ members", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "limitedCollectionsV2": { + "message": "Up to $COUNT$ collections", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, + "alwaysFree": { + "message": "Always free" + }, + "twoSecretsIncluded": { + "message": "2 secrets" + }, + "projectsIncludedV2": { + "message": "$COUNT$ project(s)", + "placeholders": { + "count": { + "content": "$1", + "example": "1" + } + } + }, + "secureItemSharing": { + "message": "Secure item sharing" + }, + "scimSupport": { + "message": "SCIM support" + }, + "includedMachineAccountsV2": { + "message": "$COUNT$ machine accounts", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "enterpriseSecurityPolicies": { + "message": "Enterprise security policies" + }, + "selfHostOption": { + "message": "Self-host option" + }, + "complimentaryFamiliesPlan": { + "message": "Complimentary families plan for all users" + }, + "strengthenCybersecurity": { + "message": "Strengthen cybersecurity" + }, + "boostProductivity": { + "message": "Boost productivity" + }, + "seamlessIntegration": { + "message": "Seamless integration" } } diff --git a/apps/web/src/videos/browser-extension-easy-access-dark.mp4 b/apps/web/src/videos/browser-extension-easy-access-dark.mp4 new file mode 100644 index 00000000000..dc9de8fbd44 Binary files /dev/null and b/apps/web/src/videos/browser-extension-easy-access-dark.mp4 differ diff --git a/apps/web/src/videos/browser-extension-easy-access.mp4 b/apps/web/src/videos/browser-extension-easy-access.mp4 new file mode 100644 index 00000000000..2d149fec219 Binary files /dev/null and b/apps/web/src/videos/browser-extension-easy-access.mp4 differ diff --git a/apps/web/src/videos/new-login-item-dark.mp4 b/apps/web/src/videos/new-login-item-dark.mp4 new file mode 100644 index 00000000000..0620ee985d9 Binary files /dev/null and b/apps/web/src/videos/new-login-item-dark.mp4 differ diff --git a/apps/web/src/videos/new-login-item.mp4 b/apps/web/src/videos/new-login-item.mp4 new file mode 100644 index 00000000000..a168dbc5148 Binary files /dev/null and b/apps/web/src/videos/new-login-item.mp4 differ diff --git a/apps/web/src/videos/onboarding-autofill-dark.mp4 b/apps/web/src/videos/onboarding-autofill-dark.mp4 new file mode 100644 index 00000000000..e09302f2ca2 Binary files /dev/null and b/apps/web/src/videos/onboarding-autofill-dark.mp4 differ diff --git a/apps/web/src/videos/onboarding-autofill.mp4 b/apps/web/src/videos/onboarding-autofill.mp4 new file mode 100644 index 00000000000..1be09c49e20 Binary files /dev/null and b/apps/web/src/videos/onboarding-autofill.mp4 differ diff --git a/apps/web/webpack.config.js b/apps/web/webpack.config.js index 77403914e9a..b311499dd55 100644 --- a/apps/web/webpack.config.js +++ b/apps/web/webpack.config.js @@ -144,6 +144,7 @@ const plugins = [ { from: "./src/browserconfig.xml" }, { from: "./src/app-id.json" }, { from: "./src/images", to: "images" }, + { from: "./src/videos", to: "videos" }, { from: "./src/locales", to: "locales" }, { from: "../../node_modules/qrious/dist/qrious.min.js", to: "scripts" }, { from: "../../node_modules/braintree-web-drop-in/dist/browser/dropin.js", to: "scripts" }, diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts index 5d0a14ca016..acbec1592a0 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts @@ -108,7 +108,16 @@ export type CriticalSummaryDetails = { }; /** - * All applications report detail. Application is the cipher + * An entry for an organization application and if it is + * marked as critical + */ +export type OrganizationReportApplication = { + applicationName: string; + isCritical: boolean; +}; + +/** + * Report details for an application * uri. Has the at risk, password, and member information */ export type ApplicationHealthReportDetail = { diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.spec.ts index 067d3f887ea..72d7e88fcab 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.spec.ts @@ -70,7 +70,7 @@ describe("CriticalAppsService", () => { const orgKey$ = new BehaviorSubject(OrgRecords); keyService.orgKeys$.mockReturnValue(orgKey$); - service.setOrganizationId(SomeOrganization, SomeUser); + service.loadOrganizationContext(SomeOrganization, SomeUser); // act await service.setCriticalApps(SomeOrganization, criticalApps); @@ -112,7 +112,7 @@ describe("CriticalAppsService", () => { const orgKey$ = new BehaviorSubject(OrgRecords); keyService.orgKeys$.mockReturnValue(orgKey$); - service.setOrganizationId(SomeOrganization, SomeUser); + service.loadOrganizationContext(SomeOrganization, SomeUser); // act await service.setCriticalApps(SomeOrganization, selectedUrls); @@ -136,7 +136,7 @@ describe("CriticalAppsService", () => { const orgKey$ = new BehaviorSubject(OrgRecords); keyService.orgKeys$.mockReturnValue(orgKey$); - service.setOrganizationId(SomeOrganization, SomeUser); + service.loadOrganizationContext(SomeOrganization, SomeUser); expect(keyService.orgKeys$).toHaveBeenCalledWith(SomeUser); expect(encryptService.decryptString).toHaveBeenCalledTimes(2); @@ -154,7 +154,7 @@ describe("CriticalAppsService", () => { const orgKey$ = new BehaviorSubject(OrgRecords); keyService.orgKeys$.mockReturnValue(orgKey$); - service.setOrganizationId(SomeOrganization, SomeUser); + service.loadOrganizationContext(SomeOrganization, SomeUser); service.setAppsInListForOrg(response); service.getAppsListForOrg(orgId as OrganizationId).subscribe((res) => { expect(res).toHaveLength(2); @@ -173,7 +173,7 @@ describe("CriticalAppsService", () => { const orgKey$ = new BehaviorSubject(OrgRecords); keyService.orgKeys$.mockReturnValue(orgKey$); - service.setOrganizationId(SomeOrganization, SomeUser); + service.loadOrganizationContext(SomeOrganization, SomeUser); service.setAppsInListForOrg(initialList); @@ -204,7 +204,7 @@ describe("CriticalAppsService", () => { const orgKey$ = new BehaviorSubject(OrgRecords); keyService.orgKeys$.mockReturnValue(orgKey$); - service.setOrganizationId(SomeOrganization, SomeUser); + service.loadOrganizationContext(SomeOrganization, SomeUser); service.setAppsInListForOrg(initialList); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.ts index be17bb2c0a5..82001387bbd 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/critical-apps.service.ts @@ -7,9 +7,7 @@ import { map, Observable, of, - Subject, switchMap, - takeUntil, zip, } from "rxjs"; @@ -30,17 +28,16 @@ import { CriticalAppsApiService } from "./critical-apps-api.service"; * Encrypts and saves data for a given organization */ export class CriticalAppsService { - private orgId = new BehaviorSubject(null); + // -------------------------- Context state -------------------------- + // The organization ID of the organization the user is currently viewing + private organizationId = new BehaviorSubject(null); private orgKey$ = new Observable(); - private criticalAppsList = new BehaviorSubject([]); - private teardown = new Subject(); - private fetchOrg$ = this.orgId - .pipe( - switchMap((orgId) => this.retrieveCriticalApps(orgId)), - takeUntil(this.teardown), - ) - .subscribe((apps) => this.criticalAppsList.next(apps)); + // -------------------------- Data ------------------------------------ + private criticalAppsListSubject$ = new BehaviorSubject< + PasswordHealthReportApplicationsResponse[] + >([]); + criticalAppsList$ = this.criticalAppsListSubject$.asObservable(); constructor( private keyService: KeyService, @@ -48,25 +45,52 @@ export class CriticalAppsService { private criticalAppsApiService: CriticalAppsApiService, ) {} + // Set context for the service for a specific organization + loadOrganizationContext(orgId: OrganizationId, userId: UserId) { + // Fetch the organization key for the user + this.orgKey$ = this.keyService.orgKeys$(userId).pipe( + filter((OrgKeys) => !!OrgKeys), + map((organizationKeysById) => organizationKeysById[orgId as OrganizationId]), + ); + + // Store organization id for service context + this.organizationId.next(orgId); + + // Setup the critical apps fetching for the organization + if (orgId) { + this.retrieveCriticalApps(orgId).subscribe({ + next: (result) => { + this.criticalAppsListSubject$.next(result); + }, + error: (error: unknown) => { + throw error; + }, + }); + } + } + // Get a list of critical apps for a given organization getAppsListForOrg(orgId: OrganizationId): Observable { - if (orgId != this.orgId.value) { - throw new Error("Organization ID mismatch"); + // [FIXME] Get organization id from context for all functions in this file + if (orgId != this.organizationId.value) { + throw new Error( + `Organization ID mismatch: expected ${this.organizationId.value}, got ${orgId}`, + ); } - return this.criticalAppsList + return this.criticalAppsListSubject$ .asObservable() .pipe(map((apps) => apps.filter((app) => app.organizationId === orgId))); } // Reset the critical apps list setAppsInListForOrg(apps: PasswordHealthReportApplicationsResponse[]) { - this.criticalAppsList.next(apps); + this.criticalAppsListSubject$.next(apps); } // Save the selected critical apps for a given organization async setCriticalApps(orgId: OrganizationId, selectedUrls: string[]) { - if (orgId != this.orgId.value) { + if (orgId != this.organizationId.value) { throw new Error("Organization ID mismatch"); } @@ -79,7 +103,7 @@ export class CriticalAppsService { // only save records that are not already in the database const newEntries = await this.filterNewEntries(orgId as OrganizationId, selectedUrls); const criticalAppsRequests = await this.encryptNewEntries( - this.orgId.value as OrganizationId, + this.organizationId.value as OrganizationId, orgKey, newEntries, ); @@ -89,7 +113,7 @@ export class CriticalAppsService { ); // add the new entries to the criticalAppsList - const updatedList = [...this.criticalAppsList.value]; + const updatedList = [...this.criticalAppsListSubject$.value]; for (const responseItem of dbResponse) { const decryptedUrl = await this.encryptService.decryptString( new EncString(responseItem.uri), @@ -103,26 +127,17 @@ export class CriticalAppsService { } as PasswordHealthReportApplicationsResponse); } } - this.criticalAppsList.next(updatedList); - } - - // Get the critical apps for a given organization - setOrganizationId(orgId: OrganizationId, userId: UserId) { - this.orgKey$ = this.keyService.orgKeys$(userId).pipe( - filter((OrgKeys) => !!OrgKeys), - map((organizationKeysById) => organizationKeysById[orgId as OrganizationId]), - ); - this.orgId.next(orgId); + this.criticalAppsListSubject$.next(updatedList); } // Drop a critical app for a given organization // Only one app may be dropped at a time async dropCriticalApp(orgId: OrganizationId, selectedUrl: string) { - if (orgId != this.orgId.value) { + if (orgId != this.organizationId.value) { throw new Error("Organization ID mismatch"); } - const app = this.criticalAppsList.value.find( + const app = this.criticalAppsListSubject$.value.find( (f) => f.organizationId === orgId && f.uri === selectedUrl, ); @@ -135,7 +150,9 @@ export class CriticalAppsService { passwordHealthReportApplicationIds: [app.id], }); - this.criticalAppsList.next(this.criticalAppsList.value.filter((f) => f.uri !== selectedUrl)); + this.criticalAppsListSubject$.next( + this.criticalAppsListSubject$.value.filter((f) => f.uri !== selectedUrl), + ); } private retrieveCriticalApps( @@ -170,7 +187,7 @@ export class CriticalAppsService { } private async filterNewEntries(orgId: OrganizationId, selectedUrls: string[]): Promise { - return await firstValueFrom(this.criticalAppsList).then((criticalApps) => { + return await firstValueFrom(this.criticalAppsListSubject$).then((criticalApps) => { const criticalAppsUri = criticalApps .filter((f) => f.organizationId === orgId) .map((f) => f.uri); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/index.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/index.ts index f547df31f41..e3f75ea0daf 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/index.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/index.ts @@ -2,5 +2,6 @@ export * from "./member-cipher-details-api.service"; export * from "./password-health.service"; export * from "./critical-apps.service"; export * from "./critical-apps-api.service"; +export * from "./risk-insights-api.service"; export * from "./risk-insights-report.service"; export * from "./risk-insights-data.service"; diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.ts index f3736c517e7..cc90fb6940a 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-data.service.ts @@ -1,22 +1,50 @@ -import { BehaviorSubject } from "rxjs"; -import { finalize } from "rxjs/operators"; +import { BehaviorSubject, firstValueFrom, Observable, of } from "rxjs"; +import { finalize, switchMap, withLatestFrom, map } from "rxjs/operators"; -import { OrganizationId } from "@bitwarden/common/types/guid"; +import { + getOrganizationById, + OrganizationService, +} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { AppAtRiskMembersDialogParams, AtRiskApplicationDetail, AtRiskMemberDetail, DrawerType, + DrawerDetails, ApplicationHealthReportDetail, + ApplicationHealthReportDetailEnriched, } from "../models/report-models"; +import { CriticalAppsService } from "./critical-apps.service"; import { RiskInsightsReportService } from "./risk-insights-report.service"; export class RiskInsightsDataService { - private applicationsSubject = new BehaviorSubject(null); + // -------------------------- Context state -------------------------- + // Current user viewing risk insights + private userIdSubject = new BehaviorSubject(null); + userId$ = this.userIdSubject.asObservable(); + // Organization the user is currently viewing + private organizationDetailsSubject = new BehaviorSubject<{ + organizationId: OrganizationId; + organizationName: string; + } | null>(null); + organizationDetails$ = this.organizationDetailsSubject.asObservable(); + + // -------------------------- Data ------------------------------------ + private applicationsSubject = new BehaviorSubject(null); applications$ = this.applicationsSubject.asObservable(); + private dataLastUpdatedSubject = new BehaviorSubject(null); + dataLastUpdated$ = this.dataLastUpdatedSubject.asObservable(); + + criticalApps$ = this.criticalAppsService.criticalAppsList$; + + // --------------------------- UI State ------------------------------------ + private isLoadingSubject = new BehaviorSubject(false); isLoading$ = this.isLoadingSubject.asObservable(); @@ -26,17 +54,63 @@ export class RiskInsightsDataService { private errorSubject = new BehaviorSubject(null); error$ = this.errorSubject.asObservable(); - private dataLastUpdatedSubject = new BehaviorSubject(null); - dataLastUpdated$ = this.dataLastUpdatedSubject.asObservable(); + // ------------------------- Drawer Variables ---------------- + // Drawer variables unified into a single BehaviorSubject + private drawerDetailsSubject = new BehaviorSubject({ + open: false, + invokerId: "", + activeDrawerType: DrawerType.None, + atRiskMemberDetails: [], + appAtRiskMembers: null, + atRiskAppDetails: null, + }); + drawerDetails$ = this.drawerDetailsSubject.asObservable(); - openDrawer = false; - drawerInvokerId: string = ""; - activeDrawerType: DrawerType = DrawerType.None; - atRiskMemberDetails: AtRiskMemberDetail[] = []; - appAtRiskMembers: AppAtRiskMembersDialogParams | null = null; - atRiskAppDetails: AtRiskApplicationDetail[] | null = null; + constructor( + private accountService: AccountService, + private criticalAppsService: CriticalAppsService, + private organizationService: OrganizationService, + private reportService: RiskInsightsReportService, + ) {} - constructor(private reportService: RiskInsightsReportService) {} + // [FIXME] PM-25612 - Call Initialization in RiskInsightsComponent instead of child components + async initializeForOrganization(organizationId: OrganizationId) { + // Fetch current user + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + if (userId) { + this.userIdSubject.next(userId); + } + + // [FIXME] getOrganizationById is now deprecated - update when we can + // Fetch organization details + const org = await firstValueFrom( + this.organizationService.organizations$(userId).pipe(getOrganizationById(organizationId)), + ); + if (org) { + this.organizationDetailsSubject.next({ + organizationId: organizationId, + organizationName: org.name, + }); + } + + // Load critical applications for organization + await this.criticalAppsService.loadOrganizationContext(organizationId, userId); + + // TODO: PM-25613 + // // Load existing report + + // this.fetchLastReport(organizationId, userId); + + // // Setup new report generation + // this._runApplicationsReport().subscribe({ + // next: (result) => { + // this.isRunningReportSubject.next(false); + // }, + // error: () => { + // this.errorSubject.next("Failed to save report"); + // }, + // }); + } /** * Fetches the applications report and updates the applicationsSubject. @@ -72,56 +146,134 @@ export class RiskInsightsDataService { this.fetchApplicationsReport(organizationId, true); } + // ------------------------------- Enrichment methods ------------------------------- + /** + * Takes the basic application health report details and enriches them to include + * critical app status and associated ciphers. + * + * @param applications The list of application health report details to enrich + * @returns The enriched application health report details with critical app status and ciphers + */ + enrichReportData$( + applications: ApplicationHealthReportDetail[], + ): Observable { + return of(applications).pipe( + withLatestFrom(this.organizationDetails$, this.criticalApps$), + switchMap(async ([apps, orgDetails, criticalApps]) => { + if (!orgDetails) { + return []; + } + + // Get ciphers for application + const cipherMap = await this.reportService.getApplicationCipherMap( + apps, + orgDetails.organizationId, + ); + + // Find critical apps + const criticalApplicationNames = new Set(criticalApps.map((ca) => ca.uri)); + + // Return enriched application data + return apps.map((app) => ({ + ...app, + ciphers: cipherMap.get(app.applicationName) || [], + isMarkedAsCritical: criticalApplicationNames.has(app.applicationName), + })) as ApplicationHealthReportDetailEnriched[]; + }), + ); + } + + // ------------------------------- Drawer management methods ------------------------------- + // ------------------------- Drawer functions ----------------------------- + + isActiveDrawerType$ = (drawerType: DrawerType): Observable => { + return this.drawerDetails$.pipe(map((details) => details.activeDrawerType === drawerType)); + }; isActiveDrawerType = (drawerType: DrawerType): boolean => { - return this.activeDrawerType === drawerType; + return this.drawerDetailsSubject.value.activeDrawerType === drawerType; + }; + + isDrawerOpenForInvoker$ = (applicationName: string) => { + return this.drawerDetails$.pipe(map((details) => details.invokerId === applicationName)); + }; + isDrawerOpenForInvoker = (applicationName: string): boolean => { + return this.drawerDetailsSubject.value.invokerId === applicationName; + }; + + closeDrawer = (): void => { + this.drawerDetailsSubject.next({ + open: false, + invokerId: "", + activeDrawerType: DrawerType.None, + atRiskMemberDetails: [], + appAtRiskMembers: null, + atRiskAppDetails: null, + }); }; setDrawerForOrgAtRiskMembers = ( atRiskMemberDetails: AtRiskMemberDetail[], invokerId: string = "", ): void => { - this.resetDrawer(DrawerType.OrgAtRiskMembers); - this.activeDrawerType = DrawerType.OrgAtRiskMembers; - this.drawerInvokerId = invokerId; - this.atRiskMemberDetails = atRiskMemberDetails; - this.openDrawer = !this.openDrawer; + const { open, activeDrawerType, invokerId: currentInvokerId } = this.drawerDetailsSubject.value; + const shouldClose = + open && activeDrawerType === DrawerType.OrgAtRiskMembers && currentInvokerId === invokerId; + + if (shouldClose) { + this.closeDrawer(); + } else { + this.drawerDetailsSubject.next({ + open: true, + invokerId, + activeDrawerType: DrawerType.OrgAtRiskMembers, + atRiskMemberDetails, + appAtRiskMembers: null, + atRiskAppDetails: null, + }); + } }; setDrawerForAppAtRiskMembers = ( atRiskMembersDialogParams: AppAtRiskMembersDialogParams, invokerId: string = "", ): void => { - this.resetDrawer(DrawerType.None); - this.activeDrawerType = DrawerType.AppAtRiskMembers; - this.drawerInvokerId = invokerId; - this.appAtRiskMembers = atRiskMembersDialogParams; - this.openDrawer = !this.openDrawer; + const { open, activeDrawerType, invokerId: currentInvokerId } = this.drawerDetailsSubject.value; + const shouldClose = + open && activeDrawerType === DrawerType.AppAtRiskMembers && currentInvokerId === invokerId; + + if (shouldClose) { + this.closeDrawer(); + } else { + this.drawerDetailsSubject.next({ + open: true, + invokerId, + activeDrawerType: DrawerType.AppAtRiskMembers, + atRiskMemberDetails: [], + appAtRiskMembers: atRiskMembersDialogParams, + atRiskAppDetails: null, + }); + } }; setDrawerForOrgAtRiskApps = ( atRiskApps: AtRiskApplicationDetail[], invokerId: string = "", ): void => { - this.resetDrawer(DrawerType.OrgAtRiskApps); - this.activeDrawerType = DrawerType.OrgAtRiskApps; - this.drawerInvokerId = invokerId; - this.atRiskAppDetails = atRiskApps; - this.openDrawer = !this.openDrawer; - }; + const { open, activeDrawerType, invokerId: currentInvokerId } = this.drawerDetailsSubject.value; + const shouldClose = + open && activeDrawerType === DrawerType.OrgAtRiskApps && currentInvokerId === invokerId; - closeDrawer = (): void => { - this.resetDrawer(DrawerType.None); - }; - - private resetDrawer = (drawerType: DrawerType): void => { - if (this.activeDrawerType !== drawerType) { - this.openDrawer = false; + if (shouldClose) { + this.closeDrawer(); + } else { + this.drawerDetailsSubject.next({ + open: true, + invokerId, + activeDrawerType: DrawerType.OrgAtRiskApps, + atRiskMemberDetails: [], + appAtRiskMembers: null, + atRiskAppDetails: atRiskApps, + }); } - - this.activeDrawerType = DrawerType.None; - this.atRiskMemberDetails = []; - this.appAtRiskMembers = null; - this.atRiskAppDetails = null; - this.drawerInvokerId = ""; }; } diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts index 45f9aeed1da..6c6fbb5b92c 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.spec.ts @@ -1,10 +1,7 @@ import { mock } from "jest-mock-extended"; import { firstValueFrom, of } from "rxjs"; -import { ZXCVBNResult } from "zxcvbn"; -import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EncryptedString } from "@bitwarden/common/key-management/crypto/models/enc-string"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -16,6 +13,7 @@ import { MemberCipherDetailsResponse } from "../response/member-cipher-details.r import { mockCiphers } from "./ciphers.mock"; import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service"; import { mockMemberCipherDetails } from "./member-cipher-details-api.service.spec"; +import { PasswordHealthService } from "./password-health.service"; import { RiskInsightsApiService } from "./risk-insights-api.service"; import { RiskInsightsEncryptionService } from "./risk-insights-encryption.service"; import { RiskInsightsReportService } from "./risk-insights-report.service"; @@ -24,10 +22,9 @@ describe("RiskInsightsReportService", () => { let service: RiskInsightsReportService; // Mock services - const pwdStrengthService = mock(); - const auditService = mock(); const cipherService = mock(); const memberCipherDetailsService = mock(); + const mockPasswordHealthService = mock(); const mockRiskInsightsApiService = mock(); const mockRiskInsightsEncryptionService = mock({ encryptRiskInsightsReport: jest.fn().mockResolvedValue("encryptedReportData"), @@ -40,26 +37,38 @@ describe("RiskInsightsReportService", () => { let mockMemberDetails: MemberCipherDetailsResponse[]; beforeEach(() => { - pwdStrengthService.getPasswordStrength.mockImplementation((password: string) => { - const score = password.length < 4 ? 1 : 4; - return { score } as ZXCVBNResult; - }); - - auditService.passwordLeaked.mockImplementation((password: string) => - Promise.resolve(password === "123" ? 100 : 0), - ); - cipherService.getAllFromApiForOrganization.mockResolvedValue(mockCiphers); memberCipherDetailsService.getMemberCipherDetails.mockResolvedValue(mockMemberCipherDetails); + // Mock PasswordHealthService methods + mockPasswordHealthService.isValidCipher.mockImplementation((cipher: any) => { + return ( + cipher.type === 1 && cipher.login?.password && !cipher.isDeleted && cipher.viewPassword + ); + }); + mockPasswordHealthService.findWeakPasswordDetails.mockImplementation((cipher: any) => { + if (cipher.login?.password === "123") { + return { score: 1, detailValue: { label: "veryWeak", badgeVariant: "danger" } }; + } + return null; + }); + mockPasswordHealthService.auditPasswordLeaks$.mockImplementation((ciphers: any[]) => { + const exposedDetails = ciphers + .filter((cipher) => cipher.login?.password === "123") + .map((cipher) => ({ + exposedXTimes: 100, + cipherId: cipher.id, + })); + return of(exposedDetails); + }); + service = new RiskInsightsReportService( - pwdStrengthService, - auditService, cipherService, memberCipherDetailsService, mockRiskInsightsApiService, mockRiskInsightsEncryptionService, + mockPasswordHealthService, ); // Reset mock ciphers before each test diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts index e6843385833..1839e89a1ae 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts @@ -3,27 +3,20 @@ import { BehaviorSubject, concatMap, - filter, first, firstValueFrom, forkJoin, from, map, - mergeMap, Observable, of, switchMap, - toArray, zip, } from "rxjs"; -import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { @@ -37,10 +30,7 @@ import { import { LEGACY_CipherHealthReportDetail, LEGACY_CipherHealthReportUriDetail, - ExposedPasswordDetail, LEGACY_MemberDetailsFlat, - WeakPasswordDetail, - WeakPasswordScore, LEGACY_ApplicationHealthReportDetailWithCriticalFlagAndCipher, } from "../models/password-health"; import { @@ -55,19 +45,11 @@ import { } from "../models/report-models"; import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service"; +import { PasswordHealthService } from "./password-health.service"; import { RiskInsightsApiService } from "./risk-insights-api.service"; import { RiskInsightsEncryptionService } from "./risk-insights-encryption.service"; export class RiskInsightsReportService { - constructor( - private passwordStrengthService: PasswordStrengthServiceAbstraction, - private auditService: AuditService, - private cipherService: CipherService, - private memberCipherDetailsApiService: MemberCipherDetailsApiService, - private riskInsightsApiService: RiskInsightsApiService, - private riskInsightsEncryptionService: RiskInsightsEncryptionService, - ) {} - private riskInsightsReportSubject = new BehaviorSubject([]); riskInsightsReport$ = this.riskInsightsReportSubject.asObservable(); @@ -84,6 +66,26 @@ export class RiskInsightsReportService { }); riskInsightsSummary$ = this.riskInsightsSummarySubject.asObservable(); + // [FIXME] CipherData + // Cipher data + // private _ciphersSubject = new BehaviorSubject(null); + // _ciphers$ = this._ciphersSubject.asObservable(); + + constructor( + private cipherService: CipherService, + private memberCipherDetailsApiService: MemberCipherDetailsApiService, + private riskInsightsApiService: RiskInsightsApiService, + private riskInsightsEncryptionService: RiskInsightsEncryptionService, + private passwordHealthService: PasswordHealthService, + ) {} + + // [FIXME] CipherData + // async loadCiphersForOrganization(organizationId: OrganizationId): Promise { + // await this.cipherService.getAllFromApiForOrganization(organizationId).then((ciphers) => { + // this._ciphersSubject.next(ciphers); + // }); + // } + /** * Report data from raw cipher health data. * Can be used in the Raw Data diagnostic tab (just exclude the members in the view) @@ -355,10 +357,12 @@ export class RiskInsightsReportService { ): Promise { const cipherHealthReports: LEGACY_CipherHealthReportDetail[] = []; const passwordUseMap = new Map(); - const exposedDetails = await this.findExposedPasswords(ciphers); + const exposedDetails = await firstValueFrom( + this.passwordHealthService.auditPasswordLeaks$(ciphers), + ); for (const cipher of ciphers) { - if (this.validateCipher(cipher)) { - const weakPassword = this.findWeakPassword(cipher); + if (this.passwordHealthService.isValidCipher(cipher)) { + const weakPassword = this.passwordHealthService.findWeakPasswordDetails(cipher); // Looping over all ciphers needs to happen first to determine reused passwords over all ciphers. // Store in the set and evaluate later if (passwordUseMap.has(cipher.login.password)) { @@ -436,104 +440,6 @@ export class RiskInsightsReportService { return appReports; } - private async findExposedPasswords(ciphers: CipherView[]): Promise { - const exposedDetails: ExposedPasswordDetail[] = []; - const promises: Promise[] = []; - - ciphers.forEach((ciph) => { - if (this.validateCipher(ciph)) { - const promise = this.auditService - .passwordLeaked(ciph.login.password) - .then((exposedCount) => { - if (exposedCount > 0) { - const detail = { - exposedXTimes: exposedCount, - cipherId: ciph.id, - } as ExposedPasswordDetail; - exposedDetails.push(detail); - } - }); - promises.push(promise); - } - }); - await Promise.all(promises); - - return exposedDetails; - } - - private findWeakPassword(cipher: CipherView): WeakPasswordDetail { - const hasUserName = this.isUserNameNotEmpty(cipher); - let userInput: string[] = []; - if (hasUserName) { - const atPosition = cipher.login.username.indexOf("@"); - if (atPosition > -1) { - userInput = userInput - .concat( - cipher.login.username - .substring(0, atPosition) - .trim() - .toLowerCase() - .split(/[^A-Za-z0-9]/), - ) - .filter((i) => i.length >= 3); - } else { - userInput = cipher.login.username - .trim() - .toLowerCase() - .split(/[^A-Za-z0-9]/) - .filter((i) => i.length >= 3); - } - } - const { score } = this.passwordStrengthService.getPasswordStrength( - cipher.login.password, - null, - userInput.length > 0 ? userInput : null, - ); - - if (score != null && score <= 2) { - const scoreValue = this.weakPasswordScore(score); - const weakPasswordDetail = { score: score, detailValue: scoreValue } as WeakPasswordDetail; - return weakPasswordDetail; - } - return null; - } - - private weakPasswordScore(score: number): WeakPasswordScore { - switch (score) { - case 4: - return { label: "strong", badgeVariant: "success" }; - case 3: - return { label: "good", badgeVariant: "primary" }; - case 2: - return { label: "weak", badgeVariant: "warning" }; - default: - return { label: "veryWeak", badgeVariant: "danger" }; - } - } - - private isUserNameNotEmpty(c: CipherView): boolean { - return !Utils.isNullOrWhitespace(c.login.username); - } - - /** - * Validates that the cipher is a login item, has a password - * is not deleted, and the user can view the password - * @param c the input cipher - */ - private validateCipher(c: CipherView): boolean { - const { type, login, isDeleted, viewPassword } = c; - if ( - type !== CipherType.Login || - login.password == null || - login.password === "" || - isDeleted || - !viewPassword - ) { - return false; - } - return true; - } - private _buildPasswordUseMap(ciphers: CipherView[]): Map { const passwordUseMap = new Map(); ciphers.forEach((cipher) => { @@ -559,6 +465,31 @@ export class RiskInsightsReportService { return applicationMap; } + /** + * + * @param applications The list of application health report details to map ciphers to + * @param organizationId + * @returns + */ + async getApplicationCipherMap( + applications: ApplicationHealthReportDetail[], + organizationId: OrganizationId, + ): Promise> { + // [FIXME] CipherData + // This call is made multiple times. We can optimize this + // by loading the ciphers once via a load method to avoid multiple API calls + // for the same organization + const allCiphers = await this.cipherService.getAllFromApiForOrganization(organizationId); + const cipherMap = new Map(); + + applications.forEach((app) => { + const filteredCiphers = allCiphers.filter((c) => app.cipherIds.includes(c.id)); + cipherMap.set(app.applicationName, filteredCiphers); + }); + return cipherMap; + } + + // --------------------------- Aggregation methods --------------------------- /** * Loop through the flattened cipher to uri data. If the item exists it's values need to be updated with the new item. * If the item is new, create and add the object with the flattened details @@ -659,11 +590,13 @@ export class RiskInsightsReportService { ciphers: CipherView[], memberDetails: MemberDetails[], ): Observable { - const validCiphers = ciphers.filter((cipher) => this.isValidCipher(cipher)); + const validCiphers = ciphers.filter((cipher) => + this.passwordHealthService.isValidCipher(cipher), + ); // Build password use map const passwordUseMap = this._buildPasswordUseMap(validCiphers); - return this.auditPasswordLeaks$(validCiphers).pipe( + return this.passwordHealthService.auditPasswordLeaks$(validCiphers).pipe( map((exposedDetails) => { return validCiphers.map((cipher) => { const exposedPassword = exposedDetails.find((x) => x.cipherId === cipher.id); @@ -673,7 +606,7 @@ export class RiskInsightsReportService { cipher: cipher, cipherMembers, healthData: { - weakPasswordDetail: this.findWeakPasswordDetails(cipher), + weakPasswordDetail: this.passwordHealthService.findWeakPasswordDetails(cipher), exposedPasswordDetail: exposedPassword, reusedPasswordCount: passwordUseMap.get(cipher.login.password) ?? 0, }, @@ -684,119 +617,4 @@ export class RiskInsightsReportService { }), ); } - - // TODO This is a temp implementation until the function is available in the password health service - /** - * Validates that the cipher is a login item, has a password - * is not deleted, and the user can view the password - * @param c the input cipher - */ - isValidCipher(c: CipherView): boolean { - const { type, login, isDeleted, viewPassword } = c; - if ( - type !== CipherType.Login || - login.password == null || - login.password === "" || - isDeleted || - !viewPassword - ) { - return false; - } - return true; - } - - // TODO This is a temp implementation until the function is available in the password health service - /** - * Extracts username parts from the cipher's username. - * This is used to help determine password strength. - * - * @param cipherUsername The username from the cipher. - * @returns An array of username parts. - */ - extractUsernameParts(cipherUsername: string) { - const atPosition = cipherUsername.indexOf("@"); - const userNameToProcess = - atPosition > -1 ? cipherUsername.substring(0, atPosition) : cipherUsername; - - return userNameToProcess - .trim() - .toLowerCase() - .split(/[^A-Za-z0-9]/) - .filter((i) => i.length >= 3); - } - - // TODO This is a temp implementation until the function is available in the password health service - /** - * Checks if the cipher has a weak password based on the password strength score. - * - * @param cipher - * @returns - */ - findWeakPasswordDetails(cipher: CipherView): WeakPasswordDetail | null { - // Validate the cipher - if (!this.isValidCipher(cipher)) { - return null; - } - - // Check the username - const userInput = this.isUserNameNotEmpty(cipher) - ? this.extractUsernameParts(cipher.login.username) - : null; - - const { score } = this.passwordStrengthService.getPasswordStrength( - cipher.login.password, - null, - userInput, - ); - - // If a score is not found or a score is less than 3, it's weak - if (score != null && score <= 2) { - return { score: score, detailValue: this.getPasswordScoreInfo(score) }; - } - return null; - } - - // TODO This is a temp implementation until the function is available in the password health service - /** - * Gets the password score information based on the score. - * - * @param score - * @returns An object containing the label and badge variant for the password score. - */ - getPasswordScoreInfo(score: number): WeakPasswordScore { - switch (score) { - case 4: - return { label: "strong", badgeVariant: "success" }; - case 3: - return { label: "good", badgeVariant: "primary" }; - case 2: - return { label: "weak", badgeVariant: "warning" }; - default: - return { label: "veryWeak", badgeVariant: "danger" }; - } - } - - // TODO This is a temp implementation until the function is available in the password health service - /** - * Finds exposed passwords in a list of ciphers. - * - * @param ciphers The list of ciphers to check. - * @returns An observable that emits an array of ExposedPasswordDetail. - */ - auditPasswordLeaks$(ciphers: CipherView[]): Observable { - return from(ciphers).pipe( - filter((cipher) => this.isValidCipher(cipher)), - mergeMap((cipher) => - this.auditService - .passwordLeaked(cipher.login.password) - .then((exposedCount) => ({ cipher, exposedCount })), - ), - filter(({ exposedCount }) => exposedCount > 0), - map(({ cipher, exposedCount }) => ({ - exposedXTimes: exposedCount, - cipherId: cipher.id, - })), - toArray(), - ); - } } diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence.module.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence.module.ts index 0fe1737bde3..1d80f2154b1 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence.module.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence.module.ts @@ -5,12 +5,16 @@ import { CriticalAppsService } from "@bitwarden/bit-common/dirt/reports/risk-ins import { CriticalAppsApiService, MemberCipherDetailsApiService, + PasswordHealthService, + RiskInsightsApiService, RiskInsightsDataService, RiskInsightsReportService, } from "@bitwarden/bit-common/dirt/reports/risk-insights/services"; import { RiskInsightsEncryptionService } from "@bitwarden/bit-common/dirt/reports/risk-insights/services/risk-insights-encryption.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service"; import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength/password-strength.service.abstraction"; @@ -28,18 +32,32 @@ import { RiskInsightsComponent } from "./risk-insights.component"; deps: [ApiService], }, { - provide: RiskInsightsReportService, - deps: [ - PasswordStrengthServiceAbstraction, - AuditService, - CipherService, - MemberCipherDetailsApiService, - ], + provide: PasswordHealthService, + deps: [PasswordStrengthServiceAbstraction, AuditService], }, { - provide: RiskInsightsDataService, - deps: [RiskInsightsReportService], + provide: RiskInsightsApiService, + deps: [ApiService], }, + { + provide: RiskInsightsReportService, + deps: [ + CipherService, + MemberCipherDetailsApiService, + RiskInsightsApiService, + RiskInsightsEncryptionService, + PasswordHealthService, + ], + }, + safeProvider({ + provide: RiskInsightsDataService, + deps: [ + AccountServiceAbstraction, + CriticalAppsService, + OrganizationService, + RiskInsightsReportService, + ], + }), { provide: RiskInsightsEncryptionService, useClass: RiskInsightsEncryptionService, diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.html index 79f18803727..b861925f7cb 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.html @@ -25,44 +25,46 @@

{{ "allApplications" | i18n }}

-
- - + -
+ + + +
+ }
{ const info = { members: @@ -226,9 +219,9 @@ export class AllApplicationsComponent implements OnInit { } }; - getSelectedUrls = () => Array.from(this.selectedUrls); - isDrawerOpenForTableRow = (applicationName: string): boolean => { - return this.dataService.drawerInvokerId === applicationName; + // Note: This function will be replaced by PR #16523 with openApplication binding + // Using private access to BehaviorSubject value for backward compatibility + return (this.dataService as any).drawerDetailsSubject?.value?.invokerId === applicationName; }; } diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.html index 720c3ec04d0..3d3a9baa6e8 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/app-table-row-scrollable.component.html @@ -3,13 +3,13 @@ - {{ "application" | i18n }} - + {{ "application" | i18n }} + {{ "atRiskPasswords" | i18n }} - {{ "totalPasswords" | i18n }} - {{ "atRiskMembers" | i18n }} - {{ "totalMembers" | i18n }} + {{ "totalPasswords" | i18n }} + {{ "atRiskMembers" | i18n }} + {{ "totalMembers" | i18n }} diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.html index f78880b34a0..18b114515af 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications.component.html @@ -43,44 +43,46 @@ }}
-
- - + -
+ + + + + }
{ - const criticalUrls = criticalApps.map((ca) => ca.uri); - const data = applications?.map((app) => ({ - ...app, - isMarkedAsCritical: criticalUrls.includes(app.applicationName), - })) as LEGACY_ApplicationHealthReportDetailWithCriticalFlag[]; - return data?.filter((app) => app.isMarkedAsCritical); - }), - switchMap(async (data) => { - if (data) { - const dataWithCiphers = await this.reportService.identifyCiphers( - data, - this.organizationId, - ); - return dataWithCiphers; + this.criticalAppsService.loadOrganizationContext(this.organizationId as OrganizationId, userId); + + if (this.organizationId) { + combineLatest([ + this.dataService.applications$, + this.criticalAppsService.getAppsListForOrg(this.organizationId as OrganizationId), + ]) + .pipe( + takeUntilDestroyed(this.destroyRef), + map(([applications, criticalApps]) => { + const criticalUrls = criticalApps.map((ca) => ca.uri); + const data = applications?.map((app) => ({ + ...app, + isMarkedAsCritical: criticalUrls.includes(app.applicationName), + })) as LEGACY_ApplicationHealthReportDetailWithCriticalFlag[]; + return data?.filter((app) => app.isMarkedAsCritical); + }), + switchMap(async (data) => { + if (data) { + const dataWithCiphers = await this.reportService.identifyCiphers( + data, + this.organizationId, + ); + return dataWithCiphers; + } + return null; + }), + ) + .subscribe((applications) => { + if (applications) { + this.dataSource.data = applications; + this.applicationSummary = this.reportService.generateApplicationsSummary(applications); + this.enableRequestPasswordChange = this.applicationSummary.totalAtRiskMemberCount > 0; } - return null; - }), - ) - .subscribe((applications) => { - if (applications) { - this.dataSource.data = applications; - this.applicationSummary = this.reportService.generateApplicationsSummary(applications); - this.enableRequestPasswordChange = this.applicationSummary.totalAtRiskMemberCount > 0; - } - }); + }); + } } goToAllAppsTab = async () => { @@ -200,10 +202,9 @@ export class CriticalApplicationsComponent implements OnInit { this.dataService.setDrawerForOrgAtRiskApps(data, invokerId); }; - trackByFunction(_: number, item: LEGACY_ApplicationHealthReportDetailWithCriticalFlag) { - return item.applicationName; - } isDrawerOpenForTableRow = (applicationName: string) => { - return this.dataService.drawerInvokerId === applicationName; + // Note: This function will be replaced by PR #16523 with openApplication binding + // Using private access to BehaviorSubject value for backward compatibility + return (this.dataService as any).drawerDetailsSubject?.value?.invokerId === applicationName; }; } diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.html index 3c2d5cd0a15..50af2c9e9a7 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.html @@ -53,93 +53,91 @@ - - - - - - {{ - (dataService.atRiskMemberDetails.length > 0 - ? "atRiskMembersDescription" - : "atRiskMembersDescriptionNone" - ) | i18n - }} - -
-
{{ "email" | i18n }}
-
- {{ "atRiskPasswords" | i18n }} + @if (dataService.drawerDetails$ | async; as drawerDetails) { + + + + + + {{ + (drawerDetails.atRiskMemberDetails.length > 0 + ? "atRiskMembersDescription" + : "atRiskMembersDescriptionNone" + ) | i18n + }} + +
+
{{ "email" | i18n }}
+
+ {{ "atRiskPasswords" | i18n }} +
+ +
+
{{ member.email }}
+
{{ member.atRiskPasswordCount }}
+
+
+
+
+
+ + @if (dataService.isActiveDrawerType(drawerTypes.AppAtRiskMembers)) { + + + +
+ {{ "atRiskMembersWithCount" | i18n: drawerDetails.appAtRiskMembers.members.length }}
- -
+
+ {{ + (drawerDetails.appAtRiskMembers.members.length > 0 + ? "atRiskMembersDescriptionWithApp" + : "atRiskMembersDescriptionWithAppNone" + ) | i18n: drawerDetails.appAtRiskMembers.applicationName + }} +
+
+
{{ member.email }}
-
{{ member.atRiskPasswordCount }}
-
- - - - - - - - - -
- {{ "atRiskMembersWithCount" | i18n: dataService.appAtRiskMembers.members.length }} -
-
- {{ - (dataService.appAtRiskMembers.members.length > 0 - ? "atRiskMembersDescriptionWithApp" - : "atRiskMembersDescriptionWithAppNone" - ) | i18n: dataService.appAtRiskMembers.applicationName - }} -
-
- -
{{ member.email }}
-
-
-
-
- - - - - - - {{ - (dataService.atRiskAppDetails.length > 0 - ? "atRiskApplicationsDescription" - : "atRiskApplicationsDescriptionNone" - ) | i18n - }} - -
-
- {{ "application" | i18n }} -
-
- {{ "atRiskPasswords" | i18n }} -
+
- -
-
{{ app.applicationName }}
-
{{ app.atRiskPasswordCount }}
+ + } + + @if (dataService.isActiveDrawerType(drawerTypes.OrgAtRiskApps)) { + + + + + {{ + (drawerDetails.atRiskAppDetails.length > 0 + ? "atRiskApplicationsDescription" + : "atRiskApplicationsDescriptionNone" + ) | i18n + }} + +
+
+ {{ "application" | i18n }} +
+
+ {{ "atRiskPasswords" | i18n }} +
+ +
+
{{ app.applicationName }}
+
{{ app.atRiskPasswordCount }}
+
+
- -
- - + + } + + } diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts index b7e440880e3..a34cae44f14 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts @@ -61,6 +61,9 @@ export enum RiskInsightsTabType { ], }) export class RiskInsightsComponent implements OnInit { + private destroyRef = inject(DestroyRef); + private _isDrawerOpen: boolean = false; + tabIndex: RiskInsightsTabType = RiskInsightsTabType.AllApps; isRiskInsightsActivityTabFeatureEnabled: boolean = false; @@ -73,7 +76,6 @@ export class RiskInsightsComponent implements OnInit { notifiedMembersCount: number = 0; private organizationId: OrganizationId = "" as OrganizationId; - private destroyRef = inject(DestroyRef); isLoading$: Observable = new Observable(); isRefreshing$: Observable = new Observable(); @@ -127,12 +129,22 @@ export class RiskInsightsComponent implements OnInit { this.appsCount = applications.length; } - this.criticalAppsService.setOrganizationId(this.organizationId as OrganizationId, userId); + this.criticalAppsService.loadOrganizationContext( + this.organizationId as OrganizationId, + userId, + ); this.criticalApps$ = this.criticalAppsService.getAppsListForOrg( this.organizationId as OrganizationId, ); }, }); + + // Subscribe to drawer state changes + this.dataService.drawerDetails$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((details) => { + this._isDrawerOpen = details.open; + }); } /** @@ -160,4 +172,27 @@ export class RiskInsightsComponent implements OnInit { get drawerTypes(): typeof DrawerType { return DrawerType; } + + /** + * Special case getter for syncing drawer state from service to component. + * This allows the template to use two-way binding while staying reactive. + */ + get isDrawerOpen() { + return this._isDrawerOpen; + } + + /** + * Special case setter for syncing drawer state from component to service. + * When the drawer component closes the drawer, this syncs the state back to the service. + */ + set isDrawerOpen(value: boolean) { + if (this._isDrawerOpen !== value) { + this._isDrawerOpen = value; + + // Close the drawer in the service if the drawer component closed the drawer + if (!value) { + this.dataService.closeDrawer(); + } + } + } } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.html index 1cf11634d72..353d8d8c8ed 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.html @@ -3,7 +3,7 @@
{{ "fileUpload" | i18n }} -
+
diff --git a/libs/admin-console/package.json b/libs/admin-console/package.json index c319beb8f4c..5e934006d0d 100644 --- a/libs/admin-console/package.json +++ b/libs/admin-console/package.json @@ -15,6 +15,7 @@ "scripts": { "clean": "rimraf dist", "build": "npm run clean && tsc", - "build:watch": "npm run clean && tsc -watch" + "build:watch": "npm run clean && tsc -watch", + "test": "jest" } } diff --git a/libs/admin-console/project.json b/libs/admin-console/project.json new file mode 100644 index 00000000000..cd9565d7e21 --- /dev/null +++ b/libs/admin-console/project.json @@ -0,0 +1,29 @@ +{ + "name": "@bitwarden/admin-console", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/admin-console/src", + "projectType": "library", + "tags": ["scope:admin-console", "type:lib"], + "targets": { + "build": { + "executor": "nx:run-script", + "dependsOn": [], + "options": { + "script": "build" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/admin-console/**/*.ts"] + } + }, + "test": { + "executor": "nx:run-script", + "options": { + "script": "test" + } + } + } +} diff --git a/libs/admin-console/src/common/collections/abstractions/collection.service.ts b/libs/admin-console/src/common/collections/abstractions/collection.service.ts index dabaf078e16..f879831324d 100644 --- a/libs/admin-console/src/common/collections/abstractions/collection.service.ts +++ b/libs/admin-console/src/common/collections/abstractions/collection.service.ts @@ -28,4 +28,11 @@ export abstract class CollectionService { * Transforms the input CollectionViews into TreeNodes and then returns the Treenode with the specified id */ abstract getNested(collections: CollectionView[], id: string): TreeNode; + + /* + * Groups/keys collections by OrganizationId + */ + abstract groupByOrganization( + collections: CollectionView[], + ): Map; } diff --git a/libs/admin-console/src/common/collections/services/default-collection.service.ts b/libs/admin-console/src/common/collections/services/default-collection.service.ts index d3b4ebe1f34..0511b692b38 100644 --- a/libs/admin-console/src/common/collections/services/default-collection.service.ts +++ b/libs/admin-console/src/common/collections/services/default-collection.service.ts @@ -230,8 +230,8 @@ export class DefaultCollectionService implements CollectionService { return all; } - groupByOrganization(collections: CollectionView[]): Map { - const groupedByOrg = new Map(); + groupByOrganization(collections: CollectionView[]): Map { + const groupedByOrg = new Map(); collections.map((c) => { const key = c.organizationId; (groupedByOrg.get(key) ?? groupedByOrg.set(key, []).get(key)!).push(c); diff --git a/libs/admin-console/src/common/organization-user/models/requests/organization-user-reset-password.request.ts b/libs/admin-console/src/common/organization-user/models/requests/organization-user-reset-password.request.ts index 7d060e3390e..bd87f6ca5d8 100644 --- a/libs/admin-console/src/common/organization-user/models/requests/organization-user-reset-password.request.ts +++ b/libs/admin-console/src/common/organization-user/models/requests/organization-user-reset-password.request.ts @@ -1,6 +1,25 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore + +import { + MasterPasswordAuthenticationData, + MasterPasswordUnlockData, +} from "@bitwarden/common/key-management/master-password/types/master-password.types"; + export class OrganizationUserResetPasswordRequest { newMasterPasswordHash: string; key: string; + + // This will eventually be changed to be an actual constructor, once all callers are updated. + // The body of this request will be changed to carry the authentication data and unlock data. + // https://bitwarden.atlassian.net/browse/PM-23234 + static newConstructor( + authenticationData: MasterPasswordAuthenticationData, + unlockData: MasterPasswordUnlockData, + ): OrganizationUserResetPasswordRequest { + const request = new OrganizationUserResetPasswordRequest(); + request.newMasterPasswordHash = authenticationData.masterPasswordAuthenticationHash; + request.key = unlockData.masterKeyWrappedUserKey; + return request; + } } diff --git a/libs/angular/package.json b/libs/angular/package.json index 322c8c826a2..0b9111f88fb 100644 --- a/libs/angular/package.json +++ b/libs/angular/package.json @@ -15,6 +15,7 @@ "scripts": { "clean": "rimraf dist", "build": "npm run clean && tsc", - "build:watch": "npm run clean && tsc -watch" + "build:watch": "npm run clean && tsc -watch", + "test": "jest" } } diff --git a/libs/angular/project.json b/libs/angular/project.json new file mode 100644 index 00000000000..eeeb6c68eb0 --- /dev/null +++ b/libs/angular/project.json @@ -0,0 +1,41 @@ +{ + "name": "@bitwarden/angular", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/angular/src", + "projectType": "library", + "tags": ["scope:angular", "type:lib"], + "targets": { + "build": { + "executor": "nx:run-script", + "dependsOn": [], + "options": { + "script": "build" + } + }, + "build:watch": { + "executor": "nx:run-script", + "options": { + "script": "build:watch" + } + }, + "clean": { + "executor": "nx:run-script", + "options": { + "script": "clean" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/angular/**/*.ts"] + } + }, + "test": { + "executor": "nx:run-script", + "options": { + "script": "test" + } + } + } +} diff --git a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts index fbab228d6e0..01e87cae0ed 100644 --- a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts +++ b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts @@ -137,8 +137,7 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi newPasswordHint, orgSsoIdentifier, keysRequest, - kdfConfig.kdfType, - kdfConfig.iterations, + kdfConfig, ); await this.masterPasswordApiService.setPassword(request); diff --git a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts index d96b4c86fb5..7a1dfc91e67 100644 --- a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts +++ b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts @@ -157,8 +157,7 @@ describe("DefaultSetInitialPasswordService", () => { credentials.newPasswordHint, credentials.orgSsoIdentifier, keysRequest, - credentials.kdfConfig.kdfType, - credentials.kdfConfig.iterations, + credentials.kdfConfig, ); enrollmentRequest = new OrganizationUserResetPasswordEnrollmentRequest(); diff --git a/libs/angular/src/billing/components/premium-badge/premium-badge.component.ts b/libs/angular/src/billing/components/premium-badge/premium-badge.component.ts index d2783781c6d..a4a1d76d1d6 100644 --- a/libs/angular/src/billing/components/premium-badge/premium-badge.component.ts +++ b/libs/angular/src/billing/components/premium-badge/premium-badge.component.ts @@ -1,7 +1,7 @@ -import { Component, input, output } from "@angular/core"; +import { Component, input } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { BadgeModule } from "@bitwarden/components"; @Component({ @@ -15,17 +15,11 @@ import { BadgeModule } from "@bitwarden/components"; imports: [BadgeModule, JslibModule], }) export class PremiumBadgeComponent { - /** Skip sending the premiumRequired message (default: false). */ - skipMessaging = input(false); - onClick = output(); + organizationId = input(); - constructor(private messagingService: MessagingService) {} + constructor(private premiumUpgradePromptService: PremiumUpgradePromptService) {} async promptForPremium() { - this.onClick.emit(); - if (this.skipMessaging()) { - return; - } - this.messagingService.send("premiumRequired"); + await this.premiumUpgradePromptService.promptForPremium(this.organizationId()); } } diff --git a/libs/angular/src/billing/components/premium-badge/premium-badge.stories.ts b/libs/angular/src/billing/components/premium-badge/premium-badge.stories.ts index 79efa6f772c..08259358f30 100644 --- a/libs/angular/src/billing/components/premium-badge/premium-badge.stories.ts +++ b/libs/angular/src/billing/components/premium-badge/premium-badge.stories.ts @@ -6,6 +6,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessageSender } from "@bitwarden/common/platform/messaging"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { BadgeModule, I18nMockService } from "@bitwarden/components"; import { PremiumBadgeComponent } from "./premium-badge.component"; @@ -51,6 +52,12 @@ export default { hasPremiumFromAnySource$: () => of(false), }, }, + { + provide: PremiumUpgradePromptService, + useValue: { + promptForPremium: (orgId?: string) => {}, + }, + }, ], }), ], diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index fa6c1071e21..ff704394bc3 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -163,6 +163,10 @@ import { EncryptServiceImplementation } from "@bitwarden/common/key-management/c import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; import { DeviceTrustService } from "@bitwarden/common/key-management/device-trust/services/device-trust.service.implementation"; +import { DefaultChangeKdfApiService } from "@bitwarden/common/key-management/kdf/change-kdf-api.service"; +import { ChangeKdfApiService } from "@bitwarden/common/key-management/kdf/change-kdf-api.service.abstraction"; +import { DefaultChangeKdfService } from "@bitwarden/common/key-management/kdf/change-kdf-service"; +import { ChangeKdfService } from "@bitwarden/common/key-management/kdf/change-kdf-service.abstraction"; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service"; import { @@ -1242,6 +1246,16 @@ const safeProviders: SafeProvider[] = [ ConfigService, ], }), + safeProvider({ + provide: ChangeKdfApiService, + useClass: DefaultChangeKdfApiService, + deps: [ApiServiceAbstraction], + }), + safeProvider({ + provide: ChangeKdfService, + useClass: DefaultChangeKdfService, + deps: [MasterPasswordServiceAbstraction, KeyService, KdfConfigService, ChangeKdfApiService], + }), safeProvider({ provide: AuthRequestServiceAbstraction, useClass: AuthRequestService, diff --git a/libs/angular/src/vault/components/spotlight/spotlight.stories.ts b/libs/angular/src/vault/components/spotlight/spotlight.stories.ts index 8e660aacbad..2988344e23d 100644 --- a/libs/angular/src/vault/components/spotlight/spotlight.stories.ts +++ b/libs/angular/src/vault/components/spotlight/spotlight.stories.ts @@ -22,6 +22,7 @@ const meta: Meta = { useFactory: () => { return new I18nMockService({ close: "Close", + loading: "Loading", }); }, }, diff --git a/libs/assets/src/svg/svgs/admin-console.ts b/libs/assets/src/svg/svgs/admin-console.ts index 596822dde57..83c8cf9f0e1 100644 --- a/libs/assets/src/svg/svgs/admin-console.ts +++ b/libs/assets/src/svg/svgs/admin-console.ts @@ -1,7 +1,17 @@ import { svgIcon } from "../icon-service"; const AdminConsoleLogo = svgIcon` - + + + + + + + + + + + `; export default AdminConsoleLogo; diff --git a/libs/assets/src/svg/svgs/bitwarden-icon.ts b/libs/assets/src/svg/svgs/bitwarden-icon.ts index 710d20937bd..2f88b38043c 100644 --- a/libs/assets/src/svg/svgs/bitwarden-icon.ts +++ b/libs/assets/src/svg/svgs/bitwarden-icon.ts @@ -3,18 +3,18 @@ import { svgIcon } from "../icon-service"; export const BitwardenIcon = svgIcon` - - - - - - - + + + + + + + - - - + + + `; diff --git a/libs/assets/src/svg/svgs/business-unit-portal.ts b/libs/assets/src/svg/svgs/business-unit-portal.ts index bae5ebada6a..db3a6b8ef4f 100644 --- a/libs/assets/src/svg/svgs/business-unit-portal.ts +++ b/libs/assets/src/svg/svgs/business-unit-portal.ts @@ -1,7 +1,17 @@ import { svgIcon } from "../icon-service"; const BusinessUnitPortalLogo = svgIcon` - + + + + + + + + + + + `; export default BusinessUnitPortalLogo; diff --git a/libs/assets/src/svg/svgs/extension-bitwarden-logo.icon.ts b/libs/assets/src/svg/svgs/extension-bitwarden-logo.icon.ts deleted file mode 100644 index 42a74e006bc..00000000000 --- a/libs/assets/src/svg/svgs/extension-bitwarden-logo.icon.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { svgIcon } from "../icon-service"; - -export const ExtensionBitwardenLogo = svgIcon` - - Bitwarden - - -`; diff --git a/libs/assets/src/svg/svgs/index.ts b/libs/assets/src/svg/svgs/index.ts index 10f2f7cd8d4..ab4f2c23f13 100644 --- a/libs/assets/src/svg/svgs/index.ts +++ b/libs/assets/src/svg/svgs/index.ts @@ -12,7 +12,6 @@ export * from "./deactivated-org"; export * from "./devices.icon"; export * from "./domain.icon"; export * from "./empty-trash"; -export * from "./extension-bitwarden-logo.icon"; export * from "./gear"; export * from "./generator"; export * from "./item-types"; diff --git a/libs/assets/src/svg/svgs/password-manager.ts b/libs/assets/src/svg/svgs/password-manager.ts index 5c8ef852025..17b6f148be3 100644 --- a/libs/assets/src/svg/svgs/password-manager.ts +++ b/libs/assets/src/svg/svgs/password-manager.ts @@ -1,7 +1,17 @@ import { svgIcon } from "../icon-service"; const PasswordManagerLogo = svgIcon` - + + + + + + + + + + `; export default PasswordManagerLogo; diff --git a/libs/assets/src/svg/svgs/provider-portal.ts b/libs/assets/src/svg/svgs/provider-portal.ts index 54e4b219385..51c04e1553b 100644 --- a/libs/assets/src/svg/svgs/provider-portal.ts +++ b/libs/assets/src/svg/svgs/provider-portal.ts @@ -1,7 +1,17 @@ import { svgIcon } from "../icon-service"; const ProviderPortalLogo = svgIcon` - + + + + + + + + + + + `; export default ProviderPortalLogo; diff --git a/libs/assets/src/svg/svgs/secrets-manager.ts b/libs/assets/src/svg/svgs/secrets-manager.ts index 3a5095b95bd..27589e7e2f9 100644 --- a/libs/assets/src/svg/svgs/secrets-manager.ts +++ b/libs/assets/src/svg/svgs/secrets-manager.ts @@ -1,7 +1,17 @@ import { svgIcon } from "../icon-service"; const SecretsManagerLogo = svgIcon` - + + + + + + + + + + + `; export default SecretsManagerLogo; diff --git a/libs/assets/src/svg/svgs/shield.ts b/libs/assets/src/svg/svgs/shield.ts index b0c781bd00d..eaf9780773e 100644 --- a/libs/assets/src/svg/svgs/shield.ts +++ b/libs/assets/src/svg/svgs/shield.ts @@ -4,13 +4,22 @@ import { svgIcon } from "../icon-service"; * Shield logo with extra space in the viewbox. */ const AnonLayoutBitwardenShield = svgIcon` - + `; const BitwardenShield = svgIcon` - + + + + + + + + + + `; export { AnonLayoutBitwardenShield, BitwardenShield }; diff --git a/libs/auth/package.json b/libs/auth/package.json index 52c1be63f81..cab83bd3b8d 100644 --- a/libs/auth/package.json +++ b/libs/auth/package.json @@ -15,6 +15,7 @@ "scripts": { "clean": "rimraf dist", "build": "npm run clean && tsc", - "build:watch": "npm run clean && tsc -watch" + "build:watch": "npm run clean && tsc -watch", + "test": "jest" } } diff --git a/libs/auth/project.json b/libs/auth/project.json new file mode 100644 index 00000000000..753e7d929b1 --- /dev/null +++ b/libs/auth/project.json @@ -0,0 +1,41 @@ +{ + "name": "@bitwarden/auth", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/auth/src", + "projectType": "library", + "tags": ["scope:auth", "type:lib"], + "targets": { + "build": { + "executor": "nx:run-script", + "dependsOn": [], + "options": { + "script": "build" + } + }, + "build:watch": { + "executor": "nx:run-script", + "options": { + "script": "build:watch" + } + }, + "clean": { + "executor": "nx:run-script", + "options": { + "script": "clean" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/auth/**/*.ts"] + } + }, + "test": { + "executor": "nx:run-script", + "options": { + "script": "test" + } + } + } +} diff --git a/libs/auth/src/angular/input-password/input-password.component.ts b/libs/auth/src/angular/input-password/input-password.component.ts index 32b02bf016a..dda471c7129 100644 --- a/libs/auth/src/angular/input-password/input-password.component.ts +++ b/libs/auth/src/angular/input-password/input-password.component.ts @@ -303,6 +303,14 @@ export class InputPasswordComponent implements OnInit { throw new Error("KdfConfig is required to create master key."); } + const salt = + this.userId != null + ? await firstValueFrom(this.masterPasswordService.saltForUser$(this.userId)) + : this.masterPasswordService.emailToSalt(this.email); + if (salt == null) { + throw new Error("Salt is required to create master key."); + } + // 2. Verify current password is correct (if necessary) if ( this.flow === InputPasswordFlow.ChangePassword || @@ -348,6 +356,7 @@ export class InputPasswordComponent implements OnInit { const passwordInputResult: PasswordInputResult = { newPassword, + salt, newMasterKey, newServerMasterKeyHash, newLocalMasterKeyHash, diff --git a/libs/auth/src/angular/input-password/password-input-result.ts b/libs/auth/src/angular/input-password/password-input-result.ts index 37f337291e5..11c8f0d274d 100644 --- a/libs/auth/src/angular/input-password/password-input-result.ts +++ b/libs/auth/src/angular/input-password/password-input-result.ts @@ -1,18 +1,26 @@ +import { MasterPasswordSalt } from "@bitwarden/common/key-management/master-password/types/master-password.types"; import { MasterKey } from "@bitwarden/common/types/key"; import { KdfConfig } from "@bitwarden/key-management"; export interface PasswordInputResult { currentPassword?: string; + newPassword: string; + kdfConfig?: KdfConfig; + salt?: MasterPasswordSalt; + newPasswordHint?: string; + rotateUserKey?: boolean; + + /** @deprecated This low-level cryptographic state will be removed. It will be replaced by high level calls to masterpassword service, in the consumers of this interface. */ currentMasterKey?: MasterKey; + /** @deprecated */ currentServerMasterKeyHash?: string; + /** @deprecated */ currentLocalMasterKeyHash?: string; - newPassword: string; - newPasswordHint?: string; + /** @deprecated */ newMasterKey?: MasterKey; + /** @deprecated */ newServerMasterKeyHash?: string; + /** @deprecated */ newLocalMasterKeyHash?: string; - - kdfConfig?: KdfConfig; - rotateUserKey?: boolean; } diff --git a/libs/auth/src/common/login-strategies/login.strategy.spec.ts b/libs/auth/src/common/login-strategies/login.strategy.spec.ts index 87b2dda46a5..e2f326d836d 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.spec.ts @@ -38,7 +38,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { FakeAccountService, makeEncString, mockAccountServiceWith } from "@bitwarden/common/spec"; +import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { PasswordStrengthServiceAbstraction, PasswordStrengthService, @@ -61,7 +61,7 @@ const masterPassword = "password"; const deviceId = Utils.newGuid(); const accessToken = "ACCESS_TOKEN"; const refreshToken = "REFRESH_TOKEN"; -const encryptedUserKey = makeEncString("USER_KEY"); +const encryptedUserKey = "USER_KEY"; const privateKey = "PRIVATE_KEY"; const kdf = 0; const kdfIterations = 10000; @@ -76,7 +76,7 @@ const defaultUserDecryptionOptionsServerResponse: IUserDecryptionOptionsServerRe KdfType: kdf, Iterations: kdfIterations, }, - MasterKeyEncryptedUserKey: encryptedUserKey.encryptedString, + MasterKeyEncryptedUserKey: encryptedUserKey, }, }; @@ -99,7 +99,7 @@ export function identityTokenResponseFactory( ForcePasswordReset: false, Kdf: kdf, KdfIterations: kdfIterations, - Key: encryptedUserKey.encryptedString, + Key: encryptedUserKey, PrivateKey: privateKey, ResetMasterPassword: false, access_token: accessToken, diff --git a/libs/billing/package.json b/libs/billing/package.json index 8858cfaa266..3afd267cc23 100644 --- a/libs/billing/package.json +++ b/libs/billing/package.json @@ -15,6 +15,7 @@ "scripts": { "clean": "rimraf dist", "build": "npm run clean && tsc", - "build:watch": "npm run clean && tsc -watch" + "build:watch": "npm run clean && tsc -watch", + "test": "jest --passWithNoTests" } } diff --git a/libs/billing/project.json b/libs/billing/project.json new file mode 100644 index 00000000000..829f65a9eb0 --- /dev/null +++ b/libs/billing/project.json @@ -0,0 +1,41 @@ +{ + "name": "@bitwarden/billing", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/billing/src", + "projectType": "library", + "tags": ["scope:billing", "type:lib"], + "targets": { + "build": { + "executor": "nx:run-script", + "dependsOn": [], + "options": { + "script": "build" + } + }, + "build:watch": { + "executor": "nx:run-script", + "options": { + "script": "build:watch" + } + }, + "clean": { + "executor": "nx:run-script", + "options": { + "script": "clean" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/billing/**/*.ts"] + } + }, + "test": { + "executor": "nx:run-script", + "options": { + "script": "test" + } + } + } +} diff --git a/libs/common/project.json b/libs/common/project.json new file mode 100644 index 00000000000..848bcd73c20 --- /dev/null +++ b/libs/common/project.json @@ -0,0 +1,41 @@ +{ + "name": "@bitwarden/common", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/common/src", + "projectType": "library", + "tags": ["scope:common", "type:lib"], + "targets": { + "build": { + "executor": "nx:run-script", + "dependsOn": [], + "options": { + "script": "build" + } + }, + "build:watch": { + "executor": "nx:run-script", + "options": { + "script": "build:watch" + } + }, + "clean": { + "executor": "nx:run-script", + "options": { + "script": "clean" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/common/**/*.ts"] + } + }, + "test": { + "executor": "nx:run-script", + "options": { + "script": "test" + } + } + } +} diff --git a/libs/common/src/admin-console/enums/policy-type.enum.ts b/libs/common/src/admin-console/enums/policy-type.enum.ts index 91f3a8229f8..a4a860a2f3f 100644 --- a/libs/common/src/admin-console/enums/policy-type.enum.ts +++ b/libs/common/src/admin-console/enums/policy-type.enum.ts @@ -17,4 +17,5 @@ export enum PolicyType { FreeFamiliesSponsorshipPolicy = 13, // Disables free families plan for organization RemoveUnlockWithPin = 14, // Do not allow members to unlock their account with a PIN. RestrictedItemTypes = 15, // Restricts item types that can be created within an organization + AutotypeDefaultSetting = 17, // Sets the default autotype setting for desktop app } diff --git a/libs/common/src/auth/models/request/email.request.ts b/libs/common/src/auth/models/request/email.request.ts index 12fdff149cc..10a2385a3e0 100644 --- a/libs/common/src/auth/models/request/email.request.ts +++ b/libs/common/src/auth/models/request/email.request.ts @@ -1,9 +1,27 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { + MasterPasswordAuthenticationData, + MasterPasswordUnlockData, +} from "@bitwarden/common/key-management/master-password/types/master-password.types"; + import { EmailTokenRequest } from "./email-token.request"; export class EmailRequest extends EmailTokenRequest { newMasterPasswordHash: string; token: string; key: string; + + // This will eventually be changed to be an actual constructor, once all callers are updated. + // The body of this request will be changed to carry the authentication data and unlock data. + // https://bitwarden.atlassian.net/browse/PM-23234 + static newConstructor( + authenticationData: MasterPasswordAuthenticationData, + unlockData: MasterPasswordUnlockData, + ): EmailRequest { + const request = new EmailRequest(); + request.newMasterPasswordHash = authenticationData.masterPasswordAuthenticationHash; + request.key = unlockData.masterKeyWrappedUserKey; + return request; + } } diff --git a/libs/common/src/auth/models/request/password.request.ts b/libs/common/src/auth/models/request/password.request.ts index 674754ff41a..e359540140b 100644 --- a/libs/common/src/auth/models/request/password.request.ts +++ b/libs/common/src/auth/models/request/password.request.ts @@ -1,9 +1,31 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { + MasterPasswordAuthenticationData, + MasterPasswordUnlockData, +} from "@bitwarden/common/key-management/master-password/types/master-password.types"; + import { SecretVerificationRequest } from "./secret-verification.request"; export class PasswordRequest extends SecretVerificationRequest { newMasterPasswordHash: string; masterPasswordHint: string; key: string; + + authenticationData?: MasterPasswordAuthenticationData; + unlockData?: MasterPasswordUnlockData; + + // This will eventually be changed to be an actual constructor, once all callers are updated. + // https://bitwarden.atlassian.net/browse/PM-23234 + static newConstructor( + authenticationData: MasterPasswordAuthenticationData, + unlockData: MasterPasswordUnlockData, + ): PasswordRequest { + const request = new PasswordRequest(); + request.newMasterPasswordHash = authenticationData.masterPasswordAuthenticationHash; + request.key = unlockData.masterKeyWrappedUserKey; + request.authenticationData = authenticationData; + request.unlockData = unlockData; + return request; + } } diff --git a/libs/common/src/auth/models/request/secret-verification.request.ts b/libs/common/src/auth/models/request/secret-verification.request.ts index bb5d913656e..0572b11bac5 100644 --- a/libs/common/src/auth/models/request/secret-verification.request.ts +++ b/libs/common/src/auth/models/request/secret-verification.request.ts @@ -1,7 +1,20 @@ // FIXME: Update this file to be type safe and remove this and next line + +import { MasterPasswordAuthenticationData } from "@bitwarden/common/key-management/master-password/types/master-password.types"; + // @ts-strict-ignore export class SecretVerificationRequest { masterPasswordHash: string; otp: string; authRequestAccessCode: string; + + /** + * Mutates this request to include the master password authentication data, to authenticate the request. + */ + authenticateWith( + masterPasswordAuthenticationData: MasterPasswordAuthenticationData, + ): SecretVerificationRequest { + this.masterPasswordHash = masterPasswordAuthenticationData.masterPasswordAuthenticationHash; + return this; + } } diff --git a/libs/common/src/auth/models/request/set-password.request.ts b/libs/common/src/auth/models/request/set-password.request.ts index 7206cd98623..b48c060198e 100644 --- a/libs/common/src/auth/models/request/set-password.request.ts +++ b/libs/common/src/auth/models/request/set-password.request.ts @@ -1,6 +1,11 @@ // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. + +import { + MasterPasswordAuthenticationData, + MasterPasswordUnlockData, +} from "@bitwarden/common/key-management/master-password/types/master-password.types"; // eslint-disable-next-line no-restricted-imports -import { KdfType } from "@bitwarden/key-management"; +import { KdfConfig, KdfType } from "@bitwarden/key-management"; import { KeysRequest } from "../../../models/request/keys.request"; @@ -21,19 +26,45 @@ export class SetPasswordRequest { masterPasswordHint: string, orgIdentifier: string, keys: KeysRequest | null, - kdf: KdfType, - kdfIterations: number, - kdfMemory?: number, - kdfParallelism?: number, + kdf: KdfConfig, ) { this.masterPasswordHash = masterPasswordHash; this.key = key; this.masterPasswordHint = masterPasswordHint; - this.kdf = kdf; - this.kdfIterations = kdfIterations; - this.kdfMemory = kdfMemory; - this.kdfParallelism = kdfParallelism; this.orgIdentifier = orgIdentifier; this.keys = keys; + + if (kdf.kdfType === KdfType.PBKDF2_SHA256) { + this.kdf = KdfType.PBKDF2_SHA256; + this.kdfIterations = kdf.iterations; + } else if (kdf.kdfType === KdfType.Argon2id) { + this.kdf = KdfType.Argon2id; + this.kdfIterations = kdf.iterations; + this.kdfMemory = kdf.memory; + this.kdfParallelism = kdf.parallelism; + } else { + throw new Error(`Unsupported KDF type: ${kdf}`); + } + } + + // This will eventually be changed to be an actual constructor, once all callers are updated. + // The body of this request will be changed to carry the authentication data and unlock data. + // https://bitwarden.atlassian.net/browse/PM-23234 + static newConstructor( + authenticationData: MasterPasswordAuthenticationData, + unlockData: MasterPasswordUnlockData, + masterPasswordHint: string, + orgIdentifier: string, + keys: KeysRequest | null, + ): SetPasswordRequest { + const request = new SetPasswordRequest( + authenticationData.masterPasswordAuthenticationHash, + unlockData.masterKeyWrappedUserKey, + masterPasswordHint, + orgIdentifier, + keys, + unlockData.kdf, + ); + return request; } } diff --git a/libs/common/src/auth/models/response/user-decryption-options/user-decryption-options.response.spec.ts b/libs/common/src/auth/models/response/user-decryption-options/user-decryption-options.response.spec.ts index b74648b418b..01f918f403d 100644 --- a/libs/common/src/auth/models/response/user-decryption-options/user-decryption-options.response.spec.ts +++ b/libs/common/src/auth/models/response/user-decryption-options/user-decryption-options.response.spec.ts @@ -1,14 +1,12 @@ // eslint-disable-next-line no-restricted-imports import { KdfType } from "@bitwarden/key-management"; -import { makeEncString } from "../../../../../spec"; - import { UserDecryptionOptionsResponse } from "./user-decryption-options.response"; describe("UserDecryptionOptionsResponse", () => { it("should create response when master password unlock is present", () => { const salt = "test@example.com"; - const encryptedUserKey = makeEncString("testUserKey"); + const encryptedUserKey = "testUserKey"; const response = new UserDecryptionOptionsResponse({ HasMasterPassword: true, @@ -18,7 +16,7 @@ describe("UserDecryptionOptionsResponse", () => { KdfType: KdfType.PBKDF2_SHA256, Iterations: 600_000, }, - MasterKeyEncryptedUserKey: encryptedUserKey.encryptedString, + MasterKeyEncryptedUserKey: encryptedUserKey, }, }); diff --git a/libs/common/src/auth/services/master-password/master-password-api.service.spec.ts b/libs/common/src/auth/services/master-password/master-password-api.service.spec.ts index 7a3b8762c13..2c26472174a 100644 --- a/libs/common/src/auth/services/master-password/master-password-api.service.spec.ts +++ b/libs/common/src/auth/services/master-password/master-password-api.service.spec.ts @@ -4,7 +4,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports -import { KdfType } from "@bitwarden/key-management"; +import { PBKDF2KdfConfig } from "@bitwarden/key-management"; import { PasswordRequest } from "../../models/request/password.request"; import { SetPasswordRequest } from "../../models/request/set-password.request"; @@ -42,8 +42,7 @@ describe("MasterPasswordApiService", () => { publicKey: "publicKey", encryptedPrivateKey: "encryptedPrivateKey", }, - KdfType.PBKDF2_SHA256, - 600_000, + new PBKDF2KdfConfig(600_000), ); // Act diff --git a/libs/common/src/key-management/kdf/change-kdf-api.service.abstraction.ts b/libs/common/src/key-management/kdf/change-kdf-api.service.abstraction.ts new file mode 100644 index 00000000000..04a454a0537 --- /dev/null +++ b/libs/common/src/key-management/kdf/change-kdf-api.service.abstraction.ts @@ -0,0 +1,9 @@ +import { KdfRequest } from "@bitwarden/common/models/request/kdf.request"; + +export abstract class ChangeKdfApiService { + /** + * Sends a request to update the user's KDF parameters. + * @param request The KDF request containing authentication data, unlock data, and old authentication data + */ + abstract updateUserKdfParams(request: KdfRequest): Promise; +} diff --git a/libs/common/src/key-management/kdf/change-kdf-api.service.ts b/libs/common/src/key-management/kdf/change-kdf-api.service.ts new file mode 100644 index 00000000000..0420dbbdfb2 --- /dev/null +++ b/libs/common/src/key-management/kdf/change-kdf-api.service.ts @@ -0,0 +1,15 @@ +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { KdfRequest } from "@bitwarden/common/models/request/kdf.request"; + +import { ChangeKdfApiService } from "./change-kdf-api.service.abstraction"; + +/** + * @internal + */ +export class DefaultChangeKdfApiService implements ChangeKdfApiService { + constructor(private apiService: ApiService) {} + + async updateUserKdfParams(request: KdfRequest): Promise { + return this.apiService.send("POST", "/accounts/kdf", request, true, false); + } +} diff --git a/libs/common/src/key-management/kdf/change-kdf-service.abstraction.ts b/libs/common/src/key-management/kdf/change-kdf-service.abstraction.ts new file mode 100644 index 00000000000..b0eb06f9037 --- /dev/null +++ b/libs/common/src/key-management/kdf/change-kdf-service.abstraction.ts @@ -0,0 +1,20 @@ +import { UserId } from "@bitwarden/common/types/guid"; +// eslint-disable-next-line no-restricted-imports +import { KdfConfig } from "@bitwarden/key-management"; + +export abstract class ChangeKdfService { + /** + * Updates the user's KDF parameters + * @param masterPassword The user's current master password + * @param kdf The new KDF configuration to apply + * @param userId The ID of the user whose KDF parameters are being updated + * @throws If any of the parameters is null + * @throws If the user is locked or logged out + * @throws If the kdf change request fails + */ + abstract updateUserKdfParams( + masterPassword: string, + kdf: KdfConfig, + userId: UserId, + ): Promise; +} diff --git a/libs/common/src/key-management/kdf/change-kdf-service.spec.ts b/libs/common/src/key-management/kdf/change-kdf-service.spec.ts new file mode 100644 index 00000000000..07cba1cc7eb --- /dev/null +++ b/libs/common/src/key-management/kdf/change-kdf-service.spec.ts @@ -0,0 +1,167 @@ +import { mock } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { KdfRequest } from "@bitwarden/common/models/request/kdf.request"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { UserId } from "@bitwarden/common/types/guid"; +import { UserKey } from "@bitwarden/common/types/key"; +// eslint-disable-next-line no-restricted-imports +import { KdfConfigService, KeyService, PBKDF2KdfConfig } from "@bitwarden/key-management"; + +import { MasterPasswordServiceAbstraction } from "../master-password/abstractions/master-password.service.abstraction"; +import { + MasterKeyWrappedUserKey, + MasterPasswordAuthenticationHash, + MasterPasswordSalt, + MasterPasswordUnlockData, +} from "../master-password/types/master-password.types"; + +import { ChangeKdfApiService } from "./change-kdf-api.service.abstraction"; +import { DefaultChangeKdfService } from "./change-kdf-service"; + +describe("ChangeKdfService", () => { + const changeKdfApiService = mock(); + const masterPasswordService = mock(); + const keyService = mock(); + const kdfConfigService = mock(); + + let sut: DefaultChangeKdfService = mock(); + + const mockUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; + const mockOldKdfConfig = new PBKDF2KdfConfig(100000); + const mockNewKdfConfig = new PBKDF2KdfConfig(200000); + const mockOldHash = "oldHash" as MasterPasswordAuthenticationHash; + const mockNewHash = "newHash" as MasterPasswordAuthenticationHash; + const mockUserId = "00000000-0000-0000-0000-000000000000" as UserId; + const mockSalt = "test@bitwarden.com" as MasterPasswordSalt; + const mockWrappedUserKey = "wrappedUserKey"; + + beforeEach(() => { + sut = new DefaultChangeKdfService( + masterPasswordService, + keyService, + kdfConfigService, + changeKdfApiService, + ); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe("updateUserKdfParams", () => { + it("should throw an error if masterPassword is null", async () => { + await expect( + sut.updateUserKdfParams(null as unknown as string, mockNewKdfConfig, mockUserId), + ).rejects.toThrow("masterPassword"); + }); + + it("should throw an error if masterPassword is undefined", async () => { + await expect( + sut.updateUserKdfParams(undefined as unknown as string, mockNewKdfConfig, mockUserId), + ).rejects.toThrow("masterPassword"); + }); + + it("should throw an error if kdf is null", async () => { + await expect( + sut.updateUserKdfParams("masterPassword", null as unknown as PBKDF2KdfConfig, mockUserId), + ).rejects.toThrow("kdf"); + }); + + it("should throw an error if kdf is undefined", async () => { + await expect( + sut.updateUserKdfParams( + "masterPassword", + undefined as unknown as PBKDF2KdfConfig, + mockUserId, + ), + ).rejects.toThrow("kdf"); + }); + + it("should throw an error if userId is null", async () => { + await expect( + sut.updateUserKdfParams("masterPassword", mockNewKdfConfig, null as unknown as UserId), + ).rejects.toThrow("userId"); + }); + + it("should throw an error if userId is undefined", async () => { + await expect( + sut.updateUserKdfParams("masterPassword", mockNewKdfConfig, undefined as unknown as UserId), + ).rejects.toThrow("userId"); + }); + + it("should throw an error if userKey is null", async () => { + keyService.userKey$.mockReturnValueOnce(of(null)); + masterPasswordService.saltForUser$.mockReturnValueOnce(of(mockSalt)); + kdfConfigService.getKdfConfig$.mockReturnValueOnce(of(mockOldKdfConfig)); + await expect( + sut.updateUserKdfParams("masterPassword", mockNewKdfConfig, mockUserId), + ).rejects.toThrow(); + }); + + it("should throw an error if salt is null", async () => { + keyService.userKey$.mockReturnValueOnce(of(mockUserKey)); + masterPasswordService.saltForUser$.mockReturnValueOnce(of(null)); + kdfConfigService.getKdfConfig$.mockReturnValueOnce(of(mockOldKdfConfig)); + await expect( + sut.updateUserKdfParams("masterPassword", mockNewKdfConfig, mockUserId), + ).rejects.toThrow("Failed to get salt"); + }); + + it("should throw an error if oldKdfConfig is null", async () => { + keyService.userKey$.mockReturnValueOnce(of(mockUserKey)); + masterPasswordService.saltForUser$.mockReturnValueOnce(of(mockSalt)); + kdfConfigService.getKdfConfig$.mockReturnValueOnce(of(null)); + await expect( + sut.updateUserKdfParams("masterPassword", mockNewKdfConfig, mockUserId), + ).rejects.toThrow("Failed to get oldKdfConfig"); + }); + + it("should call apiService.send with correct parameters", async () => { + keyService.userKey$.mockReturnValueOnce(of(mockUserKey)); + masterPasswordService.saltForUser$.mockReturnValueOnce(of(mockSalt)); + kdfConfigService.getKdfConfig$.mockReturnValueOnce(of(mockOldKdfConfig)); + + masterPasswordService.makeMasterPasswordAuthenticationData + .mockResolvedValueOnce({ + salt: mockSalt, + kdf: mockOldKdfConfig, + masterPasswordAuthenticationHash: mockOldHash, + }) + .mockResolvedValueOnce({ + salt: mockSalt, + kdf: mockNewKdfConfig, + masterPasswordAuthenticationHash: mockNewHash, + }); + + masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValueOnce( + new MasterPasswordUnlockData( + mockSalt, + mockNewKdfConfig, + mockWrappedUserKey as MasterKeyWrappedUserKey, + ), + ); + + await sut.updateUserKdfParams("masterPassword", mockNewKdfConfig, mockUserId); + + const expected = new KdfRequest( + { + salt: mockSalt, + kdf: mockNewKdfConfig, + masterPasswordAuthenticationHash: mockNewHash, + }, + new MasterPasswordUnlockData( + mockSalt, + mockNewKdfConfig, + mockWrappedUserKey as MasterKeyWrappedUserKey, + ), + ).authenticateWith({ + salt: mockSalt, + kdf: mockOldKdfConfig, + masterPasswordAuthenticationHash: mockOldHash, + }); + + expect(changeKdfApiService.updateUserKdfParams).toHaveBeenCalledWith(expected); + }); + }); +}); diff --git a/libs/common/src/key-management/kdf/change-kdf-service.ts b/libs/common/src/key-management/kdf/change-kdf-service.ts new file mode 100644 index 00000000000..d8bc3e21c1a --- /dev/null +++ b/libs/common/src/key-management/kdf/change-kdf-service.ts @@ -0,0 +1,59 @@ +import { assertNonNullish } from "@bitwarden/common/auth/utils"; +import { KdfRequest } from "@bitwarden/common/models/request/kdf.request"; +import { UserId } from "@bitwarden/common/types/guid"; +// eslint-disable-next-line no-restricted-imports +import { KdfConfig, KdfConfigService, KeyService } from "@bitwarden/key-management"; + +import { MasterPasswordServiceAbstraction } from "../master-password/abstractions/master-password.service.abstraction"; +import { firstValueFromOrThrow } from "../utils"; + +import { ChangeKdfApiService } from "./change-kdf-api.service.abstraction"; +import { ChangeKdfService } from "./change-kdf-service.abstraction"; + +export class DefaultChangeKdfService implements ChangeKdfService { + constructor( + private masterPasswordService: MasterPasswordServiceAbstraction, + private keyService: KeyService, + private kdfConfigService: KdfConfigService, + private changeKdfApiService: ChangeKdfApiService, + ) {} + + async updateUserKdfParams(masterPassword: string, kdf: KdfConfig, userId: UserId): Promise { + assertNonNullish(masterPassword, "masterPassword"); + assertNonNullish(kdf, "kdf"); + assertNonNullish(userId, "userId"); + + const userKey = await firstValueFromOrThrow(this.keyService.userKey$(userId), "userKey"); + const salt = await firstValueFromOrThrow( + this.masterPasswordService.saltForUser$(userId), + "salt", + ); + const oldKdfConfig = await firstValueFromOrThrow( + this.kdfConfigService.getKdfConfig$(userId), + "oldKdfConfig", + ); + + const oldAuthenticationData = + await this.masterPasswordService.makeMasterPasswordAuthenticationData( + masterPassword, + oldKdfConfig, + salt, + ); + const authenticationData = + await this.masterPasswordService.makeMasterPasswordAuthenticationData( + masterPassword, + kdf, + salt, + ); + const unlockData = await this.masterPasswordService.makeMasterPasswordUnlockData( + masterPassword, + kdf, + salt, + userKey, + ); + + const request = new KdfRequest(authenticationData, unlockData); + request.authenticateWith(oldAuthenticationData); + await this.changeKdfApiService.updateUserKdfParams(request); + } +} diff --git a/libs/common/src/key-management/master-password/abstractions/master-password.service.abstraction.ts b/libs/common/src/key-management/master-password/abstractions/master-password.service.abstraction.ts index 002cce662c0..8ef14904bce 100644 --- a/libs/common/src/key-management/master-password/abstractions/master-password.service.abstraction.ts +++ b/libs/common/src/key-management/master-password/abstractions/master-password.service.abstraction.ts @@ -27,6 +27,11 @@ export abstract class MasterPasswordServiceAbstraction { * @throws If the user ID is provided, but the user is not found. */ abstract saltForUser$: (userId: UserId) => Observable; + /** + * Converts an email to a master password salt. This is a canonical encoding of the + * email, no matter how the email is capitalized. + */ + abstract emailToSalt(email: string): MasterPasswordSalt; /** * An observable that emits the master key for the user. * @deprecated Interacting with the master-key directly is deprecated. Please use {@link makeMasterPasswordUnlockData}, {@link makeMasterPasswordAuthenticationData} or {@link unwrapUserKeyFromMasterPasswordUnlockData} instead. diff --git a/libs/common/src/key-management/master-password/models/response/master-password-unlock.response.spec.ts b/libs/common/src/key-management/master-password/models/response/master-password-unlock.response.spec.ts index 3dd1a28cc43..38701f2dc1c 100644 --- a/libs/common/src/key-management/master-password/models/response/master-password-unlock.response.spec.ts +++ b/libs/common/src/key-management/master-password/models/response/master-password-unlock.response.spec.ts @@ -1,13 +1,11 @@ // eslint-disable-next-line no-restricted-imports import { KdfType, PBKDF2KdfConfig } from "@bitwarden/key-management"; -import { makeEncString } from "../../../../../spec"; - import { MasterPasswordUnlockResponse } from "./master-password-unlock.response"; describe("MasterPasswordUnlockResponse", () => { const salt = "test@example.com"; - const encryptedUserKey = makeEncString("testUserKey"); + const encryptedUserKey = "testUserKey"; const testKdfResponse = { KdfType: KdfType.PBKDF2_SHA256, Iterations: 600_000 }; it("should throw error when salt is not provided", () => { @@ -15,7 +13,7 @@ describe("MasterPasswordUnlockResponse", () => { new MasterPasswordUnlockResponse({ Salt: undefined, Kdf: testKdfResponse, - MasterKeyEncryptedUserKey: encryptedUserKey.encryptedString, + MasterKeyEncryptedUserKey: encryptedUserKey, }); }).toThrow("MasterPasswordUnlockResponse does not contain a valid salt"); }); @@ -36,7 +34,7 @@ describe("MasterPasswordUnlockResponse", () => { const response = new MasterPasswordUnlockResponse({ Salt: salt, Kdf: testKdfResponse, - MasterKeyEncryptedUserKey: encryptedUserKey.encryptedString, + MasterKeyEncryptedUserKey: encryptedUserKey, }); expect(response.salt).toBe(salt); @@ -50,7 +48,7 @@ describe("MasterPasswordUnlockResponse", () => { const response = new MasterPasswordUnlockResponse({ Salt: salt, Kdf: testKdfResponse, - MasterKeyEncryptedUserKey: encryptedUserKey.encryptedString, + MasterKeyEncryptedUserKey: encryptedUserKey, }); const unlockData = response.toMasterPasswordUnlockData(); diff --git a/libs/common/src/key-management/master-password/models/response/master-password-unlock.response.ts b/libs/common/src/key-management/master-password/models/response/master-password-unlock.response.ts index 294d5f48a73..41b16d43f56 100644 --- a/libs/common/src/key-management/master-password/models/response/master-password-unlock.response.ts +++ b/libs/common/src/key-management/master-password/models/response/master-password-unlock.response.ts @@ -1,5 +1,4 @@ import { BaseResponse } from "../../../../models/response/base.response"; -import { EncString } from "../../../crypto/models/enc-string"; import { KdfConfigResponse } from "../../../models/response/kdf-config.response"; import { MasterKeyWrappedUserKey, @@ -29,9 +28,7 @@ export class MasterPasswordUnlockResponse extends BaseResponse { "MasterPasswordUnlockResponse does not contain a valid master key encrypted user key", ); } - this.masterKeyWrappedUserKey = new EncString( - masterKeyEncryptedUserKey, - ) as MasterKeyWrappedUserKey; + this.masterKeyWrappedUserKey = masterKeyEncryptedUserKey as MasterKeyWrappedUserKey; } toMasterPasswordUnlockData() { diff --git a/libs/common/src/key-management/master-password/services/fake-master-password.service.ts b/libs/common/src/key-management/master-password/services/fake-master-password.service.ts index 246132e2c85..81aea5e480a 100644 --- a/libs/common/src/key-management/master-password/services/fake-master-password.service.ts +++ b/libs/common/src/key-management/master-password/services/fake-master-password.service.ts @@ -33,6 +33,10 @@ export class FakeMasterPasswordService implements InternalMasterPasswordServiceA this.masterKeyHashSubject.next(initialMasterKeyHash); } + emailToSalt(email: string): MasterPasswordSalt { + return this.mock.emailToSalt(email); + } + saltForUser$(userId: UserId): Observable { return this.mock.saltForUser$(userId); } diff --git a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts index 8fd3f0b85c2..02b4e9a895a 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts @@ -10,7 +10,6 @@ import { Argon2KdfConfig, KdfConfig, KdfType, PBKDF2KdfConfig } from "@bitwarden import { FakeAccountService, - makeEncString, makeSymmetricCryptoKey, mockAccountServiceWith, } from "../../../../spec"; @@ -385,7 +384,7 @@ describe("MasterPasswordService", () => { const kdfPBKDF2: KdfConfig = new PBKDF2KdfConfig(600_000); const kdfArgon2: KdfConfig = new Argon2KdfConfig(4, 64, 3); const salt = "test@bitwarden.com" as MasterPasswordSalt; - const encryptedUserKey = makeEncString("testUserKet") as MasterKeyWrappedUserKey; + const encryptedUserKey = "testUserKet" as MasterKeyWrappedUserKey; it("returns null when value is null", () => { const deserialized = MASTER_PASSWORD_UNLOCK_KEY.deserializer( @@ -401,7 +400,7 @@ describe("MasterPasswordService", () => { kdfType: KdfType.PBKDF2_SHA256, iterations: kdfPBKDF2.iterations, }, - masterKeyWrappedUserKey: encryptedUserKey.encryptedString as string, + masterKeyWrappedUserKey: encryptedUserKey as string, }; const deserialized = MASTER_PASSWORD_UNLOCK_KEY.deserializer(data); @@ -419,7 +418,7 @@ describe("MasterPasswordService", () => { memory: kdfArgon2.memory, parallelism: kdfArgon2.parallelism, }, - masterKeyWrappedUserKey: encryptedUserKey.encryptedString as string, + masterKeyWrappedUserKey: encryptedUserKey as string, }; const deserialized = MASTER_PASSWORD_UNLOCK_KEY.deserializer(data); diff --git a/libs/common/src/key-management/master-password/services/master-password.service.ts b/libs/common/src/key-management/master-password/services/master-password.service.ts index 94465bd24fd..9f7e054d64c 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.ts @@ -132,7 +132,7 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr return EncString.fromJSON(key); } - private emailToSalt(email: string): MasterPasswordSalt { + emailToSalt(email: string): MasterPasswordSalt { return email.toLowerCase().trim() as MasterPasswordSalt; } @@ -256,6 +256,9 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr assertNonNullish(password, "password"); assertNonNullish(kdf, "kdf"); assertNonNullish(salt, "salt"); + if (password === "") { + throw new Error("Master password cannot be empty."); + } // We don't trust callers to use masterpasswordsalt correctly. They may type assert incorrectly. salt = salt.toLowerCase().trim() as MasterPasswordSalt; @@ -294,18 +297,19 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr assertNonNullish(kdf, "kdf"); assertNonNullish(salt, "salt"); assertNonNullish(userKey, "userKey"); + if (password === "") { + throw new Error("Master password cannot be empty."); + } // We don't trust callers to use masterpasswordsalt correctly. They may type assert incorrectly. salt = salt.toLowerCase().trim() as MasterPasswordSalt; await SdkLoadService.Ready; - const masterKeyWrappedUserKey = new EncString( - PureCrypto.encrypt_user_key_with_master_password( - userKey.toEncoded(), - password, - salt, - kdf.toSdkConfig(), - ), + const masterKeyWrappedUserKey = PureCrypto.encrypt_user_key_with_master_password( + userKey.toEncoded(), + password, + salt, + kdf.toSdkConfig(), ) as MasterKeyWrappedUserKey; return new MasterPasswordUnlockData(salt, kdf, masterKeyWrappedUserKey); } @@ -320,7 +324,7 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr await SdkLoadService.Ready; const userKey = new SymmetricCryptoKey( PureCrypto.decrypt_user_key_with_master_password( - masterPasswordUnlockData.masterKeyWrappedUserKey.encryptedString, + masterPasswordUnlockData.masterKeyWrappedUserKey, password, masterPasswordUnlockData.salt, masterPasswordUnlockData.kdf.toSdkConfig(), diff --git a/libs/common/src/key-management/master-password/types/master-password.types.ts b/libs/common/src/key-management/master-password/types/master-password.types.ts index f71bb0c317c..416b296623f 100644 --- a/libs/common/src/key-management/master-password/types/master-password.types.ts +++ b/libs/common/src/key-management/master-password/types/master-password.types.ts @@ -2,8 +2,7 @@ import { Jsonify, Opaque } from "type-fest"; // eslint-disable-next-line no-restricted-imports import { Argon2KdfConfig, KdfConfig, KdfType, PBKDF2KdfConfig } from "@bitwarden/key-management"; - -import { EncString } from "../../crypto/models/enc-string"; +import { EncString } from "@bitwarden/sdk-internal"; /** * The Base64-encoded master password authentication hash, that is sent to the server for authentication. @@ -13,7 +12,7 @@ export type MasterPasswordAuthenticationHash = Opaque; -export type MasterKeyWrappedUserKey = Opaque; +export type MasterKeyWrappedUserKey = Opaque; /** * The data required to unlock with the master password. @@ -29,7 +28,7 @@ export class MasterPasswordUnlockData { return { salt: this.salt, kdf: this.kdf, - masterKeyWrappedUserKey: this.masterKeyWrappedUserKey.toJSON(), + masterKeyWrappedUserKey: this.masterKeyWrappedUserKey, }; } @@ -43,7 +42,7 @@ export class MasterPasswordUnlockData { obj.kdf.kdfType === KdfType.PBKDF2_SHA256 ? PBKDF2KdfConfig.fromJSON(obj.kdf) : Argon2KdfConfig.fromJSON(obj.kdf), - EncString.fromJSON(obj.masterKeyWrappedUserKey) as MasterKeyWrappedUserKey, + obj.masterKeyWrappedUserKey as MasterKeyWrappedUserKey, ); } } diff --git a/libs/common/src/key-management/models/response/user-decryption.response.spec.ts b/libs/common/src/key-management/models/response/user-decryption.response.spec.ts index 20596a75ef2..06c19829900 100644 --- a/libs/common/src/key-management/models/response/user-decryption.response.spec.ts +++ b/libs/common/src/key-management/models/response/user-decryption.response.spec.ts @@ -1,14 +1,12 @@ // eslint-disable-next-line no-restricted-imports import { KdfType } from "@bitwarden/key-management"; -import { makeEncString } from "../../../../spec"; - import { UserDecryptionResponse } from "./user-decryption.response"; describe("UserDecryptionResponse", () => { it("should create response when masterPasswordUnlock provided", () => { const salt = "test@example.com"; - const encryptedUserKey = makeEncString("testUserKey"); + const encryptedUserKey = "testUserKey"; const kdfIterations = 600_000; const response = { @@ -18,7 +16,7 @@ describe("UserDecryptionResponse", () => { KdfType: KdfType.PBKDF2_SHA256 as number, Iterations: kdfIterations, }, - MasterKeyEncryptedUserKey: encryptedUserKey.encryptedString, + MasterKeyEncryptedUserKey: encryptedUserKey, }, }; diff --git a/libs/common/src/key-management/utils.ts b/libs/common/src/key-management/utils.ts new file mode 100644 index 00000000000..d0d8854df58 --- /dev/null +++ b/libs/common/src/key-management/utils.ts @@ -0,0 +1,12 @@ +import { firstValueFrom, Observable } from "rxjs"; + +export async function firstValueFromOrThrow( + value: Observable, + name: string, +): Promise { + const result = await firstValueFrom(value); + if (result == null) { + throw new Error(`Failed to get ${name}`); + } + return result; +} diff --git a/libs/common/src/models/request/kdf.request.ts b/libs/common/src/models/request/kdf.request.ts index 7ffdbcb4a4b..e49d8ffe6b8 100644 --- a/libs/common/src/models/request/kdf.request.ts +++ b/libs/common/src/models/request/kdf.request.ts @@ -1,14 +1,20 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { KdfType } from "@bitwarden/key-management"; +import { + MasterPasswordAuthenticationData, + MasterPasswordUnlockData, +} from "@bitwarden/common/key-management/master-password/types/master-password.types"; import { PasswordRequest } from "../../auth/models/request/password.request"; export class KdfRequest extends PasswordRequest { - kdf: KdfType; - kdfIterations: number; - kdfMemory?: number; - kdfParallelism?: number; + constructor( + authenticationData: MasterPasswordAuthenticationData, + unlockData: MasterPasswordUnlockData, + ) { + super(); + // Note, this init code should be in the super constructor, once PasswordRequest's constructor is updated. + this.newMasterPasswordHash = authenticationData.masterPasswordAuthenticationHash; + this.key = unlockData.masterKeyWrappedUserKey; + this.authenticationData = authenticationData; + this.unlockData = unlockData; + } } diff --git a/libs/common/src/platform/sync/default-sync.service.spec.ts b/libs/common/src/platform/sync/default-sync.service.spec.ts index 4fcafd885e4..3c3e1c3677f 100644 --- a/libs/common/src/platform/sync/default-sync.service.spec.ts +++ b/libs/common/src/platform/sync/default-sync.service.spec.ts @@ -15,7 +15,6 @@ import { // eslint-disable-next-line no-restricted-imports import { KeyService, PBKDF2KdfConfig } from "@bitwarden/key-management"; -import { makeEncString } from "../../../spec"; import { Matrix } from "../../../spec/matrix"; import { ApiService } from "../../abstractions/api.service"; import { InternalOrganizationServiceAbstraction } from "../../admin-console/abstractions/organization/organization.service.abstraction"; @@ -247,7 +246,7 @@ describe("DefaultSyncService", () => { describe("syncUserDecryption", () => { const salt = "test@example.com"; const kdf = new PBKDF2KdfConfig(600_000); - const encryptedUserKey = makeEncString("testUserKey"); + const encryptedUserKey = "testUserKey"; it("should set master password unlock when present in user decryption", async () => { const syncResponse = new SyncResponse({ @@ -261,7 +260,7 @@ describe("DefaultSyncService", () => { KdfType: kdf.kdfType, Iterations: kdf.iterations, }, - MasterKeyEncryptedUserKey: encryptedUserKey.encryptedString, + MasterKeyEncryptedUserKey: encryptedUserKey, }, }, }); diff --git a/libs/common/src/vault/models/domain/login.ts b/libs/common/src/vault/models/domain/login.ts index 723478b10a8..b34fb011254 100644 --- a/libs/common/src/vault/models/domain/login.ts +++ b/libs/common/src/vault/models/domain/login.ts @@ -183,7 +183,7 @@ export class Login extends Domain { ? new Date(obj.passwordRevisionDate) : undefined; login.totp = EncString.fromJSON(obj.totp); - login.autofillOnPageLoad = obj.autofillOnPageLoad ?? false; + login.autofillOnPageLoad = obj.autofillOnPageLoad; login.fido2Credentials = obj.fido2Credentials?.map((f) => Fido2Credential.fromSdkFido2Credential(f), ); diff --git a/libs/components/project.json b/libs/components/project.json new file mode 100644 index 00000000000..8201c4ea36d --- /dev/null +++ b/libs/components/project.json @@ -0,0 +1,41 @@ +{ + "name": "@bitwarden/components", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/components/src", + "projectType": "library", + "tags": ["scope:components", "type:lib"], + "targets": { + "build": { + "executor": "nx:run-script", + "dependsOn": [], + "options": { + "script": "build" + } + }, + "build:watch": { + "executor": "nx:run-script", + "options": { + "script": "watch" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/components/**/*.ts"] + } + }, + "test": { + "executor": "nx:run-script", + "options": { + "script": "test" + } + }, + "test:watch": { + "executor": "nx:run-script", + "options": { + "script": "test:watch" + } + } + } +} diff --git a/libs/components/src/async-actions/in-forms.stories.ts b/libs/components/src/async-actions/in-forms.stories.ts index 88383fe85a3..af5034c49d5 100644 --- a/libs/components/src/async-actions/in-forms.stories.ts +++ b/libs/components/src/async-actions/in-forms.stories.ts @@ -148,6 +148,7 @@ export default { required: "required", inputRequired: "Input is required.", inputEmail: "Input is not an email-address.", + loading: "Loading", }); }, }, diff --git a/libs/components/src/async-actions/standalone.stories.ts b/libs/components/src/async-actions/standalone.stories.ts index 99cde70566b..150bdb8813b 100644 --- a/libs/components/src/async-actions/standalone.stories.ts +++ b/libs/components/src/async-actions/standalone.stories.ts @@ -3,11 +3,13 @@ import { action } from "@storybook/addon-actions"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; import { delay, of } from "rxjs"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { ButtonModule } from "../button"; import { IconButtonModule } from "../icon-button"; +import { I18nMockService } from "../utils"; import { AsyncActionsModule } from "./async-actions.module"; import { BitActionDirective } from "./bit-action.directive"; @@ -103,6 +105,14 @@ export default { error: action("LogService.error"), } as Partial, }, + { + provide: I18nService, + useFactory: () => { + return new I18nMockService({ + loading: "Loading", + }); + }, + }, ], }), ], diff --git a/libs/components/src/banner/banner.component.spec.ts b/libs/components/src/banner/banner.component.spec.ts index edfbe8c8be9..1c4378a5aee 100644 --- a/libs/components/src/banner/banner.component.spec.ts +++ b/libs/components/src/banner/banner.component.spec.ts @@ -19,6 +19,7 @@ describe("BannerComponent", () => { useFactory: () => new I18nMockService({ close: "Close", + loading: "Loading", }), }, ], diff --git a/libs/components/src/banner/banner.stories.ts b/libs/components/src/banner/banner.stories.ts index a7649a28228..458f453bf1a 100644 --- a/libs/components/src/banner/banner.stories.ts +++ b/libs/components/src/banner/banner.stories.ts @@ -22,6 +22,7 @@ export default { useFactory: () => { return new I18nMockService({ close: "Close", + loading: "Loading", }); }, }, diff --git a/libs/components/src/breadcrumbs/breadcrumbs.stories.ts b/libs/components/src/breadcrumbs/breadcrumbs.stories.ts index 893f645a913..989b72adbd5 100644 --- a/libs/components/src/breadcrumbs/breadcrumbs.stories.ts +++ b/libs/components/src/breadcrumbs/breadcrumbs.stories.ts @@ -35,6 +35,7 @@ export default { useFactory: () => { return new I18nMockService({ moreBreadcrumbs: "More breadcrumbs", + loading: "Loading", }); }, }, diff --git a/libs/components/src/button/button.component.html b/libs/components/src/button/button.component.html index a07ab9fb99b..26e0c3b4d3d 100644 --- a/libs/components/src/button/button.component.html +++ b/libs/components/src/button/button.component.html @@ -2,10 +2,9 @@ - - - + @if (showLoadingStyle()) { + + + + } diff --git a/libs/components/src/button/button.component.ts b/libs/components/src/button/button.component.ts index 1dce792c963..47612685c16 100644 --- a/libs/components/src/button/button.component.ts +++ b/libs/components/src/button/button.component.ts @@ -14,6 +14,7 @@ import { debounce, interval } from "rxjs"; import { AriaDisableDirective } from "../a11y"; import { ButtonLikeAbstraction, ButtonType, ButtonSize } from "../shared/button-like.abstraction"; +import { SpinnerComponent } from "../spinner"; import { ariaDisableElement } from "../utils"; const focusRing = [ @@ -60,7 +61,7 @@ const buttonStyles: Record = { selector: "button[bitButton], a[bitButton]", templateUrl: "button.component.html", providers: [{ provide: ButtonLikeAbstraction, useExisting: ButtonComponent }], - imports: [NgClass], + imports: [NgClass, SpinnerComponent], hostDirectives: [AriaDisableDirective], }) export class ButtonComponent implements ButtonLikeAbstraction { diff --git a/libs/components/src/button/button.stories.ts b/libs/components/src/button/button.stories.ts index 180f5cb4aa9..7319b47bce5 100644 --- a/libs/components/src/button/button.stories.ts +++ b/libs/components/src/button/button.stories.ts @@ -1,12 +1,28 @@ -import { Meta, StoryObj } from "@storybook/angular"; +import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { formatArgsForCodeSnippet } from "../../../../.storybook/format-args-for-code-snippet"; +import { I18nMockService } from "../utils/i18n-mock.service"; import { ButtonComponent } from "./button.component"; export default { title: "Component Library/Button", component: ButtonComponent, + decorators: [ + moduleMetadata({ + providers: [ + { + provide: I18nService, + useFactory: () => + new I18nMockService({ + loading: "Loading", + }), + }, + ], + }), + ], args: { disabled: false, loading: false, diff --git a/libs/components/src/copy-click/copy-click.stories.ts b/libs/components/src/copy-click/copy-click.stories.ts index 50b9549dedd..208d42b44c4 100644 --- a/libs/components/src/copy-click/copy-click.stories.ts +++ b/libs/components/src/copy-click/copy-click.stories.ts @@ -38,6 +38,7 @@ export default { success: "Success", close: "Close", info: "Info", + loading: "Loading", }); }, }, diff --git a/libs/components/src/dialog/dialog.service.stories.ts b/libs/components/src/dialog/dialog.service.stories.ts index 5a002f7ee7e..caa7a86a2a8 100644 --- a/libs/components/src/dialog/dialog.service.stories.ts +++ b/libs/components/src/dialog/dialog.service.stories.ts @@ -155,6 +155,7 @@ export default { toggleSideNavigation: "Toggle side navigation", yes: "Yes", no: "No", + loading: "Loading", }); }, }, diff --git a/libs/components/src/dialog/dialog/dialog.component.html b/libs/components/src/dialog/dialog/dialog.component.html index 159944dcc1f..1b701a50584 100644 --- a/libs/components/src/dialog/dialog/dialog.component.html +++ b/libs/components/src/dialog/dialog/dialog.component.html @@ -52,7 +52,7 @@ > @if (loading()) {
- +
}
{ + return new I18nMockService({ + loading: "Loading", + }); + }, + }, + ], }), ], parameters: { diff --git a/libs/components/src/disclosure/disclosure.stories.ts b/libs/components/src/disclosure/disclosure.stories.ts index 2e45964ccaa..3ed6903060c 100644 --- a/libs/components/src/disclosure/disclosure.stories.ts +++ b/libs/components/src/disclosure/disclosure.stories.ts @@ -1,6 +1,9 @@ import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + import { IconButtonModule } from "../icon-button"; +import { I18nMockService } from "../utils"; import { DisclosureTriggerForDirective } from "./disclosure-trigger-for.directive"; import { DisclosureComponent } from "./disclosure.component"; @@ -11,6 +14,16 @@ export default { decorators: [ moduleMetadata({ imports: [DisclosureTriggerForDirective, DisclosureComponent, IconButtonModule], + providers: [ + { + provide: I18nService, + useFactory: () => { + return new I18nMockService({ + loading: "Loading", + }); + }, + }, + ], }), ], parameters: { diff --git a/libs/components/src/drawer/drawer.stories.ts b/libs/components/src/drawer/drawer.stories.ts index 1d7aa79137d..727d16b5481 100644 --- a/libs/components/src/drawer/drawer.stories.ts +++ b/libs/components/src/drawer/drawer.stories.ts @@ -41,6 +41,7 @@ export default { return new I18nMockService({ ...mockLayoutI18n, close: "Close", + loading: "Loading", }); }, }, diff --git a/libs/components/src/form-field/form-field.stories.ts b/libs/components/src/form-field/form-field.stories.ts index 7aeb2f63040..01aa56f896c 100644 --- a/libs/components/src/form-field/form-field.stories.ts +++ b/libs/components/src/form-field/form-field.stories.ts @@ -63,6 +63,7 @@ export default { inputRequired: "Input is required.", inputEmail: "Input is not an email-address.", toggleVisibility: "Toggle visibility", + loading: "Loading", }); }, }, diff --git a/libs/components/src/form-field/password-input-toggle.spec.ts b/libs/components/src/form-field/password-input-toggle.spec.ts index 72f2481d789..2b82fad9876 100644 --- a/libs/components/src/form-field/password-input-toggle.spec.ts +++ b/libs/components/src/form-field/password-input-toggle.spec.ts @@ -48,6 +48,7 @@ describe("PasswordInputToggle", () => { provide: I18nService, useValue: new I18nMockService({ toggleVisibility: "Toggle visibility", + loading: "Loading", }), }, ], diff --git a/libs/components/src/form-field/password-input-toggle.stories.ts b/libs/components/src/form-field/password-input-toggle.stories.ts index 3d50a4eb75a..1fcc899ddba 100644 --- a/libs/components/src/form-field/password-input-toggle.stories.ts +++ b/libs/components/src/form-field/password-input-toggle.stories.ts @@ -19,7 +19,10 @@ export default { providers: [ { provide: I18nService, - useValue: new I18nMockService({ toggleVisibility: "Toggle visibility" }), + useValue: new I18nMockService({ + toggleVisibility: "Toggle visibility", + loading: "Loading", + }), }, ], }), diff --git a/libs/components/src/icon-button/icon-button.component.html b/libs/components/src/icon-button/icon-button.component.html index e775a868871..b5826ce9928 100644 --- a/libs/components/src/icon-button/icon-button.component.html +++ b/libs/components/src/icon-button/icon-button.component.html @@ -6,10 +6,6 @@ class="tw-absolute tw-inset-0 tw-flex tw-items-center tw-justify-center" [ngClass]="{ 'tw-invisible': !showLoadingStyle() }" > - + diff --git a/libs/components/src/icon-button/icon-button.component.ts b/libs/components/src/icon-button/icon-button.component.ts index 4130cf27170..6bb6ccf10bd 100644 --- a/libs/components/src/icon-button/icon-button.component.ts +++ b/libs/components/src/icon-button/icon-button.component.ts @@ -16,6 +16,7 @@ import { AriaDisableDirective } from "../a11y"; import { setA11yTitleAndAriaLabel } from "../a11y/set-a11y-title-and-aria-label"; import { ButtonLikeAbstraction } from "../shared/button-like.abstraction"; import { FocusableElement } from "../shared/focusable-element"; +import { SpinnerComponent } from "../spinner"; import { ariaDisableElement } from "../utils"; export type IconButtonType = "primary" | "danger" | "contrast" | "main" | "muted" | "nav-contrast"; @@ -87,7 +88,7 @@ const sizes: Record = { { provide: ButtonLikeAbstraction, useExisting: BitIconButtonComponent }, { provide: FocusableElement, useExisting: BitIconButtonComponent }, ], - imports: [NgClass], + imports: [NgClass, SpinnerComponent], host: { /** * When the `bitIconButton` input is dynamic from a consumer, Angular doesn't put the diff --git a/libs/components/src/icon-button/icon-button.stories.ts b/libs/components/src/icon-button/icon-button.stories.ts index d716b9697f2..c93beb167bd 100644 --- a/libs/components/src/icon-button/icon-button.stories.ts +++ b/libs/components/src/icon-button/icon-button.stories.ts @@ -1,12 +1,29 @@ -import { Meta, StoryObj } from "@storybook/angular"; +import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { formatArgsForCodeSnippet } from "../../../../.storybook/format-args-for-code-snippet"; +import { I18nMockService } from "../utils"; import { BitIconButtonComponent } from "./icon-button.component"; export default { title: "Component Library/Icon Button", component: BitIconButtonComponent, + decorators: [ + moduleMetadata({ + providers: [ + { + provide: I18nService, + useFactory: () => { + return new I18nMockService({ + loading: "Loading", + }); + }, + }, + ], + }), + ], args: { bitIconButton: "bwi-plus", label: "Your button label here", diff --git a/libs/components/src/item/item.stories.ts b/libs/components/src/item/item.stories.ts index bc2ced7fd11..36bbebee940 100644 --- a/libs/components/src/item/item.stories.ts +++ b/libs/components/src/item/item.stories.ts @@ -44,6 +44,7 @@ export default { skipToContent: "Skip to content", submenu: "submenu", toggleCollapse: "toggle collapse", + loading: "Loading", }); }, }, diff --git a/libs/components/src/layout/layout.component.html b/libs/components/src/layout/layout.component.html index a4ee3f0d8a2..35c6f04911c 100644 --- a/libs/components/src/layout/layout.component.html +++ b/libs/components/src/layout/layout.component.html @@ -23,31 +23,27 @@ [id]="mainContentId" tabindex="-1" bitScrollLayoutHost - class="tw-overflow-auto tw-max-h-screen tw-min-w-0 tw-flex-1 tw-bg-background tw-p-8 tw-pt-6 md:tw-ms-0 tw-ms-16" + class="tw-overflow-auto tw-max-h-screen tw-min-w-0 tw-flex-1 tw-bg-background tw-p-8 tw-pt-6" > - - - @if ( - { - open: sideNavService.open$ | async, - }; - as data - ) { -
- @if (data.open) { -
- } -
- } + + @if ( + { + open: sideNavService.open$ | async, + }; + as data + ) { +
+ @if (data.open) { +
+ } +
+ }
diff --git a/libs/components/src/layout/mocks.ts b/libs/components/src/layout/mocks.ts index 50c2bd9afb2..8b001eb8fd1 100644 --- a/libs/components/src/layout/mocks.ts +++ b/libs/components/src/layout/mocks.ts @@ -4,4 +4,5 @@ export const mockLayoutI18n = { skipToContent: "Skip to content", submenu: "submenu", toggleCollapse: "toggle collapse", + loading: "Loading", }; diff --git a/libs/components/src/menu/menu.stories.ts b/libs/components/src/menu/menu.stories.ts index b29613061b8..7a4f06232ef 100644 --- a/libs/components/src/menu/menu.stories.ts +++ b/libs/components/src/menu/menu.stories.ts @@ -1,7 +1,10 @@ import { OverlayModule } from "@angular/cdk/overlay"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + import { ButtonModule } from "../button/button.module"; +import { I18nMockService } from "../utils"; import { MenuTriggerForDirective } from "./menu-trigger-for.directive"; import { MenuModule } from "./menu.module"; @@ -12,6 +15,12 @@ export default { decorators: [ moduleMetadata({ imports: [MenuModule, OverlayModule, ButtonModule], + providers: [ + { + provide: I18nService, + useValue: new I18nMockService({ loading: "Loading" }), + }, + ], }), ], parameters: { diff --git a/libs/components/src/multi-select/multi-select.component.html b/libs/components/src/multi-select/multi-select.component.html index 8aa45b7dae8..7f6a22779ef 100644 --- a/libs/components/src/multi-select/multi-select.component.html +++ b/libs/components/src/multi-select/multi-select.component.html @@ -20,7 +20,7 @@ appendTo="body" > - +